diff --git a/components/ble/CMakeLists.txt b/components/ble/CMakeLists.txt new file mode 100644 index 00000000..a48dc30f --- /dev/null +++ b/components/ble/CMakeLists.txt @@ -0,0 +1,174 @@ +include(${CMAKE_CURRENT_SOURCE_DIR}/ble_flags.cmake) +################# Add global include ################# +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/port/include") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/include") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/include/zephyr") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/include/misc") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/include/toolchain") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/include/tinycrypt") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/hci_onchip") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/bl_hci_wrapper") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/include/bluetooth") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/include/drivers/bluetooth") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/cli_cmds") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/blecontroller/ble_inc") + +if(CONFIG_BT_OAD_SERVER AND CONFIG_BT_OAD_CLIENT) +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/oad") +endif() +####################################################### + +################# Add private include ################# +# list(APPEND ADD_PRIVATE_INCLUDE +# ) +####################################################### + +############## Add current dir source files ########### +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/port/bl_port.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/atomic_c.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/buf.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/log.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/poll.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/rpa.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/work_q.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/dec.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/utils.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/dummy.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/aes_decrypt.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/aes_encrypt.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/cbc_mode.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ccm_mode.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/cmac_mode.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ctr_mode.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ctr_prng.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ecc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ecc_dh.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ecc_dsa.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/ecc_platform_specific.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/hmac.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/hmac_prng.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/sha256.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/common/tinycrypt/source/utils.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/hci_onchip/hci_driver.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/att.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/conn.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/crypto.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/gatt.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/hci_core.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/hci_ecc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/l2cap.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/uuid.c") + +if(NOT CONFIG_DISABLE_BT_SMP) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/smp.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/keys.c") +endif() + +if(CONFIG_BT_DEBUG_MONITOR) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/monitor.c") +endif() + +if(CONFIG_BT_OAD_CLIENT) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/oad/oad_client.c") +endif() + +if(NOT CONFIG_DBG_RUN_ON_FPGA) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/settings.c") +endif() + +if(CONFIG_BT_OAD_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/oad/oad_main.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/oad/oad_service.c") +endif() + +if(CONFIG_BT_BAS_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/bas.c") +endif() + +if(CONFIG_BT_SCPS_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/scps.c") +endif() + +if(CONFIG_BT_DIS_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/bas.c") +endif() + +if(CONFIG_BT_BAS_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/dis.c") +endif() + +if(CONFIG_BLE_TP_SERVER) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/services/ble_tp_svc.c") +endif() + +if(CONFIG_BLE_MULTI_ADV) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/multi_adv.c") +endif() + +if(CONFIG_BT_BREDR) +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/a2dp.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/avdtp.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/keys_br.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/l2cap_br.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/host/sdp.c") + +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/alloc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/bitalloc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/bitalloc-sbc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/bitstream-decode.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/decoder-oina.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/decoder-private.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/decoder-sbc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/dequant.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/framing.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/framing-sbc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/oi_codec_version.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/synthesis-8-generated.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/synthesis-dct8.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/synthesis-sbc.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_analysis.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_dct.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_dct_coeffs.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec/sbc_enc_bit_alloc_mono.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_enc_coeffs.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_encoder.c") +list(APPEND ADD_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc/sbc_packing.c") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/dec") +list(APPEND ADD_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/ble_stack/sbc/enc") +endif() + + + + +# aux_source_directory(src ADD_SRCS) +####################################################### + +########### Add required/dependent components ######### +list(APPEND ADD_REQUIREMENTS freertos ${CHIP}_driver) +####################################################### + +############ Add static libs ########################## +list(APPEND ADD_STATIC_LIB "${CMAKE_CURRENT_SOURCE_DIR}/bl702_rf/lib/libbl702_rf.a") +####################################################### + +############ Add dynamic libs ######################### +# list(APPEND ADD_DYNAMIC_LIB "libxxx.so" +# ) +####################################################### + +############ Add global compile option ################ +#add components denpend on this component +list(APPEND ADD_DEFINITIONS ${CFLAGS}) +####################################################### + +############ Add private compile option ################ +#add compile option for this component that won't affect other modules +# list(APPEND ADD_DEFINITIONS_PRIVATE -Dxxx) +####################################################### + +generate_library() diff --git a/components/ble/bl702_rf/lib/libbl702_rf.a b/components/ble/bl702_rf/lib/libbl702_rf.a new file mode 100644 index 00000000..05934bea Binary files /dev/null and b/components/ble/bl702_rf/lib/libbl702_rf.a differ diff --git a/components/ble/ble_flags.cmake b/components/ble/ble_flags.cmake new file mode 100644 index 00000000..4be7e4d4 --- /dev/null +++ b/components/ble/ble_flags.cmake @@ -0,0 +1,396 @@ +list(APPEND CFLAGS -DCFG_FREERTOS) +list(APPEND CFLAGS -DARCH_RISCV) + +string(TOUPPER ${CHIP} CHIP_NAME) +list(APPEND CFLAGS -D${CHIP_NAME}) +if(${CHIP} STREQUAL "bl602") +list(APPEND CFLAGS -DCONFIG_SET_TX_PWR) +endif() + +if(CONFIG_DBG_RUN_ON_FPGA) +list(APPEND CFLAGS -DCONFIG_DBG_RUN_ON_FPGA) +endif() + +if(CONFIG_BUILD_ROM_CODE) +list(APPEND CFLAGS -DBUILD_ROM_CODE) +endif() + +list(APPEND CFLAGS -DCFG_BLE_ENABLE) +list(APPEND CFLAGS -DBFLB_BLE) +list(APPEND CFLAGS -DCFG_BLE) +list(APPEND CFLAGS -DCFG_SLEEP) +list(APPEND CFLAGS -DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + +if(NOT CONFIG_BT_BREDR) +set(CONFIG_BT_BREDR 0) +endif() + +if(CONFIG_BT_BREDR) +list(APPEND CFLAGS -DCONFIG_BT_BREDR) +list(APPEND CFLAGS -DSBC_DEC_INCLUDED) +list(APPEND CFLAGS -DSBC_ENC_INCLUDED) +endif() + +if(CONFIG_BT_TL) +list(APPEND CFLAGS -DCONFIG_BT_TL) +set(CONFIG_BLE_HOST_DISABLE 1) +endif() + +if(NOT CONFIG_BT_CONN) +set(CONFIG_BT_CONN 2) +endif() + +list(APPEND CFLAGS -DBL_MCU_SDK) + +list(APPEND CFLAGS -DCFG_CON=${CONFIG_BT_CONN}) + +if(NOT CONFIG_BLE_TX_BUFF_DATA) +set(CONFIG_BLE_TX_BUFF_DATA ${CONFIG_BT_CONN}) +endif() + +list(APPEND CFLAGS -DCFG_BLE_TX_BUFF_DATA=${CONFIG_BLE_TX_BUFF_DATA}) + +if(NOT CONFIG_BT_ALLROLES) +set(CONFIG_BT_ALLROLES 1) +endif() + +if(NOT CONFIG_BT_CENTRAL) +set(CONFIG_BT_CENTRAL 1) +endif() + +if(NOT CONFIG_BT_OBSERVER) +set(CONFIG_BT_OBSERVER 1) +endif() + +if(NOT CONFIG_BT_PERIPHERAL) +set(CONFIG_BT_PERIPHERAL 1) +endif() + +if(NOT CONFIG_BT_BROADCASTER) +set(CONFIG_BT_BROADCASTER 1) +endif() + +if(NOT CONFIG_BT_SETTINGS) +set(CONFIG_BT_SETTINGS 0) +endif() + +if(NOT CONFIG_BLE_TP_SERVER) +set(CONFIG_BLE_TP_SERVER 0) +endif() + +if(NOT CONFIG_BLE_MULTI_ADV) +set(CONFIG_BLE_MULTI_ADV 0) +endif() + +if(NOT CONFIG_BLE_STACK_DBG_PRINT) +set(CONFIG_BLE_STACK_DBG_PRINT 1) +endif() + +if(NOT CONFIG_BT_STACK_PTS) +set(CONFIG_BT_STACK_PTS 0) +endif() + +if(NOT CONFIG_BLE_TP_TEST) +set(CONFIG_BLE_TP_TEST 0) +endif() + +if(NOT CONFIG_BT_GEN_RANDOM_BY_SW) +set(CONFIG_BT_GEN_RANDOM_BY_SW 0) +endif() + +if(NOT CONFIG_DISABLE_BT_SMP) +set(CONFIG_DISABLE_BT_SMP 0) +endif() + +if(NOT CONFIG_DISABLE_BT_HOST_PRIVACY) +set(CONFIG_DISABLE_BT_HOST_PRIVACY 1) +endif() + +if(NOT CFG_BLE_PDS) +set(CFG_BLE_PDS 0) +endif() + +if(NOT CONFIG_BT_MESH) +set(CONFIG_BT_MESH 0) +endif() + +if(NOT CONFIG_BT_MESH_MODEL) +set(CONFIG_BT_MESH_MODEL 0) +endif() + +if(CONFIG_BT_MESH) + + if(NOT CONFIG_BT_MESH_PB_ADV) + set(CONFIG_BT_MESH_PB_ADV 1) + endif() + + if(NOT CONFIG_BT_MESH_PB_GATT) + set(CONFIG_BT_MESH_PB_GATT 1) + endif() + + if(NOT CONFIG_BT_MESH_FRIEND) + set(CONFIG_BT_MESH_FRIEND 1) + endif() + + if(NOT CONFIG_BT_MESH_LOW_POWER) + set(CONFIG_BT_MESH_LOW_POWER 1) + endif() + + if(NOT CONFIG_BT_MESH_PROXY) + set(CONFIG_BT_MESH_PROXY 1) + endif() + + if(NOT CONFIG_BT_MESH_GATT_PROXY) + set(CONFIG_BT_MESH_GATT_PROXY 1) + endif() + + if(CONFIG_BT_MESH_MODEL) + if(NOT CONFIG_BT_MESH_MODEL_GEN_SRV) + set(CONFIG_BT_MESH_MODEL_GEN_SRV 1) + endif() + if(NOT CONFIG_BT_MESH_MODEL_GEN_CLI) + set(CONFIG_BT_MESH_MODEL_GEN_CLI 1) + endif() + if(NOT CONFIG_BT_MESH_MODEL_LIGHT_SRV) + set(CONFIG_BT_MESH_MODEL_LIGHT_SRV 1) + endif() + if(NOT CONFIG_BT_MESH_MODEL_LIGHT_CLI) + set(CONFIG_BT_MESH_MODEL_LIGHT_CLI 1) + endif() + else() + if(NOT CONFIG_BT_MESH_MODEL_GEN_SRV) + set(CONFIG_BT_MESH_MODEL_GEN_SRV 1) + endif() + endif() + + if(NOT CONFIG_BT_MESH_CDB) + set(CONFIG_BT_MESH_CDB 0) + endif() + + if(NOT CONFIG_BT_MESH_PROVISIONER) + set(CONFIG_BT_MESH_PROVISIONER 0) + endif() + + if(NOT CONFIG_BT_MESH_SYNC) + set(CONFIG_BT_MESH_SYNC 0) + endif() + + if(NOT CONFIG_BT_MESH_NODE_SEND_CFGCLI_MSG) + set(CONFIG_BT_MESH_NODE_SEND_CFGCLI_MSG 0) + endif() +endif() + + +########################################## +############## BLE STACK ################# +########################################## + +if(CONFIG_BT_ALLROLES) +list(APPEND CFLAGS -DCONFIG_BT_ALLROLES) +list(APPEND CFLAGS -DCONFIG_BT_CENTRAL) +list(APPEND CFLAGS -DCONFIG_BT_OBSERVER) +list(APPEND CFLAGS -DCONFIG_BT_PERIPHERAL) +list(APPEND CFLAGS -DCONFIG_BT_BROADCASTER) + +else() + if(CONFIG_BT_CENTRAL) + list(APPEND CFLAGS -DCONFIG_BT_CENTRAL) + endif() + if(CONFIG_BT_OBSERVER) + list(APPEND CFLAGS -DCONFIG_BT_OBSERVER) + endif() + if(CONFIG_BT_PERIPHERAL) + list(APPEND CFLAGS -DCONFIG_BT_PERIPHERAL) + endif() + if(CONFIG_BT_BROADCASTER) + list(APPEND CFLAGS -DCONFIG_BT_BROADCASTER) + endif() +endif() + +if(NOT CONFIG_DBG_RUN_ON_FPGA) + if(CONFIG_BT_SETTINGS) + list(APPEND CFLAGS -DCONFIG_BT_SETTINGS) + endif() +endif() + +if(CONFIG_BLE_MFG) +list(APPEND CFLAGS -DCONFIG_BLE_MFG) + +if(CONFIG_BLE_MFG_HCI_CMD) +list(APPEND CFLAGS -DCONFIG_BLE_MFG_HCI_CMD) +endif() + +endif() + +if(CONFIG_BT_GEN_RANDOM_BY_SW) +list(APPEND CFLAGS -DCONFIG_BT_GEN_RANDOM_BY_SW) +endif() + +if(CFG_BLE_PDS) +list(APPEND CFLAGS -DCFG_BLE_PDS) +endif() + +list(APPEND CFLAGS -DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +list(APPEND CFLAGS -DCONFIG_BT_GATT_CLIENT) +list(APPEND CFLAGS -DCONFIG_BT_CONN) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DIS_PNP) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DIS_SERIAL_NUMBER) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DIS_FW_REV) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DIS_HW_REV) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DIS_SW_REV) +list(APPEND CFLAGS -DCONFIG_BT_ECC) +list(APPEND CFLAGS -DCONFIG_BT_GATT_DYNAMIC_DB) +list(APPEND CFLAGS -DCONFIG_BT_GATT_SERVICE_CHANGED) +list(APPEND CFLAGS -DCONFIG_BT_KEYS_OVERWRITE_OLDEST) +list(APPEND CFLAGS -DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING) +list(APPEND CFLAGS -DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS) +list(APPEND CFLAGS -DCONFIG_BT_BONDABLE) +list(APPEND CFLAGS -DCONFIG_BT_HCI_VS_EVT_USER) +list(APPEND CFLAGS -DCONFIG_BT_ASSERT) + + + +if(NOT CONFIG_DISABLE_BT_SMP) +list(APPEND CFLAGS -DCONFIG_BT_SMP) +list(APPEND CFLAGS -DCONFIG_BT_SIGNING) +endif() + +if(NOT CONFIG_DBG_RUN_ON_FPGA) +list(APPEND CFLAGS -DCONFIG_BT_SETTINGS_CCC_LAZY_LOADING) +list(APPEND CFLAGS -DCONFIG_BT_SETTINGS_USE_PRINTK) +endif() + +if(CONFIG_BLE_STACK_DBG_PRINT) +list(APPEND CFLAGS -DCFG_BLE_STACK_DBG_PRINT) +endif() + +if(CONFIG_BT_DEBUG_MONITOR) +list(APPEND CFLAGS -DCONFIG_BT_DEBUG_MONITOR) +endif() + +if(CONFIG_BLE_AT_CMD) +list(APPEND CFLAGS -DCONFIG_BLE_AT_CMD) +endif() + +if(CONFIG_BT_OAD_SERVER) +list(APPEND CFLAGS -DCONFIG_BT_OAD_SERVER) +endif() + +if(CONFIG_BT_OAD_CLIENT) +list(APPEND CFLAGS -DCONFIG_BT_OAD_CLIENT) +endif() + +if(CONFIG_BT_REMOTE_CONTROL) +list(APPEND CFLAGS -DCONFIG_BT_REMOTE_CONTROL) +endif() + +if(NOT CONFIG_DISABLE_BT_HOST_PRIVACY) +list(APPEND CFLAGS -DCONFIG_BT_PRIVACY) +endif() + +if(CONFIG_BLE_TP_SERVER) +list(APPEND CFLAGS -DCONFIG_BLE_TP_SERVER) +if(CONFIG_BLE_TP_TEST) +list(APPEND CFLAGS -DCONFIG_BLE_TP_TEST) +endif() + +endif() + +if(CONFIG_BLE_MULTI_ADV) +list(APPEND CFLAGS -DCONFIG_BLE_MULTI_ADV) +endif() + +if(CONFIG_BT_STACK_PTS) +list(APPEND CFLAGS -DCONFIG_BT_STACK_PTS) +endif() + +if(PTS_TEST_CASE_INSUFFICIENT_KEY) +list(APPEND CFLAGS -DPTS_TEST_CASE_INSUFFICIENT_KEY) +endif() + +if(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +list(APPEND CFLAGS -DPTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +endif() + +########################################## +############## BLE MESH ################## +########################################## + +if(CONFIG_BT_MESH) +list(APPEND CFLAGS -DCONFIG_BT_MESH) +list(APPEND CFLAGS -DCONFIG_BT_MESH_PROV) +list(APPEND CFLAGS -DCONFIG_BT_MESH_RELAY) + +if(CONFIG_BT_MESH_PB_ADV) +list(APPEND CFLAGS -DCONFIG_BT_MESH_PB_ADV) +endif() + +if(CONFIG_BT_MESH_PB_GATT) +list(APPEND CFLAGS -DCONFIG_BT_MESH_PB_GATT) +endif() + +if(CONFIG_BT_MESH_FRIEND) +list(APPEND CFLAGS -DCONFIG_BT_MESH_FRIEND) +endif() + +if(CONFIG_BT_MESH_LOW_POWER) +list(APPEND CFLAGS -DCONFIG_BT_MESH_LOW_POWER) +endif() + +if(CONFIG_BT_MESH_PROXY) +list(APPEND CFLAGS -DCONFIG_BT_MESH_PROXY) +endif() + +if(CONFIG_BT_MESH_GATT_PROXY) +list(APPEND CFLAGS -DCONFIG_BT_MESH_GATT_PROXY) +endif() + +if(CONFIG_BLE_STACK_DBG_PRINT) +list(APPEND CFLAGS -DCONFIG_BLE_STACK_DBG_PRINT) +endif() + +if(CONFIG_BT_MESH_SYNC) +list(APPEND CFLAGS -DCONFIG_BT_MESH_SYNC) +endif() + +if(CONFIG_BT_MESH_NODE_SEND_CFGCLI_MSG) +list(APPEND CFLAGS -DCONFIG_BT_MESH_NODE_SEND_CFGCLI_MSG) +endif() + +if(CONFIG_BT_MESH_CDB) +list(APPEND CFLAGS -DCONFIG_BT_MESH_CDB) +list(APPEND CFLAGS -DCONFIG_BT_MESH_CDB_NODE_COUNT=64) +list(APPEND CFLAGS -DCONFIG_BT_MESH_CDB_SUBNET_COUNT=2) +list(APPEND CFLAGS -DCONFIG_BT_MESH_CDB_APP_KEY_COUNT=2) +endif() + +if(CONFIG_BT_MESH_PROVISIONER) +list(APPEND CFLAGS -DCONFIG_BT_MESH_PROVISIONER) +list(APPEND CFLAGS -DCONFIG_BT_MESH_CFG_CLI) +list(APPEND CFLAGS -DCONFIG_BT_MESH_HEALTH_CLI) +endif() + +if(CONFIG_BT_MESH_MODEL) +list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL) + + if(CONFIG_BT_MESH_MODEL_GEN_SRV) + list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_GEN_SRV) + endif() + if(CONFIG_BT_MESH_MODEL_GEN_CLI) + list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_GEN_CLI) + endif() + if(CONFIG_BT_MESH_MODEL_LIGHT_SRV) + list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_LIGHT_SRV) + endif() + if(CONFIG_BT_MESH_MODEL_LIGHT_CLI) + list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_LIGHT_CLI) + endif() +else() + if(CONFIG_BT_MESH_MODEL_LIGHT_CLI) + list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_LIGHT_CLI) + endif() +endif() +if(CONFIG_BT_MESH_MODEL_GEN_SRV) +list(APPEND CFLAGS -DCONFIG_BT_MESH_MODEL_GEN_SRV) +endif() +endif() \ No newline at end of file diff --git a/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c b/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c new file mode 100644 index 00000000..0095e5f9 --- /dev/null +++ b/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c @@ -0,0 +1,371 @@ +/***************************************************************************************** +* +* @file bl_hci_wrapper.c +* +* @brief Bouffalo Lab hci wrapper functions +* +* Copyright (C) Bouffalo Lab 2018 +* +* History: 2018-08 crealted by llgong @ Shanghai +* +*****************************************************************************************/ + +#include +#include +#include "hci_host.h" +#include "bl_hci_wrapper.h" +#include "hci_driver.h" +#include "../common/include/errno.h" +#include "byteorder.h" +#include "hci_onchip.h" + + +extern int hci_host_recv_pkt_handler(uint8_t *data, uint16_t len); + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +struct rx_msg_struct msg_array[9]; +struct k_queue msg_queue; +static void bl_onchiphci_rx_packet_handler(uint8_t pkt_type, uint16_t src_id, uint8_t *param, + uint8_t param_len, void *rx_buf); +#else +static void bl_onchiphci_rx_packet_handler(uint8_t pkt_type, uint16_t src_id, uint8_t *param, + uint8_t param_len); +#endif + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +struct rx_msg_struct* bl_find_valid_queued_entry(void) +{ + struct rx_msg_struct empty_msg; + memset(&empty_msg, 0, sizeof(struct rx_msg_struct)); + + for(int i = 0; i < sizeof(msg_array)/(sizeof(struct rx_msg_struct)); i++){ + if(!memcmp(&msg_array[i], &empty_msg, sizeof(struct rx_msg_struct))) + return (msg_array + i); + } + + return NULL; +} + +void bl_handle_queued_msg(void) +{ + //give a default buf type BT_BUF_ACL_IN, will be set buf type in bl_onchiphci_rx_packet_handler again. + struct net_buf *buf; + struct rx_msg_struct *msg; + + if(k_queue_is_empty(&msg_queue)) + return; + + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); + if(!buf) + return; + + msg = k_fifo_get(&msg_queue, K_NO_WAIT); + BT_ASSERT(buf); + + bl_onchiphci_rx_packet_handler(msg->pkt_type, msg->src_id, msg->param, msg->param_len, buf); + if(msg->param){ + k_free(msg->param); + } + memset(msg, 0, sizeof(struct rx_msg_struct)); +} + +void bl_onchiphci_interface_deinit(void) +{ + struct rx_msg_struct *msg; + + do{ + msg = k_fifo_get(&msg_queue, K_NO_WAIT); + if(msg){ + if(msg->param){ + k_free(msg->param); + } + }else{ + break; + } + }while(1); + + k_queue_free(&msg_queue); +} +#endif + +uint8_t bl_onchiphci_interface_init(void) +{ + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + memset(msg_array, 0, sizeof(msg_array)); + k_queue_init(&msg_queue, 9); + #endif + return bt_onchiphci_interface_init(bl_onchiphci_rx_packet_handler); +} + +int bl_onchiphci_send_2_controller(struct net_buf *buf) +{ + uint16_t opcode; + uint16_t dest_id = 0x00; + uint8_t buf_type; + uint8_t pkt_type; + hci_pkt_struct pkt; + + buf_type = bt_buf_get_type(buf); + switch(buf_type) + { + case BT_BUF_CMD: + { + struct bt_hci_cmd_hdr *chdr; + + if(buf->len < sizeof(struct bt_hci_cmd_hdr)) + return -EINVAL; + + chdr = (void *)buf->data; + + if(buf->len < chdr->param_len) + return -EINVAL; + + pkt_type = BT_HCI_CMD; + opcode = sys_le16_to_cpu(chdr->opcode); + //move buf to the payload + net_buf_pull(buf, sizeof(struct bt_hci_cmd_hdr)); + switch(opcode) + { + //Refer to hci_cmd_desc_tab_le, for the ones of which dest_ll is BLE_CTRL + case BT_HCI_OP_LE_CONN_UPDATE: + case BT_HCI_OP_LE_READ_CHAN_MAP: + case BT_HCI_OP_LE_READ_REMOTE_FEATURES: + case BT_HCI_OP_LE_START_ENCRYPTION: + case BT_HCI_OP_LE_LTK_REQ_REPLY: + case BT_HCI_OP_LE_LTK_REQ_NEG_REPLY: + case BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY: + case BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY: + case BT_HCI_OP_LE_SET_DATA_LEN: + case BT_HCI_OP_LE_READ_PHY: + case BT_HCI_OP_LE_SET_PHY: + { + //dest_id is connectin handle + dest_id = buf->data[0]; + } + default: + break; + } + pkt.p.hci_cmd.opcode = opcode; + pkt.p.hci_cmd.param_len = chdr->param_len; + pkt.p.hci_cmd.params = buf->data; + + break; + break; + } + case BT_BUF_ACL_OUT: + { + struct bt_hci_acl_hdr *acl; + //connhandle +l2cap field + uint16_t connhdl_l2cf, tlt_len; + + if(buf->len < sizeof(struct bt_hci_acl_hdr)) + return -EINVAL; + + pkt_type = BT_HCI_ACL_DATA; + acl = (void *)buf->data; + tlt_len = sys_le16_to_cpu(acl->len); + connhdl_l2cf = sys_le16_to_cpu(acl->handle); + //move buf to the payload + net_buf_pull(buf, sizeof(struct bt_hci_acl_hdr)); + + if(buf->len < tlt_len) + return -EINVAL; + + //get connection_handle + dest_id = bt_acl_handle(connhdl_l2cf); + pkt.p.acl_data.conhdl = dest_id; + pkt.p.acl_data.pb_bc_flag = bt_acl_flags(connhdl_l2cf); + pkt.p.acl_data.len = tlt_len; + pkt.p.acl_data.buffer = (uint8_t *)buf->data; + + break; + } + + default: + return -EINVAL; + } + + return bt_onchiphci_send(pkt_type, dest_id, &pkt); +} + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +static void bl_onchiphci_rx_packet_handler(uint8_t pkt_type, uint16_t src_id, uint8_t *param, + uint8_t param_len, void *rx_buf) +#else +static void bl_onchiphci_rx_packet_handler(uint8_t pkt_type, uint16_t src_id, uint8_t *param, + uint8_t param_len) +#endif +{ + uint8_t nb_h2c_cmd_pkts = 0x01, buf_type, *buf_data; + uint16_t tlt_len; + bool prio = true; + + struct net_buf *buf = NULL; + static uint32_t monitor = 0; + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + struct rx_msg_struct *rx_msg; + #endif + + + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + if(!rx_buf) + { + #endif + buf_type = (pkt_type == BT_HCI_ACL_DATA)? BT_BUF_ACL_IN: BT_BUF_EVT; + + if(pkt_type == BT_HCI_CMD_CMP_EVT || pkt_type == BT_HCI_CMD_STAT_EVT) + { + buf = bt_buf_get_cmd_complete(K_FOREVER); + } + else + { + do{ + // When deal with LE ADV report, Don't use reserve buf + if(((pkt_type == BT_HCI_LE_EVT && param[0] == BT_HCI_EVT_LE_ADVERTISING_REPORT) + || (pkt_type == BT_HCI_ACL_DATA)) && + (bt_buf_get_rx_avail_cnt() <= CONFIG_BT_RX_BUF_RSV_COUNT)) + { + break; + } + //not use K_FOREVER, rw main loop thread cannot be blocked here. if there is no rx buffer,directly igore. + //otherwise, if rw main loop blocked here, hci command cannot be handled. + buf = bt_buf_get_rx(buf_type, K_NO_WAIT); + }while(0); + } + + if(!buf){ + if(((++monitor)&0xff) == 0) + { + BT_WARN("hci_rx_pool is not available\n"); + } + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + //if it is le adv pkt, discard it. + if(pkt_type == BT_HCI_LE_EVT && param[0] == BT_HCI_EVT_LE_ADVERTISING_REPORT) + { + return; + } + else + { + rx_msg = bl_find_valid_queued_entry(); + if(!rx_msg) + { + return; + } + else + { + rx_msg->pkt_type = pkt_type; + rx_msg->src_id = src_id; + if(param_len) + { + rx_msg->param = k_malloc(param_len); + memcpy(rx_msg->param, param, param_len); + } + rx_msg->param_len = param_len; + k_fifo_put(&msg_queue, rx_msg); + return; + } + } + #else//OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER + return; + #endif + } + + monitor = 0; + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + } + else + { + buf = rx_buf; + } + #endif + + buf_data = net_buf_tail(buf); + + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + bt_buf_set_rx_adv(buf, false); + #endif + + switch(pkt_type) + { + case BT_HCI_CMD_CMP_EVT: + { + tlt_len = BT_HCI_EVT_CC_PARAM_OFFSET + param_len; + *buf_data++ = BT_HCI_EVT_CMD_COMPLETE; + *buf_data++ = BT_HCI_CCEVT_HDR_PARLEN + param_len; + *buf_data++ = nb_h2c_cmd_pkts; + sys_put_le16(src_id, buf_data); + buf_data += 2; + memcpy(buf_data, param, param_len); + break; + } + case BT_HCI_CMD_STAT_EVT: + { + tlt_len = BT_HCI_CSEVT_LEN; + *buf_data++ = BT_HCI_EVT_CMD_STATUS; + *buf_data++ = BT_HCI_CSVT_PARLEN; + *buf_data++ = *(uint8_t *)param; + *buf_data++ = nb_h2c_cmd_pkts; + sys_put_le16(src_id, buf_data); + break; + } + case BT_HCI_LE_EVT: + { + prio = false; + bt_buf_set_type(buf, BT_BUF_EVT); + + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + if(param[0] == BT_HCI_EVT_LE_ADVERTISING_REPORT) + { + bt_buf_set_rx_adv(buf, true); + } + #endif + + tlt_len = BT_HCI_EVT_LE_PARAM_OFFSET + param_len; + *buf_data++ = BT_HCI_EVT_LE_META_EVENT; + *buf_data++ = param_len; + memcpy(buf_data, param, param_len); + break; + } + case BT_HCI_EVT: + { + if(src_id != BT_HCI_EVT_NUM_COMPLETED_PACKETS) + { + prio = false; + } + bt_buf_set_type(buf, BT_BUF_EVT); + tlt_len = BT_HCI_EVT_LE_PARAM_OFFSET + param_len; + *buf_data++ = src_id; + *buf_data++ = param_len; + memcpy(buf_data, param, param_len); + break; + } + case BT_HCI_ACL_DATA: + { + prio = false; + bt_buf_set_type(buf, BT_BUF_ACL_IN); + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + tlt_len = bt_onchiphci_hanlde_rx_acl(param, buf_data); + #else + tlt_len = param_len; + memcpy(buf_data, param, param_len); + #endif + break; + } + default: + { + net_buf_unref(buf); + return; + } + } + + net_buf_add(buf, tlt_len); + + if(prio) + { + bt_recv_prio(buf); + } + else + { + hci_driver_enque_recvq(buf); + } +} diff --git a/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h b/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h new file mode 100644 index 00000000..b3913663 --- /dev/null +++ b/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h @@ -0,0 +1,30 @@ +#ifndef __BL_HCI_WRAPPER_H__ +#define __BL_HCI_WRAPPER_H__ + +#include "net/buf.h" +#include "bluetooth.h" + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +struct rx_msg_struct{ + uint8_t pkt_type; + uint16_t src_id; + uint8_t *param; + uint8_t param_len; +}__packed; +#endif + +typedef enum { + DATA_TYPE_COMMAND = 1, + DATA_TYPE_ACL = 2, + DATA_TYPE_SCO = 3, + DATA_TYPE_EVENT = 4 +} serial_data_type_t; + +uint8_t bl_onchiphci_interface_init(void); + +int bl_onchiphci_send_2_controller(struct net_buf *buf); + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +void bl_onchiphci_interface_deinit(void); +#endif +#endif //__BL_CONTROLLER_H__ \ No newline at end of file diff --git a/components/ble/ble_stack/cli_cmds/ble_cli_cmds.c b/components/ble/ble_stack/cli_cmds/ble_cli_cmds.c new file mode 100644 index 00000000..866d85c4 --- /dev/null +++ b/components/ble/ble_stack/cli_cmds/ble_cli_cmds.c @@ -0,0 +1,1671 @@ +#include +#include "conn.h" +#include "conn_internal.h" +#include "gatt.h" +#include "hci_core.h" +#include "FreeRTOS.h" +#include "task.h" +#include "cli.h" +#include "bl_port.h" +#include "ble_cli_cmds.h" +#include "ble_lib_api.h" +#if defined(CONFIG_BLE_MULTI_ADV) +#include "multi_adv.h" +#endif + +#if defined(CONFIG_HOGP_SERVER) +#include "hog.h" +#endif + +#define PASSKEY_MAX 0xF423F +#define NAME_LEN 30 +#define CHAR_SIZE_MAX 512 + +static u8_t selected_id = BT_ID_DEFAULT; +bool ble_inited = false; + +#if defined(CONFIG_BT_CONN) +struct bt_conn *default_conn = NULL; +#endif + +struct bt_data ad_discov[2] = { + BT_DATA_BYTES(BT_DATA_FLAGS,(BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)), + BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, 13), +}; +#if defined(CONFIG_BLE_MULTI_ADV) +static int ble_adv_id; +#endif + +#define vOutputString(...) printf(__VA_ARGS__) + +static void blecli_init(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(BL702) +static void blecli_set_2M_phy(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_set_default_phy(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +static void blecli_get_device_name(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_set_device_name(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(CONFIG_BT_OBSERVER) +static void blecli_start_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_stop_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(BL702) || defined(BL602) +static void blecli_scan_filter_size(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif /* BL702 || BL602 */ +#endif +static void blecli_read_local_address(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(CONFIG_BT_PERIPHERAL) +static void blecli_set_adv_channel(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_start_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_stop_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_BLE_TP_SERVER) +static void blecli_tp_start(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) +static void blecli_connect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +static void blecli_disconnect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_select_conn(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_conn_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_unpair(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_BT_SMP) +static void blecli_security(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_auth(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_auth_cancel(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_auth_passkey_confirm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_auth_pairing_confirm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_auth_passkey(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +static void blecli_exchange_mtu(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_discover(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_read(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_write(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_write_without_rsp(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_subscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_unsubscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_set_data_len(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_get_all_conn_info(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_disable(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(CONFIG_BLE_MULTI_ADV) +static void blecli_start_multi_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void blecli_stop_multi_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_SET_TX_PWR) +static void blecli_set_tx_pwr(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_HOGP_SERVER) +static void blecli_hog_srv_notify(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif + +const struct cli_command btStackCmdSet[] STATIC_CLI_CMD_ATTRIBUTE = { +#if 0 + /*1.The cmd string to type, 2.Cmd description, 3.The function to run, 4.Number of parameters*/ + + {"ble_init", "\r\nble_init:[Initialize]\r\n Parameter[Null]\r\n", blecli_init}, + + {"ble_get_device_name", "\r\nble_get_device_name:[Read local device name]\r\n Parameter[Null]\r\n", blecli_get_device_name}, + + {"ble_set_device_name", "\r\nble_set_device_name:\r\n\[Lenth of name]\r\n\[name]\r\n", blecli_set_device_name}, +#if defined(CONFIG_BLE_TP_SERVER) + + {"ble_tp_start", "\r\nble_tp_start:\r\n\ + [TP test,1:enable, 0:disable]\r\n", blecli_tp_start}, +#endif + +#if defined(CONFIG_BT_OBSERVER) + {"ble_start_scan", "\r\nble_start_scan:\r\n\ + [Scan type, 0:passive scan, 1:active scan]\r\n\ + [Duplicate filtering, 0:Disable duplicate filtering, 1:Enable duplicate filtering]\r\n\ + [Scan interval, 0x0004-4000,e.g.0080]\r\n\ + [Scan window, 0x0004-4000,e.g.0050]\r\n", blecli_start_scan}, + + {"ble_stop_scan", "\r\nble_stop_scan:[Stop scan]\r\nParameter[Null]\r\n", blecli_stop_scan}, +#endif + +#if defined(CONFIG_BT_PERIPHERAL) + {"ble_start_adv", "\r\nble_start_adv:\r\n\ + [Adv type,0:adv_ind,1:adv_scan_ind,2:adv_nonconn_ind]\r\n\ + [Mode, 0:discov, 1:non-discov]\r\n\ + [Adv Interval Min,0x0020-4000,e.g.0030]\r\n\ + [Adv Interval Max,0x0020-4000,e.g.0060]\r\n", blecli_start_advertise}, + + {"ble_stop_adv", "\r\nble_stop_adv:[Stop advertising]\r\nParameter[Null]\r\n", blecli_stop_advertise}, + + {"ble_read_local_address", "\r\nble_read_local_address:[Read local address]\r\n", blecli_read_local_address}, +#endif + +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) + {"ble_connect", "\r\nble_connect:[Connect remote device]\r\n\ + [Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value, e.g.112233AABBCC]\r\n", blecli_connect}, +#endif //CONFIG_BT_CENTRAL + + {"ble_disconnect", "\r\nble_disconnect:[Disconnect remote device]\r\n\ + [Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value,e.g.112233AABBCC]\r\n", blecli_disconnect}, + + {"ble_select_conn", "\r\nble_select_conn:[Select a specific connection]\r\n\ + [Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value, e.g.112233AABBCC]\r\n", blecli_select_conn}, + + {"ble_unpair", "\r\nble_unpair:[Unpair connection]\r\n\ + [Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value, all 0: unpair all connection, otherwise:unpair specific connection]\r\n", blecli_unpair}, + + {"ble_conn_update", "\r\nble_conn_update:\r\n\ + [Conn Interval Min,0x0006-0C80,e.g.0030]\r\n\ + [Conn Interval Max,0x0006-0C80,e.g.0030]\r\n\ + [Conn Latency,0x0000-01f3,e.g.0004]\r\n\ + [Supervision Timeout,0x000A-0C80,e.g.0010]\r\n", blecli_conn_update}, +#endif //CONFIG_BT_CONN + +#if defined(CONFIG_BT_SMP) + {"ble_security", "\r\nble_security:[Start security]\r\n\ + [Security level, Default value 4, 2:BT_SECURITY_MEDIUM, 3:BT_SECURITY_HIGH, 4:BT_SECURITY_FIPS]\r\n", blecli_security}, + + {"ble_auth", "\r\nble_auth:[Register auth callback]\r\n", blecli_auth}, + + {"ble_auth_cancel", "\r\nble_auth_cancel:[Register auth callback]\r\n", blecli_auth_cancel}, + + {"ble_auth_passkey_confirm", "\r\nble_auth_passkey_confirm:[Confirm passkey]\r\n", blecli_auth_passkey_confirm}, + + {"ble_auth_pairing_confirm", "\r\nble_auth_pairing_confirm:[Confirm pairing in secure connection]\r\n", blecli_auth_pairing_confirm}, + + {"ble_auth_passkey", "\r\nble_auth_passkey:[Input passkey]\r\n[Passkey, 00000000-000F423F]", blecli_auth_passkey}, + +#endif //CONFIG_BT_SMP + +#if defined(CONFIG_BT_GATT_CLIENT) + {"ble_exchange_mtu", "\r\nble_exchange_mtu:[Exchange mtu]\r\n Parameter[Null]\r\n", blecli_exchange_mtu}, + + {"ble_discover", "\r\nble_discover:[Gatt discovery]\r\n\ + [Discovery type, 0:Primary, 1:Secondary, 2:Include, 3:Characteristic, 4:Descriptor]\r\n\ + [Uuid value, 2 Octets, e.g.1800]\r\n\ + [Start handle, 2 Octets, e.g.0001]\r\n\ + [End handle, 2 Octets, e.g.ffff]\r\n", blecli_discover}, + + {"ble_read", "\r\nble_read:[Gatt Read]\r\n\ + [Attribute handle, 2 Octets]\r\n\ + [Value offset, 2 Octets]\r\n", blecli_read}, + + {"ble_write", "\r\nble_write:[Gatt write]\r\n\ + [Attribute handle, 2 Octets]\r\n\ + [Value offset, 2 Octets]\r\n\ + [Value length, 2 Octets]\r\n\ + [Value data]\r\n", blecli_write}, + + {"ble_write_without_rsp", "\r\nble_write_without_rsp:[Gatt write without response]\r\n\ + [Sign, 0: No need signed, 1:Signed write cmd if no smp]\r\n\ + [Attribute handle, 2 Octets]\r\n\ + [Value length, 2 Octets]\r\n\ + [Value data]\r\n", blecli_write_without_rsp}, + + {"ble_subscribe", "\r\nble_subscribe:[Gatt subscribe]\r\n\ + [CCC handle, 2 Octets]\r\n\ + [Value handle, 2 Octets]\r\n\ + [Value, 1:notify, 2:indicate]\r\n", blecli_subscribe}, + + {"ble_unsubscribe", "\r\nble_unsubscribe:[Gatt unsubscribe]\r\n Parameter[Null]\r\n", blecli_unsubscribe}, +#endif /*CONFIG_BT_GATT_CLIENT*/ + + {"ble_set_data_len", + "\r\nble_set_data_len:[LE Set Data Length]\r\n\ + [tx octets, 2 octets]\r\n\ + [tx time, 2 octets]\r\n", + blecli_set_data_len}, + + {"ble_conn_info", "\r\nble_conn_info:[LE get all connection devices info]\r\n", blecli_get_all_conn_info}, + +#if defined(CONFIG_SET_TX_PWR) + {"ble_set_tx_pwr", + "\r\nble_set_tx_pwr:[Set tx power mode]\r\n\ + [mode, 1 octet, value:5,6,7]\r\n", + blecli_set_tx_pwr}, +#endif + +#else + {"ble_init", "", blecli_init}, + +#if defined(CONFIG_BLE_TP_SERVER) + {"ble_tp_start", "", blecli_tp_start}, +#endif + + #if defined(BL702) + {"ble_set_2M_Phy", "", blecli_set_2M_phy}, + {"ble_set_default_phy", "", blecli_set_default_phy}, + #endif +#if defined(BFLB_DISABLE_BT) + {"ble_disable", "", blecli_disable}, +#endif + {"ble_get_device_name", "", blecli_get_device_name}, + {"ble_set_device_name", "", blecli_set_device_name}, +#if defined(CONFIG_BT_OBSERVER) + {"ble_start_scan", "", blecli_start_scan}, + {"ble_stop_scan", "", blecli_stop_scan}, +#if defined(BL702) || defined(BL602) + {"ble_scan_filter_size", "", blecli_scan_filter_size}, +#endif /* BL702 || BL602*/ +#endif +#if defined(CONFIG_BT_PERIPHERAL) + {"ble_set_adv_channel", "", blecli_set_adv_channel}, + {"ble_start_adv", "", blecli_start_advertise}, + {"ble_stop_adv", "", blecli_stop_advertise}, +#if defined(CONFIG_BLE_MULTI_ADV) + {"ble_start_multi_adv", "", blecli_start_multi_advertise}, + {"ble_stop_multi_adv", "", blecli_stop_multi_advertise}, +#endif + {"ble_read_local_address", "", blecli_read_local_address}, +#endif +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) + {"ble_connect", "", blecli_connect}, +#endif //CONFIG_BT_CENTRAL + {"ble_disconnect", "", blecli_disconnect}, + {"ble_select_conn", "", blecli_select_conn}, + {"ble_unpair", "", blecli_unpair}, + {"ble_conn_update", "", blecli_conn_update}, +#endif //CONFIG_BT_CONN +#if defined(CONFIG_BT_SMP) + {"ble_security", "", blecli_security}, + {"ble_auth", "", blecli_auth}, + {"ble_auth_cancel", "", blecli_auth_cancel}, + {"ble_auth_passkey_confirm", "", blecli_auth_passkey_confirm}, + {"ble_auth_pairing_confirm", "", blecli_auth_pairing_confirm}, + {"ble_auth_passkey", "", blecli_auth_passkey}, +#endif //CONFIG_BT_SMP +#if defined(CONFIG_BT_GATT_CLIENT) + {"ble_exchange_mtu", "", blecli_exchange_mtu}, + {"ble_discover", "", blecli_discover}, + {"ble_read", "", blecli_read}, + {"ble_write", "", blecli_write}, + {"ble_write_without_rsp", "", blecli_write_without_rsp}, + {"ble_subscribe", "", blecli_subscribe}, + {"ble_unsubscribe", "", blecli_unsubscribe}, +#endif /*CONFIG_BT_GATT_CLIENT*/ + {"ble_set_data_len", "", blecli_set_data_len}, + {"ble_conn_info", "", blecli_get_all_conn_info}, +#if defined(CONFIG_SET_TX_PWR) + {"ble_set_tx_pwr", "", blecli_set_tx_pwr}, +#endif +#if defined(CONFIG_HOGP_SERVER) + {"ble_hog_srv_notify", "", blecli_hog_srv_notify}, +#endif +#endif +}; + +#if defined(CONFIG_BT_CONN) +static void connected(struct bt_conn *conn, u8_t err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + +#if defined(CONFIG_BLE_MULTI_ADV) + if(ble_adv_id && !bt_le_multi_adv_stop(ble_adv_id)){ + ble_adv_id = 0; + } +#endif /* CONFIG_BLE_MULTI_ADV */ + + if (err) { + vOutputString("Failed to connect to %s (%u) \r\n", addr, err); + return; + } + + vOutputString("Connected: %s \r\n", addr); + + if (!default_conn) { + default_conn = conn; + } + +#if defined(CONFIG_BLE_RECONNECT_TEST) + if (conn->role == BT_CONN_ROLE_MASTER) { + if(bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) { + vOutputString("Disconnection fail. \r\n"); + }else{ + vOutputString("Disconnect success. \r\n"); + } + } +#endif +} + +static void disconnected(struct bt_conn *conn, u8_t reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + vOutputString("Disconnected: %s (reason %u) \r\n", addr, reason); + +#if defined(CONFIG_BLE_RECONNECT_TEST) + if(conn->role == BT_CONN_ROLE_SLAVE) { + if(set_adv_enable(true)) { + vOutputString("Restart adv fail. \r\n"); + } else { + vOutputString("Restart adv success. \r\n"); + } + } +#endif + + if (default_conn == conn) { + default_conn = NULL; + } +} + +static void le_param_updated(struct bt_conn *conn, u16_t interval, + u16_t latency, u16_t timeout) +{ + vOutputString("LE conn param updated: int 0x%04x lat %d to %d \r\n", interval, latency, timeout); +} + +#if defined(CONFIG_BT_SMP) +static void identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa, + const bt_addr_le_t *identity) +{ + char addr_identity[BT_ADDR_LE_STR_LEN]; + char addr_rpa[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(identity, addr_identity, sizeof(addr_identity)); + bt_addr_le_to_str(rpa, addr_rpa, sizeof(addr_rpa)); + + vOutputString("Identity resolved %s -> %s \r\n", addr_rpa, addr_identity); +} + +static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + vOutputString("Security changed: %s level %u \r\n", addr, level); +} +#endif + +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, + .le_param_updated = le_param_updated, +#if defined(CONFIG_BT_SMP) + .identity_resolved = identity_resolved, + .security_changed = security_changed, +#endif +}; +#endif //CONFIG_BT_CONN + +static void blecli_init(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + if(ble_inited){ + vOutputString("Has initialized \r\n"); + return; + } + + #if defined(CONFIG_BT_CONN) + default_conn = NULL; + bt_conn_cb_register(&conn_callbacks); + #endif + ble_inited = true; + vOutputString("Init successfully \r\n"); +} +#if defined(BL702) +static void blecli_set_2M_phy(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + if(!default_conn){ + vOutputString("Not connected \r\n"); + return; + } + if(!hci_le_set_phy(default_conn)){ + vOutputString("Set ble 2M Phy successfully \r\n"); + }else{ + vOutputString("Failed to set ble 2M Phy\r\n"); + } + +} + +static void blecli_set_default_phy(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t phy = 0; + u8_t default_phy = 0; + + if(!default_conn){ + vOutputString("Not connected \r\n"); + return; + } + get_uint8_from_string(&argv[1], &phy); + + if(phy == 0){ + default_phy = BT_HCI_LE_PHY_PREFER_1M; + }else if(phy == 1){ + default_phy = BT_HCI_LE_PHY_PREFER_2M; + }else if(phy == 2){ + default_phy = BT_HCI_LE_PHY_PREFER_CODED; + }else{ + vOutputString("Invaild parameter\r\n"); + } + + if(!hci_le_set_default_phy(default_conn,default_phy)){ + vOutputString("Set ble default(2M) Phy successfully \r\n"); + }else{ + vOutputString("Failed to set ble default(2M) Phy\r\n"); + } +} +#endif +static void blecli_get_device_name(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + const char *device_name = bt_get_name(); + + if(device_name){ + vOutputString("device_name: %s\r\n",device_name); + }else + vOutputString("Failed to read device name\r\n"); +} + +static void blecli_set_device_name(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err = 0; + + if(strlen(argv[1]) > 0 && strlen(argv[1])<=CONFIG_BT_DEVICE_NAME_MAX){ + err = bt_set_name((char*)argv[1]); + if(err){ + vOutputString("Failed to set device name\r\n"); + }else + vOutputString("Set the device name successfully\r\n"); + }else{ + vOutputString("Invaild lenth(%d)\r\n",strlen(argv[1])); + } +} +#if defined(CONFIG_BLE_TP_SERVER) +static void blecli_tp_start(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + extern u8_t tp_start; + + get_uint8_from_string(&argv[1], &tp_start); + if( tp_start == 1 ){ + vOutputString("Ble Throughput enable\r\n"); + }else if(tp_start == 0){ + vOutputString("Ble Throughput disable\r\n"); + }else{ + vOutputString("Invalid parameter\r\n"); + } +} + +#endif + +#if defined(CONFIG_BT_OBSERVER) +static bool data_cb(struct bt_data *data, void *user_data) +{ + char *name = user_data; + u8_t len; + + switch (data->type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + len = (data->data_len > NAME_LEN - 1)?(NAME_LEN - 1):(data->data_len); + memcpy(name, data->data, len); + return false; + default: + return true; + } +} + + +static void device_found(const bt_addr_le_t *addr, s8_t rssi, u8_t evtype, + struct net_buf_simple *buf) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + char name[NAME_LEN]; + + (void)memset(name, 0, sizeof(name)); + bt_data_parse(buf, data_cb, name); + bt_addr_le_to_str(addr, le_addr, sizeof(le_addr)); + + vOutputString("[DEVICE]: %s, AD evt type %u, RSSI %i %s \r\n",le_addr, evtype, rssi, name); +} + +static void blecli_start_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_scan_param scan_param; + int err; + + (void)err; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], &scan_param.type); + + get_uint8_from_string(&argv[2], &scan_param.filter_dup); + + get_uint16_from_string(&argv[3], &scan_param.interval); + + get_uint16_from_string(&argv[4], &scan_param.window); + + err = bt_le_scan_start(&scan_param, device_found); + + if(err){ + vOutputString("Failed to start scan (err %d) \r\n", err); + }else{ + vOutputString("Start scan successfully \r\n"); + } +} + + +static void blecli_stop_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + + err = bt_le_scan_stop(); + if (err) { + vOutputString("Stopping scanning failed (err %d)\r\n", err); + } else { + vOutputString("Scan successfully stopped \r\n"); + } +} + +#if defined(BL702) || defined(BL602) +static void blecli_scan_filter_size(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint8_t size; + int8_t err; + + if(argc != 2){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], &size); + + err = ble_controller_set_scan_filter_table_size(size); + if (err) { + vOutputString("Set failed (err %d)\r\n", err); + } else { + vOutputString("Set success\r\n"); + } +} +#endif /* BL702 || BL602 */ + +#endif //#if defined(CONFIG_BT_OBSERVER) + + +#if defined(CONFIG_BT_PERIPHERAL) +static void blecli_read_local_address(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t local_pub_addr; + bt_addr_le_t local_ram_addr; + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_get_local_public_address(&local_pub_addr); + bt_addr_le_to_str(&local_pub_addr, le_addr, sizeof(le_addr)); + vOutputString("Local public addr : %s\r\n", le_addr); + + bt_get_local_ramdon_address(&local_ram_addr); + bt_addr_le_to_str(&local_ram_addr, le_addr, sizeof(le_addr)); + vOutputString("Local random addr : %s\r\n", le_addr); +} + +static void blecli_set_adv_channel(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t channel = 7; + + if(argc != 2) + { + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], &channel); + + if (set_adv_channel_map(channel) == 0) + { + vOutputString("Set adv channel success\r\n"); + } + else + { + vOutputString("Failed to Set adv channel\r\n"); + } +} + +static void blecli_start_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_adv_param param; + const struct bt_data *ad; + size_t ad_len; + int err = -1; + uint8_t adv_type, mode; + + if(argc != 3 && argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + param.id = selected_id; + param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; + param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; + + /*Get adv type, 0:adv_ind, 1:adv_scan_ind, 2:adv_nonconn_ind 3: adv_direct_ind*/ + get_uint8_from_string(&argv[1], &adv_type); + vOutputString("adv_type 0x%x\r\n",adv_type); + if(adv_type == 0){ + param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME); + }else if(adv_type == 1){ + param.options = BT_LE_ADV_OPT_USE_NAME; + }else if(adv_type == 2){ + param.options = 0; + }else{ + vOutputString("Arg1 is invalid\r\n"); + return; + } + + /*Get mode, 0:General discoverable, 1:non discoverable, 2:limit discoverable*/ + get_uint8_from_string(&argv[2], &mode); + vOutputString("mode 0x%x\r\n",mode); + if(mode == 0 || mode == 1 || mode == 2){ + + if(mode == 0){ + struct bt_data gen_disc_data = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS,(BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)); + ad_discov[0] = gen_disc_data; + }else if(mode == 1){ + struct bt_data non_disc_data = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS,BT_LE_AD_NO_BREDR); + ad_discov[0] = non_disc_data; + }else if(mode == 2){ + struct bt_data limt_disc_data = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS,(BT_LE_AD_NO_BREDR | BT_LE_AD_LIMITED)); + ad_discov[0] = limt_disc_data; + }else{ + vOutputString("Invalied AD Mode 0x%x\r\n",mode); + } + + const char *name = bt_get_name(); + struct bt_data data = (struct bt_data)BT_DATA(BT_DATA_NAME_COMPLETE,name, strlen(name)); + ad_discov[1] = data; + + ad = ad_discov; + ad_len = ARRAY_SIZE(ad_discov); + + }else{ + vOutputString("Arg2 is invalid\r\n"); + return; + } + + if(argc == 5){ + get_uint16_from_string(&argv[3], ¶m.interval_min); + get_uint16_from_string(&argv[4], ¶m.interval_max); + + vOutputString("interval min 0x%x\r\n",param.interval_min); + vOutputString("interval max 0x%x\r\n",param.interval_max); + } + + if(adv_type == 1 || adv_type == 0){ + #if defined(CONFIG_BLE_MULTI_ADV) + if(ble_adv_id == 0){ + err = bt_le_multi_adv_start(¶m, ad, ad_len, &ad_discov[0], 1, &ble_adv_id); + } + #else + err = bt_le_adv_start(¶m, ad, ad_len, &ad_discov[0], 1); + #endif/*CONFIG_BLE_MULTI_ADV*/ + }else{ + #if defined(CONFIG_BLE_MULTI_ADV) + if(ble_adv_id == 0){ + err = bt_le_multi_adv_start(¶m, ad, ad_len, NULL, 0, &ble_adv_id); + } + #else + err = bt_le_adv_start(¶m, ad, ad_len, NULL, 0); + #endif + } + + if(err){ + vOutputString("Failed to start advertising (err %d) \r\n", err); + }else{ + vOutputString("Advertising started\r\n"); + } + +} + +static void blecli_stop_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ +#if defined(CONFIG_BLE_MULTI_ADV) + bool err = -1; + if(ble_adv_id && !bt_le_multi_adv_stop(ble_adv_id)){ + ble_adv_id = 0; + err = 0; + } + if(err){ +#else + if(bt_le_adv_stop()){ +#endif + vOutputString("Failed to stop advertising\r\n"); + }else{ + vOutputString("Advertising stopped\r\n"); + } +} + +#if defined(CONFIG_BLE_MULTI_ADV) +static void blecli_start_multi_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_adv_param param_1, param_2; + struct bt_data *ad_1, *ad_2; + size_t ad_len_1, ad_len_2; + int err_1, err_2; + int instant_id_1, instant_id_2; + + param_1.id = 0; + param_1.interval_min = 0x00A0; + param_1.interval_max = 0x00A0; + param_1.options = BT_LE_ADV_OPT_CONNECTABLE; + + const char *name_1 = "multi_adv_connect_0x0001"; + struct bt_data flag_1 = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS,(BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)); + struct bt_data data_1 = (struct bt_data)BT_DATA(BT_DATA_NAME_COMPLETE, name_1, strlen(name_1)); + ad_discov[0] = flag_1; + ad_discov[0] = data_1; + ad_1 = ad_discov; + ad_len_1 = ARRAY_SIZE(ad_discov); + + err_1 = bt_le_multi_adv_start(¶m_1, ad_1, ad_len_1, NULL, 0, &instant_id_1); + if(err_1){ + vOutputString("Failed to start multi adv_1 (err_1%d)\r\n", err_1); + }else{ + vOutputString("Multi Adv started --> instant_id: %d\r\n", instant_id_1); + } + + param_2.id = 0; + param_2.interval_min = 0x0140; + param_2.interval_max = 0x0140; + param_2.options = 0; + + const char *name_2 = "multi_adv_nonconn_0x0002"; + struct bt_data flag_2 = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS,(BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)); + struct bt_data data_2 = (struct bt_data)BT_DATA(BT_DATA_NAME_COMPLETE, name_2, strlen(name_2)); + ad_discov[0] = flag_2; + ad_discov[0] = data_2; + ad_2 = ad_discov; + ad_len_2 = ARRAY_SIZE(ad_discov); + + err_2 = bt_le_multi_adv_start(¶m_2, ad_2, ad_len_2, NULL, 0, &instant_id_2); + if(err_2){ + vOutputString("Failed to start multi adv_2 (err_2: %d)\r\n", err_2); + }else{ + vOutputString("Multi Adv started --> instant_id: %d\r\n", instant_id_2); + } +} + +static void blecli_stop_multi_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint8_t instant_id; + + get_uint8_from_string(&argv[1], &instant_id); + printf("Try to stop multi adv instant of: %d\n", instant_id); + + if(bt_le_multi_adv_stop(instant_id)) { + vOutputString("Multi adv instant %d stop failed\r\n", instant_id); + } + else + { + vOutputString("Multi adv instant %d stop successed\r\n", instant_id); + } +} +#endif //CONFIG_BLE_MULTI_ADV + +#endif //#if defined(CONFIG_BT_PERIPHERAL) + +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) +static void blecli_connect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + struct bt_conn *conn; + u8_t addr_val[6]; + s8_t type = -1; + int i=0; + + struct bt_le_conn_param param = { + .interval_min = BT_GAP_INIT_CONN_INT_MIN, + .interval_max = BT_GAP_INIT_CONN_INT_MAX, + .latency = 0, + .timeout = 400, + }; + + (void)memset(addr_val,0,6); + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], (u8_t *)&type); + + /*Get addr type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + addr.type = type; + + get_bytearray_from_string(&argv[2], addr_val,6); + for(i=0;i<6;i++){ + vOutputString("addr[%d]:[0x%x]\r\n",i,addr_val[i]); + } + + reverse_bytearray(addr_val, addr.a.val, 6); + + conn = bt_conn_create_le(&addr, /*BT_LE_CONN_PARAM_DEFAULT*/¶m); + + if(!conn){ + vOutputString("Connection failed\r\n"); + }else{ + vOutputString("Connection pending\r\n"); + } +} + +#endif //#if defined(CONFIG_BT_CENTRAL) + +static void blecli_disconnect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + u8_t addr_val[6]; + struct bt_conn *conn; + s8_t type = -1; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + get_uint8_from_string(&argv[1], (u8_t *)&type); + get_bytearray_from_string(&argv[2], addr_val,6); + reverse_bytearray(addr_val, addr.a.val, 6); + + /*Get addr type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + addr.type = type; + + conn = bt_conn_lookup_addr_le(selected_id, &addr); + + if(!conn){ + vOutputString("Not connected\r\n"); + return; + } + + if(bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)){ + vOutputString("Disconnection failed\r\n"); + }else{ + vOutputString("Disconnect successfully\r\n"); + } + + /*Notice:Because conn is got via bt_conn_lookup_addr_le in which bt_conn_ref(increase conn_ref by 1) + this conn, we need to bt_conn_unref(decrease conn_ref by 1) this conn.*/ + bt_conn_unref(conn); +} + +static void blecli_select_conn(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + struct bt_conn *conn; + u8_t addr_val[6]; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + /*Get addr type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + get_uint8_from_string(&argv[1], &addr.type); + + get_bytearray_from_string(&argv[2], addr_val,6); + + reverse_bytearray(addr_val, addr.a.val, 6); + + conn = bt_conn_lookup_addr_le(selected_id, &addr); + + if(!conn){ + vOutputString("No matching connection found\r\n"); + return; + } + + if(default_conn){ + bt_conn_unref(default_conn); + } + + default_conn = conn; +} + +static void blecli_unpair(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + u8_t addr_val[6]; + int err; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + /*Get addr type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + get_uint8_from_string(&argv[1], &addr.type); + + get_bytearray_from_string(&argv[2], addr_val,6); + + reverse_bytearray(addr_val, addr.a.val, 6); + + err = bt_unpair(selected_id, &addr); + + if(err){ + vOutputString("Failed to unpair\r\n"); + }else{ + vOutputString("Unpair successfully\r\n"); + } +} + +static void blecli_conn_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_conn_param param; + int err; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + get_uint16_from_string(&argv[1], ¶m.interval_min); + get_uint16_from_string(&argv[2], ¶m.interval_max); + get_uint16_from_string(&argv[3], ¶m.latency); + get_uint16_from_string(&argv[4], ¶m.timeout); + err = bt_conn_le_param_update(default_conn, ¶m); + + if (err) { + vOutputString("conn update failed (err %d)\r\n", err); + } else { + vOutputString("conn update initiated\r\n"); + } +} +#endif //#if defined(CONFIG_BT_CONN) + +#if defined(CONFIG_BT_SMP) +static void blecli_security(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + u8_t sec_level = /*BT_SECURITY_FIPS*/BT_SECURITY_L4; + + if(!default_conn){ + vOutputString("Please firstly choose the connection using ble_select_conn\r\n"); + return; + } + + if(argc == 2) + get_uint8_from_string(&argv[1], &sec_level); + + err = bt_conn_set_security(default_conn, sec_level); + + if(err){ + vOutputString("Failed to start security, (err %d) \r\n", err); + }else{ + vOutputString("Start security successfully\r\n"); + } +} + +static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("passkey_str is: %06u\r\n", passkey); +} + +static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("Confirm passkey for %s: %06u\r\n", addr, passkey); +} + +static void auth_passkey_entry(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("Enter passkey for %s\r\n", addr); +} + +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("Pairing cancelled: %s\r\n", addr); +} + +static void auth_pairing_confirm(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("Confirm pairing for %s\r\n", addr); +} + +static void auth_pairing_complete(struct bt_conn *conn, bool bonded) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + vOutputString("%s with %s\r\n", bonded ? "Bonded" : "Paired", addr); +} + +static void auth_pairing_failed(struct bt_conn *conn, enum bt_security_err reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + vOutputString("Pairing failed with %s\r\n", addr); +} + +static struct bt_conn_auth_cb auth_cb_display = { + .passkey_display = auth_passkey_display, + .passkey_entry = auth_passkey_entry, + .passkey_confirm = auth_passkey_confirm, + .cancel = auth_cancel, + .pairing_confirm = auth_pairing_confirm, + .pairing_failed = auth_pairing_failed, + .pairing_complete = auth_pairing_complete, +}; + +static void blecli_auth(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + + err = bt_conn_auth_cb_register(&auth_cb_display); + + if(err){ + vOutputString("Auth callback has already been registered\r\n"); + }else{ + vOutputString("Register auth callback successfully\r\n"); + } +} + +static void blecli_auth_cancel(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_conn *conn; + + if (default_conn) { + conn = default_conn; + }else { + conn = NULL; + } + + if (!conn) { + vOutputString("Not connected\r\n"); + return; + } + + bt_conn_auth_cancel(conn); +} + +static void blecli_auth_passkey_confirm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + bt_conn_auth_passkey_confirm(default_conn); +} + +static void blecli_auth_pairing_confirm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + bt_conn_auth_pairing_confirm(default_conn); +} + +static void blecli_auth_passkey(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint32_t passkey; + + if(argc != 2){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + passkey = atoi(argv[1]); + if (passkey > PASSKEY_MAX) { + vOutputString("Passkey should be between 0-999999\r\n"); + return; + } + + bt_conn_auth_passkey_entry(default_conn, passkey); +} + +#endif //#if defined(CONFIG_BT_SMP) + +#if defined(CONFIG_BT_GATT_CLIENT) +static void exchange_func(struct bt_conn *conn, u8_t err, + struct bt_gatt_exchange_params *params) +{ + vOutputString("Exchange %s MTU Size =%d \r\n", err == 0U ? "successful" : "failed",bt_gatt_get_mtu(conn)); +} + +static struct bt_gatt_exchange_params exchange_params; + +static void blecli_exchange_mtu(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + exchange_params.func = exchange_func; + + err = bt_gatt_exchange_mtu(default_conn, &exchange_params); + if (err) { + vOutputString("Exchange failed (err %d)\r\n", err); + } else { + vOutputString("Exchange pending\r\n"); + } +} + +static struct bt_gatt_discover_params discover_params; +static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0); + +static void print_chrc_props(u8_t properties) +{ + vOutputString("Properties: "); + + if (properties & BT_GATT_CHRC_BROADCAST) { + vOutputString("[bcast]\r\n"); + } + + if (properties & BT_GATT_CHRC_READ) { + vOutputString("[read]\r\n"); + } + + if (properties & BT_GATT_CHRC_WRITE) { + vOutputString("[write]\r\n"); + } + + if (properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) { + vOutputString("[write w/w rsp]\r\n"); + } + + if (properties & BT_GATT_CHRC_NOTIFY) { + vOutputString("[notify]\r\n"); + } + + if (properties & BT_GATT_CHRC_INDICATE) { + vOutputString("[indicate]"); + } + + if (properties & BT_GATT_CHRC_AUTH) { + vOutputString("[auth]\r\n"); + } + + if (properties & BT_GATT_CHRC_EXT_PROP) { + vOutputString("[ext prop]\r\n"); + } +} + +u8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) +{ + struct bt_gatt_service_val *gatt_service; + struct bt_gatt_chrc *gatt_chrc; + struct bt_gatt_include *gatt_include; + char str[37]; + + if (!attr) { + vOutputString( "Discover complete\r\n"); + (void)memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + + switch (params->type) { + case BT_GATT_DISCOVER_SECONDARY: + case BT_GATT_DISCOVER_PRIMARY: + gatt_service = attr->user_data; + bt_uuid_to_str(gatt_service->uuid, str, sizeof(str)); + vOutputString("Service %s found: start handle %x, end_handle %x\r\n", str, attr->handle, gatt_service->end_handle); + break; + case BT_GATT_DISCOVER_CHARACTERISTIC: + gatt_chrc = attr->user_data; + bt_uuid_to_str(gatt_chrc->uuid, str, sizeof(str)); + vOutputString("Characteristic %s found: attr->handle %x chrc->handle %x \r\n", str, attr->handle,gatt_chrc->value_handle); + print_chrc_props(gatt_chrc->properties); + break; + case BT_GATT_DISCOVER_INCLUDE: + gatt_include = attr->user_data; + bt_uuid_to_str(gatt_include->uuid, str, sizeof(str)); + vOutputString("Include %s found: handle %x, start %x, end %x\r\n", str, attr->handle, + gatt_include->start_handle, gatt_include->end_handle); + break; + default: + bt_uuid_to_str(attr->uuid, str, sizeof(str)); + vOutputString("Descriptor %s found: handle %x\r\n", str, attr->handle); + break; + } + + return BT_GATT_ITER_CONTINUE; +} + +static void blecli_discover(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + u8_t disc_type; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + discover_params.func = discover_func; + discover_params.start_handle = 0x0001; + discover_params.end_handle = 0xffff; + + get_uint8_from_string(&argv[1], &disc_type); + if(disc_type == 0){ + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + }else if(disc_type == 1){ + discover_params.type = BT_GATT_DISCOVER_SECONDARY; + }else if(disc_type == 2){ + discover_params.type = BT_GATT_DISCOVER_INCLUDE; + }else if(disc_type == 3){ + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + }else if(disc_type == 4){ + discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + }else{ + vOutputString("Invalid discovery type\r\n"); + return; + } + get_uint16_from_string(&argv[2], &uuid.val); + + if(uuid.val) + discover_params.uuid = &uuid.uuid; + else + discover_params.uuid = NULL; + + get_uint16_from_string(&argv[3], &discover_params.start_handle); + get_uint16_from_string(&argv[4], &discover_params.end_handle); + + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + vOutputString("Discover failed (err %d)\r\n", err); + } else { + vOutputString("Discover pending\r\n"); + } +} + +static struct bt_gatt_read_params read_params; + +static u8_t read_func(struct bt_conn *conn, u8_t err, struct bt_gatt_read_params *params, const void *data, u16_t length) +{ + vOutputString("Read complete: err %u length %u \r\n", err, length); + + char str[22]; + u8_t *buf = (u8_t *)data; + int i=0; + + memset(str,0,15); + + if(length > 0 && length <= sizeof(str)){ + memcpy(str,buf,length); + vOutputString("device name : %s \r\n",str); + for(i=0;ivalue) { + vOutputString("Unsubscribed\r\n"); + params->value_handle = 0U; + return BT_GATT_ITER_STOP; + } + +#if defined(CONFIG_BLE_TP_TEST) + if(!time){ + time = k_now_ms(); + } + len += length; + if(k_now_ms()- time >= 1000){ + vOutputString("data_len=[%d]\r\n",len); + time = k_now_ms(); + len = 0; + } +#endif + + //vOutputString("Notification: data length %u\r\n", length); + return BT_GATT_ITER_CONTINUE; +} + +static void blecli_subscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + if(argc != 4){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + get_uint16_from_string(&argv[1], &subscribe_params.ccc_handle); + get_uint16_from_string(&argv[2], &subscribe_params.value_handle); + get_uint16_from_string(&argv[3], &subscribe_params.value); + subscribe_params.notify = notify_func; + + int err = bt_gatt_subscribe(default_conn, &subscribe_params); + if (err) { + vOutputString("Subscribe failed (err %d)\r\n", err); + } else { + vOutputString("Subscribed\r\n"); + } + +} + +static void blecli_unsubscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + if (!subscribe_params.value_handle) { + vOutputString("No subscription found\r\n"); + return; + } + + int err = bt_gatt_unsubscribe(default_conn, &subscribe_params); + if (err) { + vOutputString("Unsubscribe failed (err %d)\r\n", err); + } else { + vOutputString("Unsubscribe success\r\n"); + } +} +#endif /* CONFIG_BT_GATT_CLIENT */ + +static void blecli_set_data_len(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u16_t tx_octets; + u16_t tx_time; + int err; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + get_uint16_from_string(&argv[1], &tx_octets); + get_uint16_from_string(&argv[2], &tx_time); + + err = bt_le_set_data_len(default_conn, tx_octets, tx_time); + if (err) { + vOutputString("ble_set_data_len, LE Set Data Length (err %d)\r\n", err); + } + else + { + vOutputString("ble_set_data_len, LE Set Data Length success\r\n"); + } +} + +static void blecli_get_all_conn_info(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_conn_info info[CONFIG_BT_MAX_CONN]; + char le_addr[BT_ADDR_LE_STR_LEN]; + int link_num; + + link_num = bt_conn_get_remote_dev_info(info); + + if(link_num > 0) + { + bt_addr_le_to_str(info[0].le.local, le_addr, sizeof(le_addr)); + vOutputString("ble local device address: %s\r\n", le_addr); + } + + vOutputString("ble connected devices count: %d\r\n", link_num); + for(int i = 0; i < link_num; i++) + { + bt_addr_le_to_str(info[i].le.remote, le_addr, sizeof(le_addr)); + vOutputString("[%d]: address %s\r\n", i, le_addr); + } +} + +#if defined(CONFIG_SET_TX_PWR) +static void blecli_set_tx_pwr(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t power; + int err; + + if(argc != 2){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1],&power); + #if defined(BL602) + if(power > 21){ + vOutputString("ble_set_tx_pwr, invalid value, value shall be in [%d - %d]\r\n", 0, 21); + return; + } + #elif defined(BL702) + if(power > 14){ + vOutputString("ble_set_tx_pwr, invalid value, value shall be in [%d - %d]\r\n", 0, 14); + return; + } + #endif + err = bt_set_tx_pwr((int8_t)power); + + if(err){ + vOutputString("ble_set_tx_pwr, Fail to set tx power (err %d)\r\n", err); + } + else{ + vOutputString("ble_set_tx_pwr, Set tx power successfully\r\n"); + } + +} +#endif + +#if defined(BFLB_DISABLE_BT) +static void blecli_disable(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + + err = bt_disable(); + if(err){ + vOutputString("Fail to disable bt, there is existed scan/adv/conn event \r\n"); + }else{ + vOutputString("Disable bt successfully\r\n"); + } +} +#endif + +#if defined(CONFIG_HOGP_SERVER) +static void blecli_hog_srv_notify(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint16_t hid_usage; + uint8_t press; + int err = 0; + + if(!default_conn){ + vOutputString("Not connected\r\n"); + return; + } + + get_uint16_from_string(&argv[1],&hid_usage); + get_uint8_from_string(&argv[2],&press); + + err = hog_notify(default_conn,hid_usage,press); + if(err){ + vOutputString("Failed to send notification\r\n"); + }else{ + vOutputString("Notification sent successfully\r\n"); + } +} +#endif + +int ble_cli_register(void) +{ + // static command(s) do NOT need to call aos_cli_register_command(s) to register. + // However, calling aos_cli_register_command(s) here is OK but is of no effect as cmds_user are included in cmds list. + // XXX NOTE: Calling this *empty* function is necessary to make cmds_user in this file to be kept in the final link. + //aos_cli_register_commands(btStackCmdSet, sizeof(btStackCmdSet)/sizeof(btStackCmdSet[0])); + return 0; +} diff --git a/components/ble/ble_stack/cli_cmds/ble_cli_cmds.h b/components/ble/ble_stack/cli_cmds/ble_cli_cmds.h new file mode 100644 index 00000000..7e1c2965 --- /dev/null +++ b/components/ble/ble_stack/cli_cmds/ble_cli_cmds.h @@ -0,0 +1,7 @@ +#ifndef _BLE_CLI_CMDS_H_ +#define _BLE_CLI_CMDS_H_ + +int ble_cli_register(void); +int pts_cli_register(void); + +#endif diff --git a/components/ble/ble_stack/cli_cmds/bredr_cli_cmds.c b/components/ble/ble_stack/cli_cmds/bredr_cli_cmds.c new file mode 100644 index 00000000..5f5f6076 --- /dev/null +++ b/components/ble/ble_stack/cli_cmds/bredr_cli_cmds.c @@ -0,0 +1,228 @@ +/** @file + * @brief Bluetooth BR/EDR shell module + * + * Provide some BR/EDR commands that can be useful to applications. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cli.h" + +#if PCM_PRINTF +#include "oi_codec_sbc.h" +#include "a2dp.h" +#endif + + + +static void bredr_connected(struct bt_conn *conn, u8_t err); +static void bredr_disconnected(struct bt_conn *conn, u8_t reason); + +static bool init = false; +static struct bt_conn_info conn_info; +static struct bt_conn *default_conn = NULL; + +static struct bt_conn_cb conn_callbacks = { + .connected = bredr_connected, + .disconnected = bredr_disconnected, +}; + +#if PCM_PRINTF +static void pcm(char *p_write_buffer, int write_buffer_len, int argc, char **argv); +#endif +static void bredr_init(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void bredr_write_eir(char *p_write_buffer, int write_buffer_len, int argc, char **argv); +static void bredr_discoverable(char *p_write_buffer, int write_buffer_len, int argc, char **argv); +static void bredr_connectable(char *p_write_buffer, int write_buffer_len, int argc, char **argv); + + + +const struct cli_command bredr_cmd_set[] STATIC_CLI_CMD_ATTRIBUTE = { + #if PCM_PRINTF + {"pcm", "", pcm}, + #endif + {"bredr_init", "", bredr_init}, + {"bredr_eir", "", bredr_write_eir}, + {"bredr_connectable", "", bredr_connectable}, + {"bredr_discoverable", "", bredr_discoverable}, + +}; + + +#if PCM_PRINTF +extern OI_BYTE sbc_frame[]; +extern OI_UINT32 sbc_frame_len; +OI_INT16 pcm_data[1024]; +OI_UINT32 pcm_bytes = sizeof(pcm_data); +OI_INT16 cool_edit[600000]; +OI_UINT32 byte_index = 0; +static void pcm(char *p_write_buffer, int write_buffer_len, int argc, char **argv) +{ + //a2dp_sbc_decode_init(); + //a2dp_sbc_decode_process(sbc_frame, sbc_frame_len); + + printf("pcm data count: %d \n", byte_index); + + OI_UINT32 samps = byte_index / sizeof(OI_INT16); + + printf("SAMPLES: %d\n", samps); + printf("BITSPERSAMPLE: 16\n"); + printf("CHANNELS: 2\n"); + printf("SAMPLERATE: 44100\n"); + printf("NORMALIZED: FALSE\n"); + + for(int i = 0; i < samps; i ++) + { + printf("%d\n", cool_edit[i]); + } + +} +#endif + +static void bredr_init(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + if(init){ + printf("bredr has initialized\n"); + return; + } + + default_conn = NULL; + bt_conn_cb_register(&conn_callbacks); + + init = true; + printf("bredr init successfully\n"); +} + + +static void bredr_connected(struct bt_conn *conn, u8_t err) +{ + char addr[BT_ADDR_STR_LEN]; + + bt_conn_get_info(conn, &conn_info); + bt_addr_to_str(conn_info.br.dst, addr, sizeof(addr)); + + if (err) { + printf("bredr failed to connect to %s (%u) \r\n", addr, err); + return; + } + + printf("bredr connected: %s \r\n", addr); + + if (!default_conn) + { + default_conn = conn; + } +} + +static void bredr_disconnected(struct bt_conn *conn, u8_t reason) +{ + char addr[BT_ADDR_STR_LEN]; + + bt_conn_get_info(conn, &conn_info); + bt_addr_to_str(conn_info.br.dst, addr, sizeof(addr)); + + printf("bredr disconnected: %s (reason %u) \r\n", addr, reason); + + if (default_conn == conn) + { + default_conn = NULL; + } +} + +static void bredr_write_eir(char *p_write_buffer, int write_buffer_len, int argc, char **argv) +{ + int err; + char *name = "Bouffalolab-bl606p-classic-bt"; + uint8_t rec = 1; + uint8_t data[32] = {0}; + + data[0] = 30; + data[1] = 0x09; + memcpy(data+2, name, strlen(name)); + + for(int i = 0; i < strlen(name); i++) + { + printf("0x%02x ", data[2+i]); + } + printf("\n"); + + err = bt_br_write_eir(rec, data); + if (err) { + printf("BR/EDR write EIR failed, (err %d)\n", err); + } else { + printf("BR/EDR write EIR done.\n"); + } +} + +static void bredr_discoverable(char *p_write_buffer, int write_buffer_len, int argc, char **argv) +{ + int err; + uint8_t action; + + if(argc != 2){ + printf("Number of parameters is not correct\n"); + return; + } + + get_uint8_from_string(&argv[1], &action); + + if (action == 1) { + err = bt_br_set_discoverable(true); + } else if (action == 0) { + err = bt_br_set_discoverable(false); + } else { + printf("Arg1 is invalid\n"); + return; + } + + if (err) { + printf("BR/EDR set discoverable failed, (err %d)\n", err); + } else { + printf("BR/EDR set discoverable done.\n"); + } +} + +static void bredr_connectable(char *p_write_buffer, int write_buffer_len, int argc, char **argv) +{ + int err; + uint8_t action; + + if(argc != 2){ + printf("Number of parameters is not correct\n"); + return; + } + + get_uint8_from_string(&argv[1], &action); + + if (action == 1) { + err = bt_br_set_connectable(true); + } else if (action == 0) { + err = bt_br_set_connectable(false); + } else { + printf("Arg1 is invalid\n"); + return; + } + + if (err) { + printf("BR/EDR set connectable failed, (err %d)\n", err); + } else { + printf("BR/EDR set connectable done.\n"); + } +} + + +int bredr_cli_register(void) +{ + // static command(s) do NOT need to call aos_cli_register_command(s) to register. + // However, calling aos_cli_register_command(s) here is OK but is of no effect as cmds_user are included in cmds list. + // XXX NOTE: Calling this *empty* function is necessary to make cmds_user in this file to be kept in the final link. + //aos_cli_register_commands(bredr_cmd_set, sizeof(bredr_cmd_set)/sizeof(bredr_cmd_set[0])); + return 0; +} + diff --git a/components/ble/ble_stack/cli_cmds/pts_cli_cmds.c b/components/ble/ble_stack/cli_cmds/pts_cli_cmds.c new file mode 100644 index 00000000..36e432a3 --- /dev/null +++ b/components/ble/ble_stack/cli_cmds/pts_cli_cmds.c @@ -0,0 +1,1826 @@ +#if defined(CONFIG_BT_STACK_PTS) +#include +#include "conn.h" +#include "gatt.h" +#include "hci_core.h" +#include "FreeRTOS.h" +#include "task.h" +#include "cli.h" +#include "ble_cli_cmds.h" +#include "keys.h" +#include "rpa.h" +#include "log.h" + +#define LIM_ADV_TIME 30720 +#define LIM_SCAN_TIME 10240 +#define PASSKEY_MAX 0xF423F +#define NAME_LEN 30 +#define CHAR_SIZE_MAX 512 + +u8_t peer_irk[16] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef +}; + +u8_t pts_address[6] = { + //0xE9,0x20,0xF2,0xDC,0x1b,0x00 + //0xCA,0x97,0xDE,0x05,0xB9,0x18 + 0x18,0xB9,0x05,0xDE,0x97,0xCA +}; + +const u8_t discover_mode[] = { + (BT_LE_AD_LIMITED | BT_LE_AD_NO_BREDR) +}; + +static const u8_t service_uuid[16] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f +}; + +#if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) +struct gatt_value { + u16_t len; + u8_t *data; + u8_t enc_key_size; + u8_t flags[1]; +}; + +static struct gatt_value value; +#endif +static const u8_t service_data[]= {0x00,0x01}; +static const u8_t data_manu[4]= {0x71,0x01,0x04,0x13}; +static const u8_t tx_power[1]= {0x50}; +static const u8_t data_appearance[2]= {0x80, 0x01}; +static const u8_t name[] = "BL702-BLE-DEV"; +extern volatile u8_t event_flag; +extern struct bt_conn *default_conn; +//extern struct bt_data ad_discov[2]; + +static struct bt_data ad_discov[2] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + #if defined(BL602) + BT_DATA(BT_DATA_NAME_COMPLETE, "BL602-BLE-DEV-PTS", 17), + #else + BT_DATA(BT_DATA_NAME_COMPLETE, "BL702-BLE-DEV", 13), + #endif +}; + +static struct bt_data ad_non_discov[] = { + BT_DATA(BT_DATA_NAME_COMPLETE, "BL602-BLE-DEV-PTS", 17), +}; + +static u8_t selected_id = BT_ID_DEFAULT; + +#define vOutputString(...) printf(__VA_ARGS__) + +static void pts_ble_add_peer_irk(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); + +static void pts_ble_start_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_start_scan_rpa(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_start_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_connect_le(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_disconnect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_select_conn(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_conn_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_start_scan_timeout(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_add_dev_to_resolve_list(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_address_register(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_set_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_set_smp_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_clear_smp_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_bondable(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_read_uuid(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_mread(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_discover_uuid_128(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_read_uuid_128(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); + +#if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) +static void pts_set_enc_key_size(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(CONFIG_BT_SMP) +static void pts_ble_set_mitm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +#if defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) +static void pts_ble_notify(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +/*Service change*/ +static void pts_ble_sc_indicate(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +static void pts_ble_indicate(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +static void pts_ble_register_pts_svc(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#if defined(CONFIG_BT_WHITELIST) +static void pts_ble_bt_le_whitelist_add(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void pts_ble_wl_connect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +#endif +static void pts_ble_prepare_write(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); + +const struct cli_command PtsCmdSet[] STATIC_CLI_CMD_ATTRIBUTE = { + /*1.The cmd string to type, 2.Cmd description, 3.The function to run, 4.Number of parameters*/ + + {"pts_ble_address_register", "\r\npts_ble_address_register:\r\n\[Address type, 0:non-rpa, 1:rpa, 2:public adderss]\r\n\ + [Pts_address, e.g.peer_addr]\r\n" , pts_ble_address_register}, + {"pts_ble_set_flag", "\r\npts_ble_set_flag:[Set flag for different applications]\r\n\[Flag, e.g.0,1]\r\n", pts_ble_set_flag}, + {"pts_ble_set_smp_flag", "\r\npts_ble_set_smp_flag:[Set flag for SM test]\r\n\[Flag, e.g.0,1]\r\n", pts_ble_set_smp_flag}, + {"pts_ble_clear_smp_flag", "\r\npts_ble_clear_smp_flag:[Clear smp test flag]\r\n\[Flag, e.g.0,1]\r\n", pts_ble_clear_smp_flag}, + {"pts_ble_bondable", "\r\npts_ble_bondable:[Enable or disable bond ]\r\n\ + [State, e.g.0x01:bondable mode, 0x00:non-bondable mode]\r\n", pts_ble_bondable}, + {"pts_ble_conn_update", "\r\npts_ble_conn_update:\r\n\ + [Conn Interval Min,0x0006-0C80,e.g.0030]\r\n\ + [Conn Interval Max,0x0006-0C80,e.g.0030]\r\n\ + [Conn Latency,0x0000-01f3,e.g.0004]\r\n\ + [Supervision Timeout,0x000A-0C80,e.g.0010]\r\n", pts_ble_conn_update}, + {"pts_ble_add_peer_irk", "\r\npts_ble_add_peer_irk:[Set peer irk]\r\n\ + [peer irk]\r\n", pts_ble_add_peer_irk}, + #if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) + {"pts_set_enc_key_size", "\r\npts_set_enc_key_size:[Set key size]\r\n\ + [size, e.g.0,1]\r\n\ + [index, e.g.0,1]\r\n", pts_set_enc_key_size}, + #endif + +#if defined(CONFIG_BT_OBSERVER) + {"pts_ble_start_scan", "\r\npts_ble_start_scan:\r\n\ + [Scan type, 0:passive scan, 1:active scan]\r\n\ + [Duplicate filtering, 0:Disable duplicate filtering, 1:Enable duplicate filtering]\r\n\ + [Scan interval, 0x0004-4000,e.g.0080]\r\n\ + [Scan window, 0x0004-4000,e.g.0050]\r\n\ + [Is_RPA,0:non-rpa, 1:rpa]\r\n", pts_ble_start_scan}, + + {"ble_start_scan_timeout", "\r\nble_start_scan_timeout:\r\n\ + [Scan type, 0:passive scan, 1:active scan]\r\n\ + [Duplicate filtering, 0:Disable duplicate filtering, 1:Enable duplicate filtering]\r\n\ + [Scan interval, 0x0004-4000,e.g.0080]\r\n\ + [Scan window, 0x0004-4000,e.g.0050]\r\n\ + [Is_RPA, 0:non-rpa, 1:rpa]\r\n",pts_ble_start_scan_timeout}, + + {"pts_ble_add_dev_to_resolve_list", "\r\nble_add_dev_to_resolve_list:\r\n\ + [Address type, 0:non-rpa, 1:rpa, 2:public adderss]\r\n\ + [Peer_address, e.g.peer_addr]\r\n" , pts_ble_add_dev_to_resolve_list}, +#endif + +#if defined(CONFIG_BT_PERIPHERAL) + {"pts_ble_start_adv", "\r\npts_ble_start_adv:\r\n\ + [Adv type,0:adv_ind,1:adv_scan_ind,2:adv_nonconn_ind 3: adv_direct_ind 4:adv_direct_ind_low_duty]\r\n\ + [Mode, 0:discov, 1:non-discov,2:limit discoverable]\r\n\ + [Addr_type, 0:non-rpa,1:rpa,2:public]\r\n\ + [Adv Interval Min,0x0020-4000,e.g.0030]\r\n\ + [Adv Interval Max,0x0020-4000,e.g.0060]\r\n", pts_ble_start_advertise}, + +#if defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) + {"pts_ble_notify", "\r\npts_ble_notify:\r\n [Lenth, e.g. 0000]\r\n [Data, e.g. 00]\r\n", pts_ble_notify}, +#endif + +#if defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) + {"pts_ble_indicate", "\r\npts_ble_indicate:\r\n [Lenth, e.g. 0000]\r\n [Data, e.g. 00]\r\n", pts_ble_indicate}, +#endif + {"pts_ble_register_pts_svc", "\r\npts_ble_register_pts_svc:\r\n", pts_ble_register_pts_svc}, + {"pts_ble_sc_indicate", "\r\npts_ble_sc_indicate:\r\n", pts_ble_sc_indicate}, +#endif + +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) + + {"pts_ble_connect_le", "\r\npts_ble_connect_le:[Connect remote device]\r\n\ + [Perr Address type,0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value, e.g.112233AABBCC]\r\n\ + [Own Address type,0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n", pts_ble_connect_le}, + +#if defined(CONFIG_BT_WHITELIST) + {"pts_ble_wl_connect", "\r\npts_ble_wl_connect:[Autoconnect whilt list device]\r\n[Enable, 0x01:enable, 0x02:disable]\r\n\ + [Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n",pts_ble_wl_connect}, + {"pts_ble_bt_le_whitelist_add", "\r\npts_ble_bt_le_whitelist_add:[add device to white list]\r\n\ + [Address type,0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value, e.g.112233AABBCC]\r\n",pts_ble_bt_le_whitelist_add}, +#endif//CONFIG_BT_WHITELIST +#endif //CONFIG_BT_CENTRAL + + {"pts_ble_disconnect", "\r\npts_ble_disconnect:[Disconnect remote device]\r\n\ + [Address type, 0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND]\r\n\ + [Address value,e.g.112233AABBCC]\r\n", pts_ble_disconnect}, + +#endif //CONFIG_BT_CONN + +#if defined(CONFIG_BT_SMP) + {"pts_ble_set_mitm", "\r\npts_ble_set_mitm:[set MIMT]\r\n\[State, 0x01:define,0x00:undefine]\r\n", pts_ble_set_mitm}, +#endif //CONFIG_BT_SMP + +#if defined(CONFIG_BT_GATT_CLIENT) + + {"pts_ble_discover_uuid_128", "\r\npts_ble_discover_uuid_128:[Gatt discovery]\r\n\ + [Discovery type, 0:Primary, 1:Secondary, 2:Include, 3:Characteristic, 4:Descriptor]\r\n\ + [Uuid, 16 Octets, e.g.0000A00C000000000123456789abcdef]\r\n\ + [Start handle, 2 Octets, e.g.0001]\r\n\ + [End handle, 2 Octets, e.g.ffff]\r",pts_ble_discover_uuid_128}, + + {"pts_ble_read_uuid_128", "\r\npts_ble_read_uuid_128:[Gatt Read by uuid 128 bit]\r\n\ + [Uuid, 16 Octets]\r\n\ + [Start_handle, 2 Octets]\r\n\ + [End_handle, 2 Octets]\r\n", pts_ble_read_uuid_128}, + + {"pts_ble_read_uuid", "\r\npts_ble_read_uuid:[Gatt Read by uuid]\r\n\ + [Uuid, 2 Octets]\r\n\ + [Start_handle, 2 Octets]\r\n\ + [End_handle, 2 Octets]\r\n", pts_ble_read_uuid}, + + {"pts_ble_mread", "\r\npts_ble_mread:[Gatt Read multiple]\r\n ...\r\n",pts_ble_mread}, + + {"pts_ble_prepare_write", "\r\npts_ble_prepare_write:[Gatt prepare write]\r\n\ + [Attribute handle, 2 Octets]\r\n\ + [Value offset, 2 Octets]\r\n\ + [Value length, 2 Octets]\r\n\ + [Value data]\r\n", pts_ble_prepare_write}, +#endif /*CONFIG_BT_GATT_CLIENT*/ +}; + +#if defined(CONFIG_BT_PERIPHERAL) +#if defined(PTS_GAP_SLAVER_CONFIG_READ_CHARC) +static u8_t report[]= { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09 +}; + +static ssize_t read_pts_long_value(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + const char *lvalue = "PTS-LONG-VALUE0123456789abcdef1122"; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, lvalue, + strlen(lvalue)); +} + +static ssize_t read_pts_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + const char *name = "PTS_NAME"; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, name,strlen(name)); +} + +static ssize_t pts_read_report( + struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, + attr->user_data, + sizeof(report)/sizeof(u8_t)); +} + +#endif /* PTS_GAP_SLAVER_CONFIG_READ_CHARC */ + +#if defined(PTS_GAP_SLAVER_CONFIG_WRITE_CHARC) +#define TEST_LVAL_MAX 30 +#define TEST_SVAL_MAX 4 + +static u16_t test_len = 0; +//static u16_t test_slen = 0; + +static u8_t short_buf[TEST_SVAL_MAX]= +{ + 0x01,0x02,0x03,0x04 +}; +static u8_t long_buf[TEST_LVAL_MAX] = +{ + 0x01,0x02,0x03,0x04, + 0x05,0x06,0x07,0x08, + 0x09,0x0a,0x11,0x12, + 0x13,0x14,0x15,0x16, + 0x17,0x18,0x19,0x1a, + 0x21,0x22,0x23,0x24, + 0x25,0x26,0x27,0x28, + 0x29,0x2a +}; + +static ssize_t pts_write_short_value(struct bt_conn *conn,const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + u8_t i = 0; + u16_t tlen = len; + u8_t *data = (u8_t*)buf; + + for(i=0;i[0x%x]\r\n",i,data[i]); + + /*The rest of space is enough.*/ + if(TEST_SVAL_MAX >= tlen){ + (void)memcpy(short_buf,data,tlen); + } + else{ + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + /*If prepare write, it will return 0 */ + if(flags == BT_GATT_WRITE_FLAG_PREPARE) + tlen = 0; + + return tlen; +} + +static ssize_t pts_read_value(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + u8_t *data = short_buf; + u16_t data_len = sizeof(short_buf)/sizeof(u8_t); + + if(/*test_len8*/0) + { + data_len = test_len; + data = long_buf; + test_len = 0; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, data,data_len); +} + + +static ssize_t pts_read_long_value(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + u8_t *data = long_buf; + u16_t data_len = sizeof(long_buf)/sizeof(u8_t); + + /*Get new value */ + if(test_len > 0 && test_len <= TEST_LVAL_MAX) + { + data = long_buf; + data_len = test_len; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, data,data_len); +} + +static ssize_t pts_write_long_value( + struct bt_conn *conn, + const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + u16_t tlen = len; + u8_t *data = (u8_t*)buf; + u8_t i = 0; + + /*Reset test value */ + if(!offset) + { + test_len = 0; + (void)memset(long_buf,0,TEST_LVAL_MAX); + } + else if(offset > TEST_LVAL_MAX) + { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + /*The rest of space is enough.*/ + if(TEST_LVAL_MAX - test_len >= tlen) + { + (void)memcpy(&long_buf[test_len],data,tlen); + test_len += tlen; + } + else + { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + /*If prepare write, it will return 0 */ + if(flags == BT_GATT_WRITE_FLAG_PREPARE) + tlen = 0; + + return tlen; +} + +#endif /*PTS_GAP_SLAVER_CONFIG_WRITE_CHARC*/ + +#if defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) || defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +static bool notify_status = 0; +static u8_t battery_level = 100; + +static void notify(const struct bt_gatt_attr *attr, u16_t value) +{ + ARG_UNUSED(attr); + + notify_status = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t pts_read_value( + struct bt_conn *conn, + const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + const char *data = "PTS_NAME"; + u16_t length = strlen(data); + + return bt_gatt_attr_read(conn, bt_attr, buf, len, offset, data,length); +} + +static ssize_t pts_write_value( + struct bt_conn *conn, + const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + return len; +} + +#endif /*PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC*/ + + +static ssize_t pts_read_chara_value( + struct bt_conn *conn, + const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + const char *data = "PTS_NAME0123456789abcde"; + u16_t length = strlen(data); + + return bt_gatt_attr_read(conn, bt_attr, buf, len, offset, data,length); +} +#if defined(PTS_CHARC_LEN_EQUAL_MTU_SIZE) +static ssize_t pts_read_mtu_size( + struct bt_conn *conn, + const struct bt_gatt_attr *bt_attr, + void *buf,u16_t len, u16_t offset,u8_t flags) +{ + const char *data = "876543210123456789abcde"; + u16_t length = strlen(data); + + return bt_gatt_attr_read(conn, bt_attr, buf, len, offset, data,length); +} +#endif +#if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) +static ssize_t pts_read_value_with_key(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset,u8_t flags) +{ + const struct gatt_value *value = attr->user_data; + + if ((attr->perm & (BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_READ_AUTHEN)) && + (value->enc_key_size > bt_conn_enc_key_size(conn))) { + return BT_GATT_ERR(BT_ATT_ERR_ENCRYPTION_KEY_SIZE); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value->data,value->len); +} + +static ssize_t pts_write_value_with_key(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset,u8_t flags) +{ + const struct gatt_value *value = attr->user_data; + + if ((attr->perm & (BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_WRITE_AUTHEN )) && + (value->enc_key_size > bt_conn_enc_key_size(conn))) { + return BT_GATT_ERR(BT_ATT_ERR_ENCRYPTION_KEY_SIZE); + } + + return len; +} + +#endif + +static struct bt_gatt_attr pts_attr[] = { + + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_AUTH_CHAR, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ_AUTHEN, + pts_read_chara_value, + NULL, + NULL + ), + + #if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_ENC_KEY, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_READ_ENCRYPT | + BT_GATT_PERM_WRITE | BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_PREPARE_WRITE, + pts_read_value_with_key, + pts_write_value_with_key, + &value + ), + #endif + + + #if defined(PTS_GAP_SLAVER_CONFIG_WRITE_CHARC) + + /* Case : GATT/SR/GAR/BV-03-C + * Verify that a Generic Attribute Profile server can support writing a Characteristic + * Value selected by handle + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_VALUE, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + pts_read_value, + pts_write_short_value, + NULL + ), + + + /* Case : GATT/SR/GAR/BV-01-C + * Verify that a Generic Attribute Profile server can support a write to a + * characteristic without response + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_NORSP, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + pts_read_value, + pts_write_short_value, + NULL + ), + + /* Case : GATT/SR/GAR/BI-03-C, GATT/SR/GAR/BI-12-C + * Verify that a Generic Attribute Profile server can detect and reject a + * Write Characteristic Value Request to a non-writeable Characteristic Value + * and issue a Write Not Permitted Response. + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_AUTHEN, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_AUTHEN, + pts_read_long_value, + pts_write_long_value, + NULL + ), + + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_AUTHEN, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_AUTHEN, + pts_read_value, + pts_write_long_value, + NULL + ), + + /* Case : GATT/SR/GAW/BV-05-C */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_LONGVAL, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + pts_read_long_value, + pts_write_long_value, + NULL + ), + + /* Case : GATT/SR/GAW/BV-10-C */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_WRITE_2LONGVAL, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + pts_read_long_value, + pts_write_long_value, + NULL + ), + + BT_GATT_DESCRIPTOR + ( + BT_UUID_PTS_CHAR_WRITE_L_DES, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + pts_read_long_value, + pts_write_long_value, + NULL + ), + +#endif + + #if defined(PTS_GAP_SLAVER_CONFIG_READ_CHARC) + + /* Case : GATT/SR/GAR/BI-04-C + * Using authenticated link-key for read access. handle is 15 + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_READ_AUTHEN, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ_AUTHEN, + read_pts_name, + NULL, + NULL + ), + + /* Case : GATT/SR/GAR/BI-06-C + * Verify that a Generic Attribute Profile server can detect and reject a Read + * Characteristic by UUID Request to a non-readable Characteristic Value and issue + * a Read Not Permitted Response + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_READ_NOPERM, + BT_GATT_CHRC_READ, + BT_GATT_PERM_NONE, + read_pts_name, + NULL, + NULL + ), + + /* Case : GATT/SR/GAR/BV-04-C;GATT/SR/GAR/BI-13-C + * Verify that a Generic Attribute Profile server can support reading a + * long Characteristic Value selected by handle. + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_READ_LONGVAL, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + read_pts_long_value, + NULL, + NULL + ), + + /* Case : GATT/SR/GAR/BV-06-C;GATT/SR/GAR/BV-07-C;GATT/SR/GAR/BV-08-C + * Verify that a Generic Attribute Profile server can support reading a Long + * Characteristic Descriptor selected by handle. + */ + BT_GATT_DESCRIPTOR + ( + BT_UUID_PTS_CHAR_READ_LVAL_REF, + BT_GATT_PERM_READ, + pts_read_report, + NULL, + report + ), + + /* Case : GATT/SR/GAR/BI-12-C + * Verify that a Generic Attribute Profile server can detect and reject a Read Long + * Characteristic Value Request to a non-readable Characteristic Value and issue a + * Read Not Permitted Response. + */ + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_READ_L_NOPERM, + BT_GATT_CHRC_READ, + BT_GATT_PERM_NONE, + read_pts_long_value, + NULL, + NULL + ), + + #endif /* PTS_GAP_SLAVER_CONFIG_READ_CHARC */ + + #if defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_NOTIFY_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ , + pts_read_value, + NULL, + &battery_level + ), + + BT_GATT_CCC + ( + notify, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE + ), + #endif + + #if defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_CHAR_INDICATE_CHAR, + BT_GATT_CHRC_READ | BT_GATT_CHRC_INDICATE, + BT_GATT_PERM_READ , + pts_read_value, + NULL, + &battery_level + ), + + BT_GATT_CCC + ( + notify, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE + ), + #endif + #if defined(PTS_CHARC_LEN_EQUAL_MTU_SIZE) + BT_GATT_CHARACTERISTIC + ( + BT_UUID_PTS_READ_MTU_SIZE_CHAR, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + pts_read_mtu_size, + NULL, + NULL + ), + #endif + +}; + +struct bt_gatt_service pts_svc = BT_GATT_SERVICE(pts_attr); + +#if defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) +static void notify_cb(struct bt_conn *conn, void *user_data) +{ + vOutputString("%s: Nofication sent to conn %p\r\n",__func__,conn); +} + +static void pts_ble_notify(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_gatt_notify_params params; + u8_t data = 0; + u16_t len = 0; + int err; + + if(!default_conn){ + vOutputString("Not connected\r\n"); + return; + } + + get_uint16_from_string(&argv[1], &len); + get_bytearray_from_string(&argv[2], (u8_t *)&data,len); + + memset(¶ms, 0, sizeof(params)); + + params.uuid = pts_attr[1].uuid; + params.attr = &pts_attr[1]; + params.data = &data; + params.len = len; + params.func = notify_cb; + + vOutputString("len = [0x%x]\r\n",params.len); + + err = bt_gatt_notify_cb(default_conn, ¶ms); + if(err){ + vOutputString("Failed to notifition [%d]\r\n",err); + } +} +#endif /* PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC */ + +#if defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +static void indicate_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr, + u8_t err) +{ + if (err != 0U) { + vOutputString("Indication fail"); + } + else { + vOutputString("Indication success"); + } +} + +static void pts_ble_indicate(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_gatt_indicate_params params; + u8_t data[16]; + u16_t len = 0; + int err; + + if(!default_conn){ + vOutputString("Not connected\r\n"); + return; + } + + get_uint16_from_string(&argv[1], &len); + + if(len > 16){ + len = 16; + } + + get_bytearray_from_string(&argv[2], data,len); + + memset(¶ms, 0, sizeof(params)); + + params.attr = &pts_attr[1]; + params.data = data; + params.len = len; + params.func = indicate_cb; + + vOutputString("len = [0x%x] handle(0x%x)\r\n",params.len,params.attr->handle); + + err = bt_gatt_indicate(default_conn, ¶ms); + if(err){ + vOutputString("Failed to indicate [%d]\r\n",err); + } +} +#endif /* PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC */ +#endif /* CONFIG_BT_PERIPHERAL */ + +#if defined(CONFIG_BT_OBSERVER) +static bool data_cb(struct bt_data *data, void *user_data) +{ + char *name = user_data; + u8_t len; + + switch (data->type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + len = (data->data_len > NAME_LEN - 1)?(NAME_LEN - 1):(data->data_len); + memcpy(name, data->data, len); + return false; + default: + return true; + } +} + +static bool ad_flag_data_cb(struct bt_data *data, void *user_data) +{ + char *ad_flag = user_data; + + switch(data->type){ + case BT_DATA_FLAGS: + memcpy(ad_flag, data->data, data->data_len); + return false; + + default: + return true; + } +} + +static char *pts_cmplt_name = "PTS-GAP-224B"; +static char *pts_short_name = "PTS-GAP"; +extern bt_addr_le_t pts_addr; + +static void pts_device_found(const bt_addr_le_t *addr, s8_t rssi, u8_t evtype, + struct net_buf_simple *buf) +{ + char le_addr[BT_ADDR_LE_STR_LEN]; + char name[NAME_LEN]; + u8_t dst_address[6]; + char ad_flag[1]; + struct net_buf_simple abuf; + + (void)memset(ad_flag, 0, sizeof(ad_flag)); + (void)memset(dst_address, 0, sizeof(dst_address)); + memcpy(&abuf,buf,sizeof(struct net_buf_simple)); + + (void)memset(name, 0, sizeof(name)); + bt_data_parse(buf, data_cb, name); + bt_addr_le_to_str(addr, le_addr, sizeof(le_addr)); + + if(!memcmp(&pts_addr, addr, sizeof(bt_addr_le_t)) || + !memcmp(name,pts_cmplt_name, sizeof(*pts_cmplt_name)) || + !memcmp(name,pts_short_name, sizeof(*pts_short_name))){ + + if(memcmp(&pts_addr, addr, sizeof(bt_addr_le_t))) + memcpy(&pts_addr, addr, sizeof(pts_addr)); + + vOutputString("[DEVICE]: %s, AD evt type %u, RSSI %i %s \r\n",le_addr, evtype, rssi, name); + + bt_data_parse(&abuf,ad_flag_data_cb,ad_flag); + + if(*ad_flag & 0x01){ + vOutputString("Advertising data : 'Limited Discovered Mode' flag is set one\r\n"); + }else + vOutputString("Advertising data : 'Limited Discovered Mode' flag is not set\r\n"); + + if(*ad_flag & 0x02){ + vOutputString("Advertising data : 'General Discovered Mode' flag is set one\r\n"); + }else + vOutputString("Advertising data : 'General Discovered Mode' flag is not set\r\n"); + } +} + +static void pts_ble_start_scan(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_scan_param scan_param; + int err; + + (void)err; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], &scan_param.type); + get_uint8_from_string(&argv[2], &scan_param.filter_dup); + + get_uint16_from_string(&argv[3], &scan_param.interval); + get_uint16_from_string(&argv[4], &scan_param.window); + + err = bt_le_scan_start(&scan_param, pts_device_found); + + if(err){ + vOutputString("Failed to start scan (err %d) \r\n", err); + }else{ + vOutputString("Start scan successfully \r\n"); + } +} + +static void pts_ble_sc_indicate(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_gatt_indicate_params params; + u8_t data[16]; + u16_t len = 0; + int err; + + if(!default_conn){ + vOutputString("Not connected\r\n"); + return; + } + + memset(¶ms, 0, sizeof(params)); + params.func = NULL; + err = service_change_test(¶ms,default_conn); + if(err){ + vOutputString("Failed to sc indicate\r\n"); + return; + } + +} + + +static void pts_ble_start_scan_rpa(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_scan_param scan_param; + int err; + (void)err; + u8_t is_rpa; + + if(argc != 6){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + get_uint8_from_string(&argv[1], &scan_param.type); + get_uint8_from_string(&argv[2], &scan_param.filter_dup); + get_uint16_from_string(&argv[3], &scan_param.interval); + get_uint16_from_string(&argv[4], &scan_param.window); + get_uint8_from_string(&argv[5], (uint8_t *)&is_rpa); + + err = bt_le_pts_scan_start(&scan_param, pts_device_found, is_rpa); + if(err){ + vOutputString("Failed to start scan (err %d) \r\n", err); + }else{ + vOutputString("Start scan successfully \r\n"); + } +} + +static void pts_ble_add_peer_irk(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t peer_key[16]; + + memset(peer_key,0,16); + + get_bytearray_from_string(&argv[1], peer_key,16); + reverse_bytearray(peer_key, peer_irk, 16); + vOutputString("peer_irk=[%s]\r\n",bt_hex(peer_irk, 16)); +} + +static void pts_ble_add_dev_to_resolve_list(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_keys key; + bt_addr_le_t addr; + u8_t type = 0; + u8_t val[6]; + + memset(&key,0,sizeof(struct bt_keys)); + + get_uint8_from_string(&argv[1], &type); + get_bytearray_from_string(&argv[2], val/*(uint8_t *)addr.a.val*/,6); + reverse_bytearray(val, addr.a.val, 6); + if(type == 0) + addr.type = BT_ADDR_LE_PUBLIC; + else if(type == 1) + addr.type = BT_ADDR_LE_RANDOM; + + memcpy(&key.addr,&addr,sizeof(bt_addr_le_t)); + memcpy(key.irk.val,peer_irk,16); + + bt_id_add(&key); + +} + + +#if defined(CONFIG_BT_PERIPHERAL) +static void pts_ble_register_pts_svc(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + + err = bt_gatt_service_register(&pts_svc); + if(err){ + vOutputString("Failed to register PTS service\r\n"); + }else{ + vOutputString("Succeed to register PTS service\r\n"); + } +} + +#if defined(PTS_TEST_CASE_INSUFFICIENT_KEY) +static int set_attr_enc_key_size(const struct bt_gatt_attr *attr, + u8_t key_size) +{ + struct gatt_value *value; + + /* Fail if requested attribute is a service */ + /*if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) || + !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY) || + !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_INCLUDE)) { + return -1; + }*/ + + /* Fail if permissions are not set */ + /*if (!(attr->perm & ((BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_READ_AUTHEN) | + (BT_GATT_PERM_WRITE_ENCRYPT | BT_GATT_PERM_WRITE_AUTHEN)))) { + + return -1; + }*/ + + vOutputString("perm:0x%x\r\n",attr->perm); + vOutputString("handle:0x%04x\r\n",attr->handle); + value = attr->user_data; + value->enc_key_size = key_size; + + return 0; +} + + +static void pts_set_enc_key_size(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t key_size; + u8_t index; + int err; + + get_uint8_from_string(&argv[1], &key_size); + get_uint8_from_string(&argv[2], &index); + + if(key_size < 0x07 || key_size > 0x0f) + vOutputString("Invalid key size(%d)\r\n",key_size); + else{ + err = set_attr_enc_key_size(&pts_attr[index],key_size); + if(err){ + vOutputString("Failed to set attr enc key size(%d)\r\n",err); + } + } +} +#endif //PTS_TEST_CASE_INSUFFICIENT_KEY +#endif +#if defined(CONFIG_BT_WHITELIST) +static void pts_ble_wl_connect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + unsigned char enable = 0U; + struct bt_le_conn_param param = { + .interval_min = BT_GAP_INIT_CONN_INT_MIN, + .interval_max = BT_GAP_INIT_CONN_INT_MAX, + .latency = 0, + .timeout = 400, + }; + /*Auto connect whitelist device, enable : 0x01, disable : 0x02*/ + get_uint8_from_string(&argv[1], &enable); + /*Address type, 0:ADDR_PUBLIC, 1:ADDR_RAND, 2:ADDR_RPA_OR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + get_uint8_from_string(&argv[2], ¶m.own_address_type); + + if(enable == 0x01){ + err = bt_conn_create_auto_le(¶m); + if(err){ + vOutputString("Auto connect failed (err = [%d])\r\n",err); + return; + }else{ + vOutputString("Auto connection is succeed\r\n"); + } + }else if(enable == 0x02){ + err = bt_conn_create_auto_stop(); + if(err){ + vOutputString("Auto connection stop (err = [%d])\r\n",err); + return ; + } + } +} + +static void pts_ble_bt_le_whitelist_add(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t waddr; + int err; + u8_t val[6]; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct (argc = [%d])\r\n",argc); + return; + } + + err = bt_le_whitelist_clear(); + if(err){ + vOutputString("Clear white list device failed (err = [%d])\r\n",err); + } + + get_uint8_from_string(&argv[1], &waddr.type); + get_bytearray_from_string(&argv[2], val,6); + + reverse_bytearray(val, waddr.a.val, 6); + + err = bt_le_whitelist_add(&waddr); + if(err){ + vOutputString("Failed to add device to whitelist (err = [%d])\r\n",err); + } +} + +#endif +static void pts_ble_start_scan_timeout(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_scan_param scan_param; + uint16_t time = 0; + int err; + u8_t addre_type; + + get_uint8_from_string(&argv[1], &scan_param.type); + get_uint8_from_string(&argv[2], &scan_param.filter_dup); + get_uint16_from_string(&argv[3], &scan_param.interval); + get_uint16_from_string(&argv[4], &scan_param.window); + get_uint8_from_string(&argv[5], (uint8_t *)&addre_type); + get_uint16_from_string(&argv[6], (uint16_t *)&time); + + err = bt_le_pts_scan_start(&scan_param, pts_device_found, addre_type); + if(err){ + vOutputString("Failed to start scan (err %d) \r\n", err); + }else{ + vOutputString("Start scan successfully \r\n"); + } + + k_sleep(time); + bt_le_scan_stop(); + vOutputString("Scan stop \r\n"); +} + +static void pts_ble_address_register(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + u8_t type; + char le_addr[BT_ADDR_LE_STR_LEN]; + + get_uint8_from_string(&argv[1], &type); + get_bytearray_from_string(&argv[2], addr.a.val,6); + + if(type == 0){ + addr.type = BT_ADDR_LE_PUBLIC; + }else if(type == 1) + addr.type = BT_ADDR_LE_RANDOM; + + memcpy(&pts_addr,&addr,sizeof(bt_addr_le_t)); + + bt_addr_le_to_str(&pts_addr, le_addr, sizeof(le_addr)); + + memcpy(pts_address,pts_addr.a.val,6); + + vOutputString("Pts address %s \r\n",le_addr); +} + + +static void pts_ble_set_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t flag; + get_uint8_from_string(&argv[1],&flag); + event_flag = flag; + + vOutputString("Event flag = [0x%x] \r\n",event_flag); +} + +static void pts_ble_set_smp_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t flag; + + get_uint8_from_string(&argv[1],&flag); + bt_set_smpflag((smp_test_id)flag); + + vOutputString("Smp flag = [0x%x] \r\n",flag); +} + +static void pts_ble_clear_smp_flag(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t flag; + + get_uint8_from_string(&argv[1],&flag); + bt_clear_smpflag((smp_test_id)flag); + + vOutputString("Clear smp flag \r\n"); +} + +static void pts_ble_bondable(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t bondable; + + get_uint8_from_string(&argv[1], &bondable); + + if(bondable == 0x01) + bt_set_bondable(true); + else if(bondable == 0x00) + bt_set_bondable(false); + else + vOutputString("Bondable status is unknow \r\n"); + +} +#endif //#if defined(CONFIG_BT_OBSERVER) + + +#if defined(CONFIG_BT_PERIPHERAL) +static void pts_ble_start_advertise(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_adv_param param; + const struct bt_data *ad; + size_t ad_len=0; + int err = 0; + uint8_t adv_type, tmp; + struct bt_data pts_ad ; + bt_addr_le_t pts; + struct bt_conn *conn; + u8_t adder_type = 0; + u8_t is_ad = 0; + + memset(pts.a.val,0,6); + memset(&pts_ad,0,sizeof(struct bt_data)); + + if(argc != 4 && argc != 6){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + switch(event_flag){ + case ad_type_service_uuid: + pts_ad.type = BT_DATA_UUID128_ALL; + pts_ad.data = service_uuid; + pts_ad.data_len = sizeof(service_uuid); + is_ad = 1; + break; + + case ad_type_local_name: + pts_ad.type = BT_DATA_NAME_COMPLETE; + pts_ad.data = name; + pts_ad.data_len = 13; + is_ad = 1; + break; + + case ad_type_flags: + pts_ad.type = BT_DATA_FLAGS; + pts_ad.data = discover_mode; + pts_ad.data_len = sizeof(discover_mode); + is_ad = 1; + break; + + case ad_type_manu_data: + pts_ad.type = BT_DATA_MANUFACTURER_DATA; + pts_ad.data = data_manu; + pts_ad.data_len = sizeof(data_manu); + is_ad = 1; + break; + + case ad_type_tx_power_level: + pts_ad.type = BT_DATA_TX_POWER; + pts_ad.data = tx_power; + pts_ad.data_len = sizeof(tx_power); + is_ad = 1; + break; + + case ad_type_service_data: + pts_ad.type = BT_DATA_SVC_DATA16; + pts_ad.data = service_data; + pts_ad.data_len = sizeof(service_data); + is_ad = 1; + break; + + case ad_type_appearance: + pts_ad.type = BT_DATA_GAP_APPEARANCE; + pts_ad.data = data_appearance; + pts_ad.data_len = sizeof(data_appearance); + is_ad = 1; + break; + + default: + break; + } + + param.id = selected_id; + param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; + param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; + + /*Get adv type, 0:adv_ind, 1:adv_scan_ind, 2:adv_nonconn_ind 3: adv_direct_ind*/ + get_uint8_from_string(&argv[1], &adv_type); + + if(adv_type == 0){ + param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME); + }else if(adv_type == 1){ + param.options = BT_LE_ADV_OPT_USE_NAME; + }else if(adv_type == 2){ + param.options = 0; + }else if(adv_type == 3){ + param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME); + }else if(adv_type == 4){ + param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY | BT_LE_ADV_OPT_ONE_TIME); + } + else{ + vOutputString("Arg1 is invalid\r\n"); + return; + } + + ad = ad_discov; + ad_len = ARRAY_SIZE(ad_discov); + + /*Get mode, 0:General discoverable, 1:non discoverable, 2:limit discoverable*/ + get_uint8_from_string(&argv[2], &tmp); + + if(tmp == 0 || tmp == 1 || tmp == 2){ + if(tmp == 1){ + //ad_discov[0].data = 0; + ad = ad_non_discov; + ad_len = 1; + } + else if(tmp == 2){ + ad_discov[0].data = &discover_mode[0]; + ad = ad_discov; + ad_len = ARRAY_SIZE(ad_discov); + } + if(is_ad == 1){ + ad = &pts_ad; + ad_len = 1; + }/*else{ + ad = ad_discov; + ad_len = ARRAY_SIZE(ad_discov); + }*/ + + }else{ + vOutputString("Arg2 is invalid\r\n"); + return; + } + + /*upper address type, 0:non-resolvable private address,1:resolvable private address,2:public address*/ + //get_bytearray_from_string(&argv[3], (u8_t *)¶m.addr_type, 1); + get_uint8_from_string(&argv[3], (u8_t *)&adder_type); + + if(adder_type == 0) + param.addr_type = BT_ADDR_TYPE_NON_RPA; + else if(adder_type == 1) + param.addr_type = BT_ADDR_TYPE_RPA; + else if(adder_type == 2) + param.addr_type = BT_ADDR_LE_PUBLIC; + else + vOutputString("Invaild address type\r\n"); + + if(argc == 6){ + get_uint16_from_string(&argv[4], ¶m.interval_min); + get_uint16_from_string(&argv[5], ¶m.interval_max); + } + + if(adv_type == 3){ + param.interval_min = 0; + param.interval_max = 0; + } + + if(adv_type == 1){ + err = bt_le_adv_start(¶m, ad, ad_len, &ad_discov[0], 1); + }else if(adv_type == 3 || adv_type == 4){ + pts.type = BT_ADDR_LE_PUBLIC; + memcpy(pts.a.val,pts_address,6); + conn = bt_conn_create_slave_le(&pts,¶m); + if(!conn){ + err = 1; + } + else{ + bt_conn_unref(conn); + } + + }else{ + err = bt_le_adv_start(¶m, ad, ad_len, NULL, 0); + } + + if(err){ + vOutputString("Failed to start advertising\r\n"); + }else{ + vOutputString("Advertising started\r\n"); + } + + if(tmp == 2){ + k_sleep(LIM_ADV_TIME); + bt_le_adv_stop(); + vOutputString("Adv timeout \r\n"); + } + +} + +#endif //#if defined(CONFIG_BT_PERIPHERAL) + +#if defined(CONFIG_BT_CONN) +#if defined(CONFIG_BT_CENTRAL) +static void pts_ble_connect_le(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + struct bt_conn *conn; + u8_t addr_val[6]; + s8_t type = -1; + + struct bt_le_conn_param param = { + .interval_min = BT_GAP_INIT_CONN_INT_MIN, + .interval_max = BT_GAP_INIT_CONN_INT_MAX, + .latency = 0, + .timeout = 400, + .own_address_type = BT_ADDR_LE_PUBLIC_ID, + + }; + + if(argc != 4){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + if(event_flag == own_addr_type_random){ + param.own_address_type = BT_ADDR_LE_RANDOM_ID; + } + + get_uint8_from_string(&argv[1], (u8_t *)&type); + + /*Get addr type,0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + if(type == 0) + addr.type = 1; /*ADDR_RAND*/ + else if(type == 1) + addr.type = 2; /*ADDR_RPA_OR_PUBLIC*/ + else if(type == 2) + addr.type = 0; /*ADDR_PUBLIC*/ + else if(type == 3) + addr.type = 3; /*ADDR_RPA_OR_RAND*/ + else + vOutputString("adderss type is unknow [0x%x]\r\n",type); + + get_bytearray_from_string(&argv[2], addr_val,6); + + reverse_bytearray(addr_val, addr.a.val, 6); + + get_uint8_from_string(&argv[3], (u8_t *)&type); + + if(type == 0) + param.own_address_type = 1; /*ADDR_RAND*/ + else if(type == 1) + param.own_address_type = 2; /*ADDR_RPA_OR_PUBLIC*/ + else if(type == 2) + param.own_address_type = 0; /*ADDR_PUBLIC*/ + else if(type == 3) + param.own_address_type = 3; /*ADDR_RPA_OR_RAND*/ + else + vOutputString("adderss type is unknow [0x%x]\r\n",type); + + conn = bt_conn_create_le(&addr, /*BT_LE_CONN_PARAM_DEFAULT*/¶m); + + if(!conn){ + vOutputString("Connection failed\r\n"); + }else{ + vOutputString("Connection pending\r\n"); + } +} + +#endif //#if defined(CONFIG_BT_CENTRAL) + +static void pts_ble_disconnect(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + bt_addr_le_t addr; + u8_t addr_val[6]; + struct bt_conn *conn; + s8_t type = -1; + + if(argc != 3){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + get_uint8_from_string(&argv[1], (u8_t *)&type); + get_bytearray_from_string(&argv[2], addr_val,6); + reverse_bytearray(addr_val, addr.a.val, 6); + + /*Get addr type,0:ADDR_RAND, 1:ADDR_RPA_OR_PUBLIC,2:ADDR_PUBLIC, 3:ADDR_RPA_OR_RAND*/ + if(type == 0) + addr.type = 1; /*ADDR_RAND*/ + else if(type == 1) + addr.type = 2; /*ADDR_RPA_OR_PUBLIC*/ + else if(type == 2) + addr.type = 0; /*ADDR_PUBLIC*/ + else if(type == 3) + addr.type = 3; /*ADDR_RPA_OR_RAND*/ + else + vOutputString("adderss type is unknow\r\n"); + + conn = bt_conn_lookup_addr_le(selected_id, &addr); + + if(!conn){ + vOutputString("Not connected\r\n"); + return; + } + + if(bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)){ + vOutputString("Disconnection failed\r\n"); + }else{ + vOutputString("Disconnect successfully\r\n"); + } + bt_conn_unref(conn); +} + +static void pts_ble_conn_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + struct bt_le_conn_param param; + int err; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + if(default_conn == NULL){ + vOutputString("Connection is NULL\r\n"); + return; + } + memset(¶m,0,sizeof(struct bt_le_conn_param)); + get_uint16_from_string(&argv[1], ¶m.interval_min); + get_uint16_from_string(&argv[2], ¶m.interval_max); + get_uint16_from_string(&argv[3], ¶m.latency); + get_uint16_from_string(&argv[4], ¶m.timeout); + + if(event_flag == dir_connect_req) + err = bt_conn_le_param_update(default_conn, ¶m); + else + err = pts_bt_conn_le_param_update(default_conn, ¶m); + + if (err) { + vOutputString("conn update failed (err %d)\r\n", err); + } else { + vOutputString("conn update initiated\r\n"); + } +} +#endif //#if defined(CONFIG_BT_CONN) + +#if defined(CONFIG_BT_SMP) +static void pts_ble_set_mitm(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + u8_t enable = 0; + + get_uint8_from_string(&argv[1],&enable); + + if(enable == 0x01) + bt_set_mitm(true); + else if(enable == 0x00) + bt_set_mitm(false); + else + vOutputString("Inviad parameter\r\n"); +} + +#endif //#if defined(CONFIG_BT_SMP) + +#if defined(CONFIG_BT_GATT_CLIENT) +static struct bt_gatt_discover_params discover_params; +static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0); +static struct bt_uuid_128 uuid_128 = BT_UUID_INIT_128(0); + +extern u8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params); +static void pts_ble_discover_uuid_128(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + u8_t disc_type; + u8_t val[16]; + + (void)memset(val,0x0,16); + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + discover_params.func = discover_func; + discover_params.start_handle = 0x0001; + discover_params.end_handle = 0xffff; + + get_uint8_from_string(&argv[1], &disc_type); + if(disc_type == 0){ + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + }else if(disc_type == 1){ + discover_params.type = BT_GATT_DISCOVER_SECONDARY; + }else if(disc_type == 2){ + discover_params.type = BT_GATT_DISCOVER_INCLUDE; + }else if(disc_type == 3){ + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + }else if(disc_type == 4){ + discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + }else{ + vOutputString("Invalid discovery type\r\n"); + return; + } + get_bytearray_from_string(&argv[2], val,16); + + reverse_bytearray(val, uuid_128.val,16); + + /*Set array value to 0 */ + (void)memset(val, 0x0, 16); + + if(!memcmp(uuid_128.val, val, 16)) + discover_params.uuid = NULL; + else + discover_params.uuid = &uuid_128.uuid; + + get_uint16_from_string(&argv[3], &discover_params.start_handle); + get_uint16_from_string(&argv[4], &discover_params.end_handle); + + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + vOutputString("Discover failed (err %d)\r\n", err); + } else { + vOutputString("Discover pending\r\n"); + } +} + + +static struct bt_gatt_read_params read_params; + +static u8_t read_func(struct bt_conn *conn, u8_t err, struct bt_gatt_read_params *params, const void *data, u16_t length) +{ + vOutputString("Read complete: err %u length %u \r\n", err, length); + + char str[22]; + u8_t *buf = (u8_t *)data; + + memset(str,0,15); + + if(length > 0 && length <= sizeof(str)){ + memcpy(str,buf,length); + vOutputString("device name : %s \r\n",str); + + u8_t i = 0; + for(i=0;i ARRAY_SIZE(h)){ + vOutputString("Enter max %lu handle items to read\r\n",ARRAY_SIZE(h)); + return; + } + + for (i = 0; i < argc - 1; i++) { + get_uint16_from_string(&argv[i + 1],&h[i]); + } + + read_params.func = read_func; + read_params.handle_count = i; + read_params.handles = h; /* not used in read func */ + + vOutputString("i = [%d]\r\n",i); + + err = bt_gatt_read(default_conn, &read_params); + if (err) { + vOutputString("Read failed (err %d)\r\n", err); + } else { + vOutputString("Read pending\r\n"); + } +} + + +static struct bt_gatt_write_params write_params; +static u8_t gatt_write_buf[CHAR_SIZE_MAX]; + +static void write_func(struct bt_conn *conn, u8_t err, + struct bt_gatt_write_params *params) +{ + vOutputString("Write complete: err %u \r\n", err); + + (void)memset(&write_params, 0, sizeof(write_params)); +} + +static void pts_ble_prepare_write(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + uint16_t data_len; + + if(argc != 5){ + vOutputString("Number of Parameters is not correct\r\n"); + return; + } + + if (!default_conn) { + vOutputString("Not connected\r\n"); + return; + } + + if (write_params.func) { + vOutputString("Write ongoing\r\n"); + return; + } + + get_uint16_from_string(&argv[1], &write_params.handle); + get_uint16_from_string(&argv[2], &write_params.offset); + get_uint16_from_string(&argv[3], &write_params.length); + data_len = write_params.length > sizeof(gatt_write_buf)? (sizeof(gatt_write_buf)):(write_params.length); + get_bytearray_from_string(&argv[4], gatt_write_buf,data_len); + + write_params.data = gatt_write_buf; + write_params.length = data_len; + write_params.func = write_func; + + err = bt_gatt_prepare_write(default_conn, &write_params); + + if (err) { + vOutputString("Prepare write failed (err %d)\r\n", err); + } else { + vOutputString("Prepare write pending\r\n"); + } + +} + +//static struct bt_gatt_subscribe_params subscribe_params; + +#endif /* CONFIG_BT_GATT_CLIENT */ + + +int pts_cli_register(void) +{ + //memcpy(&pts_addr.a, BT_ADDR_NONE, sizeof(pts_addr.a)); + + // static command(s) do NOT need to call aos_cli_register_command(s) to register. + // However, calling aos_cli_register_command(s) here is OK but is of no effect as cmds_user are included in cmds list. + // XXX NOTE: Calling this *empty* function is necessary to make cmds_user in this file to be kept in the final link. + //aos_cli_register_commands(btStackCmdSet, sizeof(btStackCmdSet)/sizeof(btStackCmdSet[0])); + return 0; +} + +#endif diff --git a/components/ble/ble_stack/common/atomic_c.c b/components/ble/ble_stack/common/atomic_c.c new file mode 100644 index 00000000..591e27b9 --- /dev/null +++ b/components/ble/ble_stack/common/atomic_c.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2011-2014 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file Atomic ops in pure C + * + * This module provides the atomic operators for processors + * which do not support native atomic operations. + * + * The atomic operations are guaranteed to be atomic with respect + * to interrupt service routines, and to operations performed by peer + * processors. + * + * (originally from x86's atomic.c) + */ + +#include +#include "bl_port.h" +//#include +//#include + +/** + * + * @brief Atomic compare-and-set primitive + * + * This routine provides the compare-and-set operator. If the original value at + * equals , then is stored at and the + * function returns 1. + * + * If the original value at does not equal , then the store + * is not done and the function returns 0. + * + * The reading of the original value at , the comparison, + * and the write of the new value (if it occurs) all happen atomically with + * respect to both interrupts and accesses of other processors to . + * + * @param target address to be tested + * @param old_value value to compare against + * @param new_value value to compare against + * @return Returns 1 if is written, 0 otherwise. + */ +int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + unsigned int key; + int ret = 0; + + key = irq_lock(); + + if (*target == old_value) { + *target = new_value; + ret = 1; + } + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic addition primitive + * + * This routine provides the atomic addition operator. The is + * atomically added to the value at , placing the result at , + * and the old value from is returned. + * + * @param target memory location to add to + * @param value the value to add + * + * @return The previous value from + */ +atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target += value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic subtraction primitive + * + * This routine provides the atomic subtraction operator. The is + * atomically subtracted from the value at , placing the result at + * , and the old value from is returned. + * + * @param target the memory location to subtract from + * @param value the value to subtract + * + * @return The previous value from + */ +atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target -= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic increment primitive + * + * @param target memory location to increment + * + * This routine provides the atomic increment operator. The value at + * is atomically incremented by 1, and the old value from is returned. + * + * @return The value from before the increment + */ +atomic_val_t atomic_inc(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + (*target)++; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic decrement primitive + * + * @param target memory location to decrement + * + * This routine provides the atomic decrement operator. The value at + * is atomically decremented by 1, and the old value from is returned. + * + * @return The value from prior to the decrement + */ +atomic_val_t atomic_dec(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + (*target)--; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic get primitive + * + * @param target memory location to read from + * + * This routine provides the atomic get primitive to atomically read + * a value from . It simply does an ordinary load. Note that + * is expected to be aligned to a 4-byte boundary. + * + * @return The value read from + */ +atomic_val_t atomic_get(const atomic_t *target) +{ + return *target; +} + +/** + * + * @brief Atomic get-and-set primitive + * + * This routine provides the atomic set operator. The is atomically + * written at and the previous value at is returned. + * + * @param target the memory location to write to + * @param value the value to write + * + * @return The previous value from + */ +atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic clear primitive + * + * This routine provides the atomic clear operator. The value of 0 is atomically + * written at and the previous value at is returned. (Hence, + * atomic_clear(pAtomicVar) is equivalent to atomic_set(pAtomicVar, 0).) + * + * @param target the memory location to write + * + * @return The previous value from + */ +atomic_val_t atomic_clear(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = 0; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise inclusive OR primitive + * + * This routine provides the atomic bitwise inclusive OR operator. The + * is atomically bitwise OR'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to OR + * + * @return The previous value from + */ +atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target |= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR) primitive + * + * This routine provides the atomic bitwise exclusive OR operator. The + * is atomically bitwise XOR'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to XOR + * + * @return The previous value from + */ +atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target ^= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise AND primitive + * + * This routine provides the atomic bitwise AND operator. The is + * atomically bitwise AND'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to AND + * + * @return The previous value from + */ +atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target &= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise NAND primitive + * + * This routine provides the atomic bitwise NAND operator. The is + * atomically bitwise NAND'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to NAND + * + * @return The previous value from + */ +atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = ~(*target & value); + + irq_unlock(key); + + return ret; +} diff --git a/components/ble/ble_stack/common/buf.c b/components/ble/ble_stack/common/buf.c new file mode 100644 index 00000000..1af5af7c --- /dev/null +++ b/components/ble/ble_stack/common/buf.c @@ -0,0 +1,1133 @@ +/* buf.c - Buffer management */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_MODULE_NAME net_buf + +#if !defined(BFLB_BLE) +#define LOG_LEVEL CONFIG_NET_BUF_LOG_LEVEL +#endif + +#include +//LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include +#include +#include +#include +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +#include "bl_port.h" +#endif + +#if defined(CONFIG_NET_BUF_LOG) +#define NET_BUF_DBG(fmt, ...) LOG_DBG("(%p) " fmt, k_current_get(), \ + ##__VA_ARGS__) +#define NET_BUF_ERR(fmt, ...) LOG_ERR(fmt, ##__VA_ARGS__) +#define NET_BUF_WARN(fmt, ...) LOG_WRN(fmt, ##__VA_ARGS__) +#define NET_BUF_INFO(fmt, ...) LOG_INF(fmt, ##__VA_ARGS__) +#define NET_BUF_ASSERT(cond) do { if (!(cond)) { \ + NET_BUF_ERR("assert: '" #cond "' failed"); \ + } } while (0) +#else + +#define NET_BUF_DBG(fmt, ...) +#define NET_BUF_ERR(fmt, ...) +#define NET_BUF_WARN(fmt, ...) +#define NET_BUF_INFO(fmt, ...) +#define NET_BUF_ASSERT(cond) +#endif /* CONFIG_NET_BUF_LOG */ + +#if defined(CONFIG_NET_BUF_WARN_ALLOC_INTERVAL) && ( CONFIG_NET_BUF_WARN_ALLOC_INTERVAL > 0) +//#if CONFIG_NET_BUF_WARN_ALLOC_INTERVAL > 0 +#define WARN_ALLOC_INTERVAL K_SECONDS(CONFIG_NET_BUF_WARN_ALLOC_INTERVAL) +#else +#define WARN_ALLOC_INTERVAL K_FOREVER +#endif + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +extern struct net_buf_pool hci_cmd_pool; +extern struct net_buf_pool hci_rx_pool; +#if defined(CONFIG_BT_CONN) +extern struct net_buf_pool acl_tx_pool; +extern struct net_buf_pool num_complete_pool; +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 +extern struct net_buf_pool prep_pool; +#endif +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +extern struct net_buf_pool acl_in_pool; +#endif +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 +extern struct net_buf_pool frag_pool; +#endif +#endif //CONFIG_BT_CONN +#if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) +extern struct net_buf_pool discardable_pool; +#endif +#ifdef CONFIG_BT_MESH +extern struct net_buf_pool adv_buf_pool; +extern struct net_buf_pool loopback_buf_pool; +#if defined(CONFIG_BT_MESH_FRIEND) +extern struct net_buf_pool friend_buf_pool; +#endif //CONFIG_BT_MESH_FRIEND +#endif +#if defined(CONFIG_BT_BREDR) +extern struct net_buf_pool br_sig_pool; +extern struct net_buf_pool sdp_pool; +extern struct net_buf_pool hf_pool; +extern struct net_buf_pool dummy_pool; +#endif + +#if defined(CONFIG_AUTO_PTS) +extern struct net_buf_pool server_pool; +extern struct net_buf_pool data_pool; +#endif + +struct net_buf_pool *_net_buf_pool_list[] = {&hci_cmd_pool, &hci_rx_pool, + + #if defined(CONFIG_BT_CONN) + &acl_tx_pool, + &num_complete_pool, + #if CONFIG_BT_ATT_PREPARE_COUNT > 0 + &prep_pool, + #endif + #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + &acl_in_pool, + #endif + #if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + &frag_pool, + #endif + #endif//defined(CONFIG_BT_CONN) + #if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) + discardable_pool, + #endif + #ifdef CONFIG_BT_MESH + &adv_buf_pool, + &loopback_buf_pool, + #if defined(CONFIG_BT_MESH_FRIEND) + &friend_buf_pool, + #endif + #endif + #if defined(CONFIG_BT_BREDR) + &sdp_pool, + &br_sig_pool, + &hf_pool, + &dummy_pool, + #endif + #if defined(CONFIG_AUTO_PTS) + &server_pool, + &data_pool, + #endif +}; + +#else //defined(BFLB_DYNAMIC_ALLOC_MEM) +extern struct net_buf_pool _net_buf_pool_list[]; +#endif //BFLB_BLE + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +void net_buf_init(struct net_buf_pool *buf_pool, u16_t buf_count, size_t data_size, destroy_cb_t destroy) +{ + struct net_buf_pool_fixed *buf_fixed; + buf_pool->alloc = (struct net_buf_data_alloc *)k_malloc(sizeof(void *)); + buf_pool->alloc->alloc_data = (struct net_buf_pool_fixed *)k_malloc(sizeof(void *)); + + buf_fixed = (struct net_buf_pool_fixed *)buf_pool->alloc->alloc_data; + + buf_pool->alloc->cb = &net_buf_fixed_cb; + buf_fixed->data_size = data_size; + buf_fixed->data_pool = (u8_t *)k_malloc(buf_count * data_size); + buf_pool->__bufs = (struct net_buf *)k_malloc(buf_count * sizeof(struct net_buf)); + buf_pool->buf_count = buf_count; + buf_pool->uninit_count = buf_count; + #if defined(CONFIG_NET_BUF_POOL_USAGE) + buf_pool->avail_count = buf_count; + #endif + buf_pool->destroy = destroy; +} + +void net_buf_deinit(struct net_buf_pool *buf_pool) +{ + struct net_buf_pool_fixed *buf_fixed = (struct net_buf_pool_fixed *)buf_pool->alloc->alloc_data; + k_free(buf_fixed->data_pool); + k_free(buf_pool->__bufs); + k_free(buf_pool->alloc->alloc_data); + k_free(buf_pool->alloc); +} +#endif + +struct net_buf_pool *net_buf_pool_get(int id) +{ +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + return _net_buf_pool_list[id]; +#else + return &_net_buf_pool_list[id]; +#endif +} + +static int pool_id(struct net_buf_pool *pool) +{ +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + int index; + + for (index = 0; index < (sizeof(_net_buf_pool_list) / 4); index++) { + if (_net_buf_pool_list[index] == pool) { + break; + } + } + NET_BUF_ASSERT(index < (sizeof(_net_buf_pool_list) / 4)); + return index; +#else + return pool - _net_buf_pool_list; +#endif +} + +int net_buf_id(struct net_buf *buf) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + return buf - pool->__bufs; +} + +static inline struct net_buf *pool_get_uninit(struct net_buf_pool *pool, + u16_t uninit_count) +{ + struct net_buf *buf; + + buf = &pool->__bufs[pool->buf_count - uninit_count]; + + buf->pool_id = pool_id(pool); + + return buf; +} + +void net_buf_reset(struct net_buf *buf) +{ + NET_BUF_ASSERT(buf->flags == 0U); + NET_BUF_ASSERT(buf->frags == NULL); + + net_buf_simple_reset(&buf->b); +} + +#if !defined(BFLB_BLE) +static u8_t *generic_data_ref(struct net_buf *buf, u8_t *data) +{ + u8_t *ref_count; + + ref_count = data - 1; + (*ref_count)++; + + return data; +} + +static u8_t *mem_pool_data_alloc(struct net_buf *buf, size_t *size, + s32_t timeout) +{ + struct net_buf_pool *buf_pool = net_buf_pool_get(buf->pool_id); + struct k_mem_pool *pool = buf_pool->alloc->alloc_data; + struct k_mem_block block; + u8_t *ref_count; + + /* Reserve extra space for k_mem_block_id and ref-count (u8_t) */ + if (k_mem_pool_alloc(pool, &block, + sizeof(struct k_mem_block_id) + 1 + *size, + timeout)) { + return NULL; + } + + /* save the block descriptor info at the start of the actual block */ + memcpy(block.data, &block.id, sizeof(block.id)); + + ref_count = (u8_t *)block.data + sizeof(block.id); + *ref_count = 1U; + + /* Return pointer to the byte following the ref count */ + return ref_count + 1; +} + +static void mem_pool_data_unref(struct net_buf *buf, u8_t *data) +{ + struct k_mem_block_id id; + u8_t *ref_count; + + ref_count = data - 1; + if (--(*ref_count)) { + return; + } + + /* Need to copy to local variable due to alignment */ + memcpy(&id, ref_count - sizeof(id), sizeof(id)); + k_mem_pool_free_id(&id); +} + +const struct net_buf_data_cb net_buf_var_cb = { + .alloc = mem_pool_data_alloc, + .ref = generic_data_ref, + .unref = mem_pool_data_unref, +}; +#endif + +static u8_t *fixed_data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + *size = MIN(fixed->data_size, *size); + + return fixed->data_pool + fixed->data_size * net_buf_id(buf); +} + +static void fixed_data_unref(struct net_buf *buf, u8_t *data) +{ + /* Nothing needed for fixed-size data pools */ +} + +const struct net_buf_data_cb net_buf_fixed_cb = { + .alloc = fixed_data_alloc, + .unref = fixed_data_unref, +}; + +#if defined (CONFIG_HEAP_MEM_POOL_SIZE) && (CONFIG_HEAP_MEM_POOL_SIZE > 0) + +static u8_t *heap_data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + u8_t *ref_count; + + ref_count = k_malloc(1 + *size); + if (!ref_count) { + return NULL; + } + + *ref_count = 1U; + + return ref_count + 1; +} + +static void heap_data_unref(struct net_buf *buf, u8_t *data) +{ + u8_t *ref_count; + + ref_count = data - 1; + if (--(*ref_count)) { + return; + } + + k_free(ref_count); +} + +static const struct net_buf_data_cb net_buf_heap_cb = { + .alloc = heap_data_alloc, + .ref = generic_data_ref, + .unref = heap_data_unref, +}; + +const struct net_buf_data_alloc net_buf_heap_alloc = { + .cb = &net_buf_heap_cb, +}; + +#endif /* CONFIG_HEAP_MEM_POOL_SIZE > 0 */ + +static u8_t *data_alloc(struct net_buf *buf, size_t *size, s32_t timeout) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + return pool->alloc->cb->alloc(buf, size, timeout); +} + +static u8_t *data_ref(struct net_buf *buf, u8_t *data) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + return pool->alloc->cb->ref(buf, data); +} + +static void data_unref(struct net_buf *buf, u8_t *data) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + if (buf->flags & NET_BUF_EXTERNAL_DATA) { + return; + } + + pool->alloc->cb->unref(buf, data); +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_len_debug(struct net_buf_pool *pool, size_t size, + s32_t timeout, const char *func, + int line) +#else +struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, + s32_t timeout) +#endif +{ + u32_t alloc_start = k_uptime_get_32(); + struct net_buf *buf; + unsigned int key; + + NET_BUF_ASSERT(pool); + + NET_BUF_DBG("%s():%d: pool %p size %zu timeout %d", func, line, pool, + size, timeout); + + /* We need to lock interrupts temporarily to prevent race conditions + * when accessing pool->uninit_count. + */ + key = irq_lock(); + + /* If there are uninitialized buffers we're guaranteed to succeed + * with the allocation one way or another. + */ + if (pool->uninit_count) { + u16_t uninit_count; + + /* If this is not the first access to the pool, we can + * be opportunistic and try to fetch a previously used + * buffer from the LIFO with K_NO_WAIT. + */ + if (pool->uninit_count < pool->buf_count) { + buf = k_lifo_get(&pool->free, K_NO_WAIT); + if (buf) { + irq_unlock(key); + goto success; + } + } + + uninit_count = pool->uninit_count--; + irq_unlock(key); + + buf = pool_get_uninit(pool, uninit_count); + goto success; + } + + irq_unlock(key); + +#if defined(CONFIG_NET_BUF_LOG) && (CONFIG_NET_BUF_LOG_LEVEL >= LOG_LEVEL_WRN) + if (timeout == K_FOREVER) { + u32_t ref = k_uptime_get_32(); + buf = k_lifo_get(&pool->free, K_NO_WAIT); + while (!buf) { +#if defined(CONFIG_NET_BUF_POOL_USAGE) + NET_BUF_WARN("%s():%d: Pool %s low on buffers.", + func, line, pool->name); +#else + NET_BUF_WARN("%s():%d: Pool %p low on buffers.", + func, line, pool); +#endif + buf = k_lifo_get(&pool->free, WARN_ALLOC_INTERVAL); +#if defined(CONFIG_NET_BUF_POOL_USAGE) + NET_BUF_WARN("%s():%d: Pool %s blocked for %u secs", + func, line, pool->name, + (k_uptime_get_32() - ref) / MSEC_PER_SEC); +#else + NET_BUF_WARN("%s():%d: Pool %p blocked for %u secs", + func, line, pool, + (k_uptime_get_32() - ref) / MSEC_PER_SEC); +#endif + } + } else { + buf = k_lifo_get(&pool->free, timeout); + } +#else + buf = k_lifo_get(&pool->free, timeout); +#endif + if (!buf) { + NET_BUF_ERR("%s():%d: Failed to get free buffer", func, line); + return NULL; + } + +success: + NET_BUF_DBG("allocated buf %p", buf); + + if (size) { + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= MIN(timeout, diff); + } + + buf->__buf = data_alloc(buf, &size, timeout); + if (!buf->__buf) { + NET_BUF_ERR("%s():%d: Failed to allocate data", + func, line); + net_buf_destroy(buf); + return NULL; + } + } else { + buf->__buf = NULL; + } + + buf->ref = 1U; + buf->flags = 0U; + buf->frags = NULL; + buf->size = size; + net_buf_reset(buf); + +#if defined(CONFIG_NET_BUF_POOL_USAGE) + pool->avail_count--; + NET_BUF_ASSERT(pool->avail_count >= 0); +#endif + + return buf; +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, + s32_t timeout, const char *func, + int line) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len_debug(pool, fixed->data_size, timeout, func, + line); +} +#else +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout) +{ + const struct net_buf_pool_fixed *fixed = pool->alloc->alloc_data; + + return net_buf_alloc_len(pool, fixed->data_size, timeout); +} +#endif + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_with_data_debug(struct net_buf_pool *pool, + void *data, size_t size, + s32_t timeout, const char *func, + int line) +#else +struct net_buf *net_buf_alloc_with_data(struct net_buf_pool *pool, + void *data, size_t size, + s32_t timeout) +#endif +{ + struct net_buf *buf; + +#if defined(CONFIG_NET_BUF_LOG) + buf = net_buf_alloc_len_debug(pool, 0, timeout, func, line); +#else + buf = net_buf_alloc_len(pool, 0, timeout); +#endif + if (!buf) { + return NULL; + } + + buf->__buf = data; + buf->data = data; + buf->size = size; + buf->len = size; + buf->flags = NET_BUF_EXTERNAL_DATA; + + return buf; +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_get_debug(struct k_fifo *fifo, s32_t timeout, + const char *func, int line) +#else +struct net_buf *net_buf_get(struct k_fifo *fifo, s32_t timeout) +#endif +{ + struct net_buf *buf, *frag; + + NET_BUF_DBG("%s():%d: fifo %p timeout %d", func, line, fifo, timeout); + + buf = k_fifo_get(fifo, timeout); + if (!buf) { + return NULL; + } + + NET_BUF_DBG("%s():%d: buf %p fifo %p", func, line, buf, fifo); + + /* Get any fragments belonging to this buffer */ + for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) { + frag->frags = k_fifo_get(fifo, K_NO_WAIT); + NET_BUF_ASSERT(frag->frags); + + /* The fragments flag is only for FIFO-internal usage */ + frag->flags &= ~NET_BUF_FRAGS; + } + + /* Mark the end of the fragment list */ + frag->frags = NULL; + + return buf; +} + +void net_buf_simple_init_with_data(struct net_buf_simple *buf, + void *data, size_t size) +{ + buf->__buf = data; + buf->data = data; + buf->size = size; + buf->len = size; +} + +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve) +{ + NET_BUF_ASSERT(buf); + NET_BUF_ASSERT(buf->len == 0U); + NET_BUF_DBG("buf %p reserve %zu", buf, reserve); + + buf->data = buf->__buf + reserve; +} + +void net_buf_slist_put(sys_slist_t *list, struct net_buf *buf) +{ + struct net_buf *tail; + unsigned int key; + + NET_BUF_ASSERT(list); + NET_BUF_ASSERT(buf); + + for (tail = buf; tail->frags; tail = tail->frags) { + tail->flags |= NET_BUF_FRAGS; + } + + key = irq_lock(); + sys_slist_append_list(list, &buf->node, &tail->node); + irq_unlock(key); +} + +struct net_buf *net_buf_slist_get(sys_slist_t *list) +{ + struct net_buf *buf, *frag; + unsigned int key; + + NET_BUF_ASSERT(list); + + key = irq_lock(); + buf = (void *)sys_slist_get(list); + irq_unlock(key); + + if (!buf) { + return NULL; + } + + /* Get any fragments belonging to this buffer */ + for (frag = buf; (frag->flags & NET_BUF_FRAGS); frag = frag->frags) { + key = irq_lock(); + frag->frags = (void *)sys_slist_get(list); + irq_unlock(key); + + NET_BUF_ASSERT(frag->frags); + + /* The fragments flag is only for list-internal usage */ + frag->flags &= ~NET_BUF_FRAGS; + } + + /* Mark the end of the fragment list */ + frag->frags = NULL; + + return buf; +} + +void net_buf_put(struct k_fifo *fifo, struct net_buf *buf) +{ + struct net_buf *tail; + + NET_BUF_ASSERT(fifo); + NET_BUF_ASSERT(buf); + + for (tail = buf; tail->frags; tail = tail->frags) { + tail->flags |= NET_BUF_FRAGS; + } + + k_fifo_put_list(fifo, buf, tail); +} + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +extern struct net_buf_pool hci_rx_pool; +extern void bl_handle_queued_msg(void); +#endif + +#if defined(CONFIG_NET_BUF_LOG) +void net_buf_unref_debug(struct net_buf *buf, const char *func, int line) +#else +void net_buf_unref(struct net_buf *buf) +#endif +{ + NET_BUF_ASSERT(buf); + + while (buf) { + struct net_buf *frags = buf->frags; + struct net_buf_pool *pool; +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + u8_t buf_type = bt_buf_get_type(buf); + bool adv_report = bt_buf_check_rx_adv(buf); +#endif + +#if defined(CONFIG_NET_BUF_LOG) + if (!buf->ref) { + NET_BUF_ERR("%s():%d: buf %p double free", func, line, + buf); + return; + } +#endif + NET_BUF_DBG("buf %p ref %u pool_id %u frags %p", buf, buf->ref, + buf->pool_id, buf->frags); + + unsigned int key = irq_lock();/* Added by bouffalo lab, to protect ref decrease */ + if (--buf->ref > 0) { + irq_unlock(key);/* Added by bouffalo lab */ + return; + } + irq_unlock(key);/* Added by bouffalo lab */ + + if (buf->__buf) { + data_unref(buf, buf->__buf); + buf->__buf = NULL; + } + + buf->data = NULL; + buf->frags = NULL; + + pool = net_buf_pool_get(buf->pool_id); + +#if defined(CONFIG_NET_BUF_POOL_USAGE) + pool->avail_count++; + NET_BUF_ASSERT(pool->avail_count <= pool->buf_count); +#endif + + if (pool->destroy) { + pool->destroy(buf); + } else { + net_buf_destroy(buf); + } + + buf = frags; + + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + if(pool == &hci_rx_pool && (buf_type == BT_BUF_ACL_IN || adv_report == true)){ + bl_handle_queued_msg(); + return; + } + #endif + } +} + +struct net_buf *net_buf_ref(struct net_buf *buf) +{ + NET_BUF_ASSERT(buf); + + NET_BUF_DBG("buf %p (old) ref %u pool_id %u", + buf, buf->ref, buf->pool_id); + + unsigned int key = irq_lock();/* Added by bouffalo lab, to protect ref increase */ + buf->ref++; + irq_unlock(key);/* Added by bouffalo lab */ + return buf; +} + +struct net_buf *net_buf_clone(struct net_buf *buf, s32_t timeout) +{ + u32_t alloc_start = k_uptime_get_32(); + struct net_buf_pool *pool; + struct net_buf *clone; + + NET_BUF_ASSERT(buf); + + pool = net_buf_pool_get(buf->pool_id); + + clone = net_buf_alloc_len(pool, 0, timeout); + if (!clone) { + return NULL; + } + + /* If the pool supports data referencing use that. Otherwise + * we need to allocate new data and make a copy. + */ + if (pool->alloc->cb->ref && !(buf->flags & NET_BUF_EXTERNAL_DATA)) { + clone->__buf = data_ref(buf, buf->__buf); + clone->data = buf->data; + clone->len = buf->len; + clone->size = buf->size; + } else { + size_t size = buf->size; + + if (timeout != K_NO_WAIT && timeout != K_FOREVER) { + u32_t diff = k_uptime_get_32() - alloc_start; + + timeout -= MIN(timeout, diff); + } + + clone->__buf = data_alloc(clone, &size, timeout); + if (!clone->__buf || size < buf->size) { + net_buf_destroy(clone); + return NULL; + } + + clone->size = size; + clone->data = clone->__buf + net_buf_headroom(buf); + net_buf_add_mem(clone, buf->data, buf->len); + } + + return clone; +} + +struct net_buf *net_buf_frag_last(struct net_buf *buf) +{ + NET_BUF_ASSERT(buf); + + while (buf->frags) { + buf = buf->frags; + } + + return buf; +} + +void net_buf_frag_insert(struct net_buf *parent, struct net_buf *frag) +{ + NET_BUF_ASSERT(parent); + NET_BUF_ASSERT(frag); + + if (parent->frags) { + net_buf_frag_last(frag)->frags = parent->frags; + } + /* Take ownership of the fragment reference */ + parent->frags = frag; +} + +struct net_buf *net_buf_frag_add(struct net_buf *head, struct net_buf *frag) +{ + NET_BUF_ASSERT(frag); + + if (!head) { + return net_buf_ref(frag); + } + + net_buf_frag_insert(net_buf_frag_last(head), frag); + + return head; +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_frag_del_debug(struct net_buf *parent, + struct net_buf *frag, + const char *func, int line) +#else +struct net_buf *net_buf_frag_del(struct net_buf *parent, struct net_buf *frag) +#endif +{ + struct net_buf *next_frag; + + NET_BUF_ASSERT(frag); + + if (parent) { + NET_BUF_ASSERT(parent->frags); + NET_BUF_ASSERT(parent->frags == frag); + parent->frags = frag->frags; + } + + next_frag = frag->frags; + + frag->frags = NULL; + +#if defined(CONFIG_NET_BUF_LOG) + net_buf_unref_debug(frag, func, line); +#else + net_buf_unref(frag); +#endif + + return next_frag; +} + +size_t net_buf_linearize(void *dst, size_t dst_len, struct net_buf *src, + size_t offset, size_t len) +{ + struct net_buf *frag; + size_t to_copy; + size_t copied; + + len = MIN(len, dst_len); + + frag = src; + + /* find the right fragment to start copying from */ + while (frag && offset >= frag->len) { + offset -= frag->len; + frag = frag->frags; + } + + /* traverse the fragment chain until len bytes are copied */ + copied = 0; + while (frag && len > 0) { + to_copy = MIN(len, frag->len - offset); + memcpy((u8_t *)dst + copied, frag->data + offset, to_copy); + + copied += to_copy; + + /* to_copy is always <= len */ + len -= to_copy; + frag = frag->frags; + + /* after the first iteration, this value will be 0 */ + offset = 0; + } + + return copied; +} + +/* This helper routine will append multiple bytes, if there is no place for + * the data in current fragment then create new fragment and add it to + * the buffer. It assumes that the buffer has at least one fragment. + */ +size_t net_buf_append_bytes(struct net_buf *buf, size_t len, + const void *value, s32_t timeout, + net_buf_allocator_cb allocate_cb, void *user_data) +{ + struct net_buf *frag = net_buf_frag_last(buf); + size_t added_len = 0; + const u8_t *value8 = value; + + do { + u16_t count = MIN(len, net_buf_tailroom(frag)); + + net_buf_add_mem(frag, value8, count); + len -= count; + added_len += count; + value8 += count; + + if (len == 0) { + return added_len; + } + + frag = allocate_cb(timeout, user_data); + if (!frag) { + return added_len; + } + + net_buf_frag_add(buf, frag); + } while (1); + + /* Unreachable */ + return 0; +} + +#if defined(CONFIG_NET_BUF_SIMPLE_LOG) +#define NET_BUF_SIMPLE_DBG(fmt, ...) NET_BUF_DBG(fmt, ##__VA_ARGS__) +#define NET_BUF_SIMPLE_ERR(fmt, ...) NET_BUF_ERR(fmt, ##__VA_ARGS__) +#define NET_BUF_SIMPLE_WARN(fmt, ...) NET_BUF_WARN(fmt, ##__VA_ARGS__) +#define NET_BUF_SIMPLE_INFO(fmt, ...) NET_BUF_INFO(fmt, ##__VA_ARGS__) +#define NET_BUF_SIMPLE_ASSERT(cond) NET_BUF_ASSERT(cond) +#else +#define NET_BUF_SIMPLE_DBG(fmt, ...) +#define NET_BUF_SIMPLE_ERR(fmt, ...) +#define NET_BUF_SIMPLE_WARN(fmt, ...) +#define NET_BUF_SIMPLE_INFO(fmt, ...) +#define NET_BUF_SIMPLE_ASSERT(cond) +#endif /* CONFIG_NET_BUF_SIMPLE_LOG */ + +void net_buf_simple_clone(const struct net_buf_simple *original, + struct net_buf_simple *clone) +{ + memcpy(clone, original, sizeof(struct net_buf_simple)); +} + +void *net_buf_simple_add(struct net_buf_simple *buf, size_t len) +{ + u8_t *tail = net_buf_simple_tail(buf); + + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + NET_BUF_SIMPLE_ASSERT(net_buf_simple_tailroom(buf) >= len); + + buf->len += len; + return tail; +} + +void *net_buf_simple_add_mem(struct net_buf_simple *buf, const void *mem, + size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + return memcpy(net_buf_simple_add(buf, len), mem, len); +} + +u8_t *net_buf_simple_add_u8(struct net_buf_simple *buf, u8_t val) +{ + u8_t *u8; + + NET_BUF_SIMPLE_DBG("buf %p val 0x%02x", buf, val); + + u8 = net_buf_simple_add(buf, 1); + *u8 = val; + + return u8; +} + +void net_buf_simple_add_le16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_le16(val, net_buf_simple_add(buf, sizeof(val))); +} + +void net_buf_simple_add_be16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_be16(val, net_buf_simple_add(buf, sizeof(val))); +} + +void net_buf_simple_add_le24(struct net_buf_simple *buf, uint32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_le24(val, net_buf_simple_add(buf, 3)); +} + +void net_buf_simple_add_be24(struct net_buf_simple *buf, uint32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_be24(val, net_buf_simple_add(buf, 3)); +} + +void net_buf_simple_add_le32(struct net_buf_simple *buf, u32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_le32(val, net_buf_simple_add(buf, sizeof(val))); +} + +void net_buf_simple_add_be32(struct net_buf_simple *buf, u32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_be32(val, net_buf_simple_add(buf, sizeof(val))); +} + +void *net_buf_simple_push(struct net_buf_simple *buf, size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + NET_BUF_SIMPLE_ASSERT(net_buf_simple_headroom(buf) >= len); + + buf->data -= len; + buf->len += len; + return buf->data; +} + +void net_buf_simple_push_le16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_le16(val, net_buf_simple_push(buf, sizeof(val))); +} + +void net_buf_simple_push_be16(struct net_buf_simple *buf, u16_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_be16(val, net_buf_simple_push(buf, sizeof(val))); +} + +void net_buf_simple_push_u8(struct net_buf_simple *buf, u8_t val) +{ + u8_t *data = net_buf_simple_push(buf, 1); + + *data = val; +} + +void net_buf_simple_push_le24(struct net_buf_simple *buf, uint32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_le24(val, net_buf_simple_push(buf, 3)); +} + +void net_buf_simple_push_be24(struct net_buf_simple *buf, uint32_t val) +{ + NET_BUF_SIMPLE_DBG("buf %p val %u", buf, val); + + sys_put_be24(val, net_buf_simple_push(buf, 3)); +} + +void *net_buf_simple_pull(struct net_buf_simple *buf, size_t len) +{ + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + NET_BUF_SIMPLE_ASSERT(buf->len >= len); + + buf->len -= len; + return buf->data += len; +} + +void *net_buf_simple_pull_mem(struct net_buf_simple *buf, size_t len) +{ + void *data = buf->data; + + NET_BUF_SIMPLE_DBG("buf %p len %zu", buf, len); + + NET_BUF_SIMPLE_ASSERT(buf->len >= len); + + buf->len -= len; + buf->data += len; + + return data; +} + +u8_t net_buf_simple_pull_u8(struct net_buf_simple *buf) +{ + u8_t val; + + val = buf->data[0]; + net_buf_simple_pull(buf, 1); + + return val; +} + +u16_t net_buf_simple_pull_le16(struct net_buf_simple *buf) +{ + u16_t val; + + val = UNALIGNED_GET((u16_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_le16_to_cpu(val); +} + +u16_t net_buf_simple_pull_be16(struct net_buf_simple *buf) +{ + u16_t val; + + val = UNALIGNED_GET((u16_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_be16_to_cpu(val); +} + +u32_t net_buf_simple_pull_le32(struct net_buf_simple *buf) +{ + u32_t val; + + val = UNALIGNED_GET((u32_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_le32_to_cpu(val); +} + +u32_t net_buf_simple_pull_be32(struct net_buf_simple *buf) +{ + u32_t val; + + val = UNALIGNED_GET((u32_t *)buf->data); + net_buf_simple_pull(buf, sizeof(val)); + + return sys_be32_to_cpu(val); +} + +size_t net_buf_simple_headroom(struct net_buf_simple *buf) +{ + return buf->data - buf->__buf; +} + +size_t net_buf_simple_tailroom(struct net_buf_simple *buf) +{ + return buf->size - net_buf_simple_headroom(buf) - buf->len; +} diff --git a/components/ble/ble_stack/common/dec.c b/components/ble/ble_stack/common/dec.c new file mode 100644 index 00000000..4827b0a7 --- /dev/null +++ b/components/ble/ble_stack/common/dec.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +u8_t u8_to_dec(char *buf, u8_t buflen, u8_t value) +{ + u8_t divisor = 100; + u8_t num_digits = 0; + u8_t digit; + + while (buflen > 0 && divisor > 0) { + digit = value / divisor; + if (digit != 0 || divisor == 1 || num_digits != 0) { + *buf = (char)digit + '0'; + buf++; + buflen--; + num_digits++; + } + + value -= digit * divisor; + divisor /= 10; + } + + if (buflen) { + *buf = '\0'; + } + + return num_digits; +} + diff --git a/components/ble/ble_stack/common/dummy.c b/components/ble/ble_stack/common/dummy.c new file mode 100644 index 00000000..a06d28bf --- /dev/null +++ b/components/ble/ble_stack/common/dummy.c @@ -0,0 +1,40 @@ +/** + * @file dummy.c + * Static compilation checks. + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#if defined(CONFIG_BT_HCI_HOST) +/* The Bluetooth subsystem requires the Tx thread to execute at higher priority + * than the Rx thread as the Tx thread needs to process the acknowledgements + * before new Rx data is processed. This is a necessity to correctly detect + * transaction violations in ATT and SMP protocols. + */ +BUILD_ASSERT(CONFIG_BT_HCI_TX_PRIO < CONFIG_BT_RX_PRIO); +#endif + +#if defined(CONFIG_BT_CTLR) +/* The Bluetooth Controller's priority receive thread priority shall be higher + * than the Bluetooth Host's Tx and the Controller's receive thread priority. + * This is required in order to dispatch Number of Completed Packets event + * before any new data arrives on a connection to the Host threads. + */ +BUILD_ASSERT(CONFIG_BT_CTLR_RX_PRIO < CONFIG_BT_HCI_TX_PRIO); +#endif /* CONFIG_BT_CTLR */ + +/* Immediate logging is not supported with the software-based Link Layer + * since it introduces ISR latency due to outputting log messages with + * interrupts disabled. + */ +#if !defined(CONFIG_TEST) && !defined(CONFIG_ARCH_POSIX) && \ + (defined(CONFIG_BT_LL_SW_SPLIT) || defined(CONFIG_BT_LL_SW_LEGACY)) +BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_LOG_IMMEDIATE), "Immediate logging not " + "supported with the software Link Layer"); +#endif diff --git a/components/ble/ble_stack/common/hex.c b/components/ble/ble_stack/common/hex.c new file mode 100644 index 00000000..e9f46f8a --- /dev/null +++ b/components/ble/ble_stack/common/hex.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +int char2hex(char c, u8_t *x) +{ + if (c >= '0' && c <= '9') { + *x = c - '0'; + } else if (c >= 'a' && c <= 'f') { + *x = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + *x = c - 'A' + 10; + } else { + return -EINVAL; + } + + return 0; +} + +int hex2char(u8_t x, char *c) +{ + if (x <= 9) { + *c = x + '0'; + } else if (x >= 10 && x <= 15) { + *c = x - 10 + 'a'; + } else { + return -EINVAL; + } + + return 0; +} + +size_t bin2hex(const u8_t *buf, size_t buflen, char *hex, size_t hexlen) +{ + if ((hexlen + 1) < buflen * 2) { + return 0; + } + + for (size_t i = 0; i < buflen; i++) { + if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) { + return 0; + } + if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) { + return 0; + } + } + + hex[2 * buflen] = '\0'; + return 2 * buflen; +} + +size_t hex2bin(const char *hex, size_t hexlen, u8_t *buf, size_t buflen) +{ + u8_t dec; + + if (buflen < hexlen / 2 + hexlen % 2) { + return 0; + } + + /* if hexlen is uneven, insert leading zero nibble */ + if (hexlen % 2) { + if (char2hex(hex[0], &dec) < 0) { + return 0; + } + buf[0] = dec; + hex++; + buf++; + } + + /* regular hex conversion */ + for (size_t i = 0; i < hexlen / 2; i++) { + if (char2hex(hex[2 * i], &dec) < 0) { + return 0; + } + buf[i] = dec << 4; + + if (char2hex(hex[2 * i + 1], &dec) < 0) { + return 0; + } + buf[i] += dec; + } + + return hexlen / 2 + hexlen % 2; +} diff --git a/components/ble/ble_stack/common/include/atomic.h b/components/ble/ble_stack/common/include/atomic.h new file mode 100644 index 00000000..e0d6c122 --- /dev/null +++ b/components/ble/ble_stack/common/include/atomic.h @@ -0,0 +1,445 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} +#else +extern int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value); +#endif + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_add(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} +#else +extern atomic_val_t atomic_inc(atomic_t *target); +#endif + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} +#else +extern atomic_val_t atomic_dec(atomic_t *target); +#endif + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_get(const atomic_t *target); +#endif + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_set(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} +#else +extern atomic_val_t atomic_clear(atomic_t *target); +#endif + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_or(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_and(atomic_t *target, atomic_val_t value); +#endif + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} +#else +extern atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value); +#endif + + +/** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + +/** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + +/** + * INTERNAL_HIDDEN @endcond + */ + +/** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + +/** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int atomic_test_bit(const atomic_t *target, int bit) +{ + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); +} + +/** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int atomic_test_and_clear_bit(atomic_t *target, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; +} + +/** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ +static inline int atomic_test_and_set_bit(atomic_t *target, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; +} + +/** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ +static inline void atomic_clear_bit(atomic_t *target, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); +} + +/** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ +static inline void atomic_set_bit(atomic_t *target, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); +} + +/** + * @brief Atomically set a bit to a given value. + * + * Atomically set bit number @a bit of @a target to value @a val. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * @param val true for 1, false for 0. + * + * @return N/A + */ +static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + if (val) { + (void)atomic_or(ATOMIC_ELEM(target, bit), mask); + } else { + (void)atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/components/ble/ble_stack/common/include/errno.h b/components/ble/ble_stack/common/include/errno.h new file mode 100644 index 00000000..6acf9bdd --- /dev/null +++ b/components/ble/ble_stack/common/include/errno.h @@ -0,0 +1,133 @@ +/* errno.h - errno numbers */ + +/* + * Copyright (c) 1984-1999, 2012 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + * + * @(#)errno.h 7.1 (Berkeley) 6/4/86 + */ + +#ifndef __INCerrnoh +#define __INCerrnoh + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int *__errno(void); +#define errno (*__errno()) + +/* + * POSIX Error codes + */ + +#define EPERM 1 /* Not owner */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such context */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No children */ +#define EAGAIN 11 /* No more contexts */ +#define ENOMEM 12 /* Not enough core */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTEMPTY 15 /* Directory not empty */ +#define EBUSY 16 /* Mount device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ENAMETOOLONG 26 /* File name too long */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDEADLK 33 /* Resource deadlock avoided */ +#define ENOLCK 34 /* No locks available */ +#define ENOTSUP 35 /* Unsupported value */ +#define EMSGSIZE 36 /* Message size */ + +/* ANSI math software */ +#define EDOM 37 /* Argument too large */ +#define ERANGE 38 /* Result too large */ + +/* ipc/network software */ + +/* argument errors */ +#define EDESTADDRREQ 40 /* Destination address required */ +#define EPROTOTYPE 41 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 42 /* Protocol not available */ +#define EPROTONOSUPPORT 43 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#define EOPNOTSUPP 45 /* Operation not supported on socket */ +#define EPFNOSUPPORT 46 /* Protocol family not supported */ +#define EAFNOSUPPORT 47 /* Addr family not supported */ +#define EADDRINUSE 48 /* Address already in use */ +#define EADDRNOTAVAIL 49 /* Can't assign requested address */ +#define ENOTSOCK 50 /* Socket operation on non-socket */ + +/* operational errors */ +#define ENETUNREACH 51 /* Network is unreachable */ +#define ENETRESET 52 /* Network dropped connection on reset */ +#define ECONNABORTED 53 /* Software caused connection abort */ +#define ECONNRESET 54 /* Connection reset by peer */ +#define ENOBUFS 55 /* No buffer space available */ +#define EISCONN 56 /* Socket is already connected */ +#define ENOTCONN 57 /* Socket is not connected */ +#define ESHUTDOWN 58 /* Can't send after socket shutdown */ +#define ETOOMANYREFS 59 /* Too many references: can't splice */ +#define ETIMEDOUT 60 /* Connection timed out */ +#define ECONNREFUSED 61 /* Connection refused */ +#define ENETDOWN 62 /* Network is down */ +#define ETXTBSY 63 /* Text file busy */ +#define ELOOP 64 /* Too many levels of symbolic links */ +#define EHOSTUNREACH 65 /* No route to host */ +#define ENOTBLK 66 /* Block device required */ +#define EHOSTDOWN 67 /* Host is down */ + +/* non-blocking and interrupt i/o */ +#define EINPROGRESS 68 /* Operation now in progress */ +#define EALREADY 69 /* Operation already in progress */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ + +#define ENOSYS 71 /* Function not implemented */ + +/* aio errors (should be under posix) */ +#define ECANCELED 72 /* Operation canceled */ + +#define ERRMAX 81 + +/* specific STREAMS errno values */ + +#define ENOSR 74 /* Insufficient memory */ +#define ENOSTR 75 /* STREAMS device required */ +#define EPROTO 76 /* Generic STREAMS error */ +#define EBADMSG 77 /* Invalid STREAMS message */ +#define ENODATA 78 /* Missing expected message data */ +#define ETIME 79 /* STREAMS timeout occurred */ +#define ENOMSG 80 /* Unexpected message type */ + +#ifdef __cplusplus +} +#endif + +#endif /* __INCerrnoh */ diff --git a/components/ble/ble_stack/common/include/misc/__assert.h b/components/ble/ble_stack/common/include/misc/__assert.h new file mode 100644 index 00000000..48e1d326 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/__assert.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011-2014 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Debug aid + * + * + * The __ASSERT() macro can be used inside kernel code. + * + * Assertions are enabled by setting the __ASSERT_ON symbol to a non-zero value. + * There are two ways to do this: + * a) Use the ASSERT and ASSERT_LEVEL kconfig options + * b) Add "CFLAGS += -D__ASSERT_ON=" at the end of a project's Makefile + * The Makefile method takes precedence over the kconfig option if both are + * used. + * + * Specifying an assertion level of 1 causes the compiler to issue warnings that + * the kernel contains debug-type __ASSERT() statements; this reminder is issued + * since assertion code is not normally present in a final product. Specifying + * assertion level 2 suppresses these warnings. + * + * The __ASSERT_EVAL() macro can also be used inside kernel code. + * + * It makes use of the __ASSERT() macro, but has some extra flexibility. It + * allows the developer to specify different actions depending whether the + * __ASSERT() macro is enabled or not. This can be particularly useful to + * prevent the compiler from generating comments (errors, warnings or remarks) + * about variables that are only used with __ASSERT() being assigned a value, + * but otherwise unused when the __ASSERT() macro is disabled. + * + * Consider the following example: + * + * int x; + * + * x = foo (); + * __ASSERT (x != 0, "foo() returned zero!"); + * + * If __ASSERT() is disabled, then 'x' is assigned a value, but never used. + * This type of situation can be resolved using the __ASSERT_EVAL() macro. + * + * __ASSERT_EVAL ((void) foo(), + * int x = foo(), + * x != 0, + * "foo() returned zero!"); + * + * The first parameter tells __ASSERT_EVAL() what to do if __ASSERT() is + * disabled. The second parameter tells __ASSERT_EVAL() what to do if + * __ASSERT() is enabled. The third and fourth parameters are the parameters + * it passes to __ASSERT(). + * + * The __ASSERT_NO_MSG() macro can be used to perform an assertion that reports + * the failed test and its location, but lacks additional debugging information + * provided to assist the user in diagnosing the problem; its use is + * discouraged. + */ + +#ifndef ___ASSERT__H_ +#define ___ASSERT__H_ + +#ifdef CONFIG_ASSERT +#ifndef __ASSERT_ON +#define __ASSERT_ON CONFIG_ASSERT_LEVEL +#endif +#endif + +#ifdef __ASSERT_ON +#if (__ASSERT_ON < 0) || (__ASSERT_ON > 2) +#error "Invalid __ASSERT() level: must be between 0 and 2" +#endif + +#if __ASSERT_ON +#include +#define __ASSERT(test, fmt, ...) \ + do { \ + if (!(test)) { \ + printk("ASSERTION FAIL [%s] @ %s:%d:\n\t", \ + _STRINGIFY(test), \ + __FILE__, \ + __LINE__); \ + printk(fmt, ##__VA_ARGS__); \ + for (;;) \ + ; /* spin thread */ \ + } \ + } while ((0)) + +#define __ASSERT_EVAL(expr1, expr2, test, fmt, ...) \ + do { \ + expr2; \ + __ASSERT(test, fmt, ##__VA_ARGS__); \ + } while (0) + +#if (__ASSERT_ON == 1) +#warning "__ASSERT() statements are ENABLED" +#endif +#else +#define __ASSERT(test, fmt, ...) \ + do {/* nothing */ \ + } while ((0)) +#define __ASSERT_EVAL(expr1, expr2, test, fmt, ...) expr1 +#endif +#else +#define __ASSERT(test, fmt, ...) \ + do {/* nothing */ \ + } while ((0)) +#define __ASSERT_EVAL(expr1, expr2, test, fmt, ...) expr1 +#endif + +#define __ASSERT_NO_MSG(test) __ASSERT(test, "") + +#endif /* ___ASSERT__H_ */ diff --git a/components/ble/ble_stack/common/include/misc/byteorder.h b/components/ble/ble_stack/common/include/misc/byteorder.h new file mode 100644 index 00000000..1dd38290 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/byteorder.h @@ -0,0 +1,406 @@ +/** @file + * @brief Byte order helpers. + */ + +/* + * Copyright (c) 2015-2016, Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BYTEORDER_H__ +#define __BYTEORDER_H__ + +#include +#include +#include + +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif + + +/* Internal helpers only used by the sys_* APIs further below */ +#define __bswap_16(x) ((u16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))) +#define __bswap_32(x) ((u32_t) ((((x) >> 24) & 0xff) | \ + (((x) >> 8) & 0xff00) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff) << 24))) +#define __bswap_64(x) ((u64_t) ((((x) >> 56) & 0xff) | \ + (((x) >> 40) & 0xff00) | \ + (((x) >> 24) & 0xff0000) | \ + (((x) >> 8) & 0xff000000) | \ + (((x) & 0xff000000) << 8) | \ + (((x) & 0xff0000) << 24) | \ + (((x) & 0xff00) << 40) | \ + (((x) & 0xff) << 56))) + +/** @def sys_le16_to_cpu + * @brief Convert 16-bit integer from little-endian to host endianness. + * + * @param val 16-bit integer in little-endian format. + * + * @return 16-bit integer in host endianness. + */ + +/** @def sys_cpu_to_le16 + * @brief Convert 16-bit integer from host endianness to little-endian. + * + * @param val 16-bit integer in host endianness. + * + * @return 16-bit integer in little-endian format. + */ + +/** @def sys_be16_to_cpu + * @brief Convert 16-bit integer from big-endian to host endianness. + * + * @param val 16-bit integer in big-endian format. + * + * @return 16-bit integer in host endianness. + */ + +/** @def sys_cpu_to_be16 + * @brief Convert 16-bit integer from host endianness to big-endian. + * + * @param val 16-bit integer in host endianness. + * + * @return 16-bit integer in big-endian format. + */ + +/** @def sys_le32_to_cpu + * @brief Convert 32-bit integer from little-endian to host endianness. + * + * @param val 32-bit integer in little-endian format. + * + * @return 32-bit integer in host endianness. + */ + +/** @def sys_cpu_to_le32 + * @brief Convert 32-bit integer from host endianness to little-endian. + * + * @param val 32-bit integer in host endianness. + * + * @return 32-bit integer in little-endian format. + */ + +/** @def sys_be32_to_cpu + * @brief Convert 32-bit integer from big-endian to host endianness. + * + * @param val 32-bit integer in big-endian format. + * + * @return 32-bit integer in host endianness. + */ + +/** @def sys_cpu_to_be32 + * @brief Convert 32-bit integer from host endianness to big-endian. + * + * @param val 32-bit integer in host endianness. + * + * @return 32-bit integer in big-endian format. + */ + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define sys_le16_to_cpu(val) (val) +#define sys_cpu_to_le16(val) (val) +#define sys_be16_to_cpu(val) __bswap_16(val) +#define sys_cpu_to_be16(val) __bswap_16(val) +#define sys_le32_to_cpu(val) (val) +#define sys_cpu_to_le32(val) (val) +#define sys_le64_to_cpu(val) (val) +#define sys_cpu_to_le64(val) (val) +#define sys_be32_to_cpu(val) __bswap_32(val) +#define sys_cpu_to_be32(val) __bswap_32(val) +#define sys_be64_to_cpu(val) __bswap_64(val) +#define sys_cpu_to_be64(val) __bswap_64(val) +/******************************************************************************** +** Macros to get and put bytes to a stream (Little Endian format). +*/ +#define UINT32_TO_STREAM(p, u32) {*(p)++ = (u8_t)(u32); *(p)++ = (u8_t)((u32) >> 8); *(p)++ = (u8_t)((u32) >> 16); *(p)++ = (u8_t)((u32) >> 24);} +#define UINT24_TO_STREAM(p, u24) {*(p)++ = (u8_t)(u24); *(p)++ = (u8_t)((u24) >> 8); *(p)++ = (u8_t)((u24) >> 16);} +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (u8_t)(u16); *(p)++ = (u8_t)((u16) >> 8);} +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (u8_t)(u8);} + +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define sys_le16_to_cpu(val) __bswap_16(val) +#define sys_cpu_to_le16(val) __bswap_16(val) +#define sys_be16_to_cpu(val) (val) +#define sys_cpu_to_be16(val) (val) +#define sys_le32_to_cpu(val) __bswap_32(val) +#define sys_cpu_to_le32(val) __bswap_32(val) +#define sys_le64_to_cpu(val) __bswap_64(val) +#define sys_cpu_to_le64(val) __bswap_64(val) +#define sys_be32_to_cpu(val) (val) +#define sys_cpu_to_be32(val) (val) +#define sys_be64_to_cpu(val) (val) +#define sys_cpu_to_be64(val) (val) +/******************************************************************************** +** Macros to get and put bytes to a stream (Big Endian format) +*/ +#define UINT32_TO_STREAM(p, u32) {*(p)++ = (u8_t)((u32) >> 24); *(p)++ = (u8_t)((u32) >> 16); *(p)++ = (u8_t)((u32) >> 8); *(p)++ = (u8_t)(u32); } +#define UINT24_TO_STREAM(p, u24) {*(p)++ = (u8_t)((u24) >> 16); *(p)++ = (u8_t)((u24) >> 8); *(p)++ = (u8_t)(u24);} +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (u8_t)((u16) >> 8); *(p)++ = (u8_t)(u16);} +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (u8_t)(u8);} + +#else +#error "Unknown byte order" +#endif + +/** + * @brief Put a 16-bit integer as big-endian to arbitrary location. + * + * Put a 16-bit integer, originally in host endianness, to a + * potentially unaligned memory location in big-endian format. + * + * @param val 16-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_be16(u16_t val, u8_t dst[2]) +{ + dst[0] = val >> 8; + dst[1] = val; +} + +/** + * @brief Put a 24-bit integer as big-endian to arbitrary location. + * + * Put a 24-bit integer, originally in host endianness, to a + * potentially unaligned memory location in big-endian format. + * + * @param val 24-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_be24(uint32_t val, uint8_t dst[3]) +{ + dst[0] = val >> 16; + sys_put_be16(val, &dst[1]); +} + +/** + * @brief Put a 32-bit integer as big-endian to arbitrary location. + * + * Put a 32-bit integer, originally in host endianness, to a + * potentially unaligned memory location in big-endian format. + * + * @param val 32-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_be32(u32_t val, u8_t dst[4]) +{ + sys_put_be16(val >> 16, dst); + sys_put_be16(val, &dst[2]); +} + +/** + * @brief Put a 16-bit integer as little-endian to arbitrary location. + * + * Put a 16-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 16-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le16(u16_t val, u8_t dst[2]) +{ + dst[0] = val; + dst[1] = val >> 8; +} + +/** + * @brief Put a 24-bit integer as little-endian to arbitrary location. + * + * Put a 24-bit integer, originally in host endianness, to a + * potentially unaligned memory location in littel-endian format. + * + * @param val 24-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le24(uint32_t val, uint8_t dst[3]) +{ + sys_put_le16(val, dst); + dst[2] = val >> 16; +} + +/** + * @brief Put a 32-bit integer as little-endian to arbitrary location. + * + * Put a 32-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 32-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le32(u32_t val, u8_t dst[4]) +{ + sys_put_le16(val, dst); + sys_put_le16(val >> 16, &dst[2]); +} + +/** + * @brief Put a 64-bit integer as little-endian to arbitrary location. + * + * Put a 64-bit integer, originally in host endianness, to a + * potentially unaligned memory location in little-endian format. + * + * @param val 64-bit integer in host endianness. + * @param dst Destination memory address to store the result. + */ +static inline void sys_put_le64(u64_t val, u8_t dst[8]) +{ + sys_put_le32(val, dst); + sys_put_le32(val >> 32, &dst[4]); +} + +/** + * @brief Get a 16-bit integer stored in big-endian format. + * + * Get a 16-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 16-bit integer to get. + * + * @return 16-bit integer in host endianness. + */ +static inline u16_t sys_get_be16(const u8_t src[2]) +{ + return ((u16_t)src[0] << 8) | src[1]; +} + +/** + * @brief Get a 24-bit integer stored in big-endian format. + * + * Get a 24-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 24-bit integer to get. + * + * @return 24-bit integer in host endianness. + */ +static inline uint32_t sys_get_be24(const uint8_t src[3]) +{ + return ((uint32_t)src[0] << 16) | sys_get_be16(&src[1]); +} + +/** + * @brief Get a 32-bit integer stored in big-endian format. + * + * Get a 32-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 32-bit integer to get. + * + * @return 32-bit integer in host endianness. + */ +static inline u32_t sys_get_be32(const u8_t src[4]) +{ + return ((u32_t)sys_get_be16(&src[0]) << 16) | sys_get_be16(&src[2]); +} + +/** + * @brief Get a 16-bit integer stored in little-endian format. + * + * Get a 16-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 16-bit integer to get. + * + * @return 16-bit integer in host endianness. + */ +static inline u16_t sys_get_le16(const u8_t src[2]) +{ + return ((u16_t)src[1] << 8) | src[0]; +} + +/** + * @brief Get a 24-bit integer stored in big-endian format. + * + * Get a 24-bit integer, stored in big-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the big-endian 24-bit integer to get. + * + * @return 24-bit integer in host endianness. + */ +static inline uint32_t sys_get_le24(const uint8_t src[3]) +{ + return ((uint32_t)src[2] << 16) | sys_get_le16(&src[0]); +} + +/** + * @brief Get a 32-bit integer stored in little-endian format. + * + * Get a 32-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 32-bit integer to get. + * + * @return 32-bit integer in host endianness. + */ +static inline u32_t sys_get_le32(const u8_t src[4]) +{ + return ((u32_t)sys_get_le16(&src[2]) << 16) | sys_get_le16(&src[0]); +} + +/** + * @brief Get a 64-bit integer stored in little-endian format. + * + * Get a 64-bit integer, stored in little-endian format in a potentially + * unaligned memory location, and convert it to the host endianness. + * + * @param src Location of the little-endian 64-bit integer to get. + * + * @return 64-bit integer in host endianness. + */ +static inline u64_t sys_get_le64(const u8_t src[8]) +{ + return ((u64_t)sys_get_le32(&src[4]) << 32) | sys_get_le32(&src[0]); +} + +/** + * @brief Swap one buffer content into another + * + * Copy the content of src buffer into dst buffer in reversed order, + * i.e.: src[n] will be put in dst[end-n] + * Where n is an index and 'end' the last index in both arrays. + * The 2 memory pointers must be pointing to different areas, and have + * a minimum size of given length. + * + * @param dst A valid pointer on a memory area where to copy the data in + * @param src A valid pointer on a memory area where to copy the data from + * @param length Size of both dst and src memory areas + */ +static inline void sys_memcpy_swap(void *dst, const void *src, size_t length) +{ + __ASSERT(((src < dst && (src + length) <= dst) || + (src > dst && (dst + length) <= src)), + "Source and destination buffers must not overlap"); + + src += length - 1; + + for (; length > 0; length--) { + *((u8_t *)dst++) = *((u8_t *)src--); + } +} + +/** + * @brief Swap buffer content + * + * In-place memory swap, where final content will be reversed. + * I.e.: buf[n] will be put in buf[end-n] + * Where n is an index and 'end' the last index of buf. + * + * @param buf A valid pointer on a memory area to swap + * @param length Size of buf memory area + */ +static inline void sys_mem_swap(void *buf, size_t length) +{ + size_t i; + + for (i = 0; i < (length/2); i++) { + u8_t tmp = ((u8_t *)buf)[i]; + + ((u8_t *)buf)[i] = ((u8_t *)buf)[length - 1 - i]; + ((u8_t *)buf)[length - 1 - i] = tmp; + } +} + +#endif /* __BYTEORDER_H__ */ diff --git a/components/ble/ble_stack/common/include/misc/dlist.h b/components/ble/ble_stack/common/include/misc/dlist.h new file mode 100644 index 00000000..5df9bd99 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/dlist.h @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2013-2015 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Doubly-linked list implementation + * + * Doubly-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + * + * The lists are expected to be initialized such that both the head and tail + * pointers point to the list itself. Initializing the lists in such a fashion + * simplifies the adding and removing of nodes to/from the list. + */ + +#ifndef _misc_dlist__h_ +#define _misc_dlist__h_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct _dnode { + union { + struct _dnode *head; /* ptr to head of list (sys_dlist_t) */ + struct _dnode *next; /* ptr to next node (sys_dnode_t) */ + }; + union { + struct _dnode *tail; /* ptr to tail of list (sys_dlist_t) */ + struct _dnode *prev; /* ptr to previous node (sys_dnode_t) */ + }; +}; + +typedef struct _dnode sys_dlist_t; +typedef struct _dnode sys_dnode_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __dn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list + */ +#define SYS_DLIST_FOR_EACH_NODE(__dl, __dn) \ + for (__dn = sys_dlist_peek_head(__dl); __dn; \ + __dn = sys_dlist_peek_next(__dl, __dn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __dn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_DLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list; + * it contains the starting node, or NULL to start from the head + */ +#define SYS_DLIST_ITERATE_FROM_NODE(__dl, __dn) \ + for (__dn = __dn ? sys_dlist_peek_next_no_check(__dl, __dn) \ + : sys_dlist_peek_head(__dl); \ + __dn; \ + __dn = sys_dlist_peek_next(__dl, __dn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __dn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_DLIST_*() macros are not thread safe. + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __dn A sys_dnode_t pointer to peek each node of the list + * @param __dns A sys_dnode_t pointer for the loop to run safely + */ +#define SYS_DLIST_FOR_EACH_NODE_SAFE(__dl, __dn, __dns) \ + for (__dn = sys_dlist_peek_head(__dl), \ + __dns = sys_dlist_peek_next(__dl, __dn); \ + __dn; __dn = __dns, \ + __dns = sys_dlist_peek_next(__dl, __dn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __dn A pointer on a sys_dnode_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_CONTAINER(__dn, __cn, __n) \ + (__dn ? CONTAINER_OF(__dn, __typeof__(*__cn), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __dl A pointer on a sys_dlist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n) \ + SYS_DLIST_CONTAINER(sys_dlist_peek_head(__dl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __dl A pointer on a sys_dlist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n) \ + ((__cn) ? SYS_DLIST_CONTAINER(sys_dlist_peek_next(__dl, &(__cn->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_FOR_EACH_CONTAINER(__dl, __cn, __n) \ + for (__cn = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n); __cn; \ + __cn = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_DLIST_FOR_EACH_CONTAINER_SAFE(l, c, cn, n) { + * + * } + * + * @param __dl A pointer on a sys_dlist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_dnode_t within the container struct + */ +#define SYS_DLIST_FOR_EACH_CONTAINER_SAFE(__dl, __cn, __cns, __n) \ + for (__cn = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n), \ + __cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n); __cn; \ + __cn = __cns, \ + __cns = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n)) + +/** + * @brief initialize list + * + * @param list the doubly-linked list + * + * @return N/A + */ + +static inline void sys_dlist_init(sys_dlist_t *list) +{ + list->head = (sys_dnode_t *)list; + list->tail = (sys_dnode_t *)list; +} + +#define SYS_DLIST_STATIC_INIT(ptr_to_list) {{(ptr_to_list)}, {(ptr_to_list)}} + +/** + * @brief check if a node is the list's head + * + * @param list the doubly-linked list to operate on + * @param node the node to check + * + * @return 1 if node is the head, 0 otherwise + */ + +static inline int sys_dlist_is_head(sys_dlist_t *list, sys_dnode_t *node) +{ + return list->head == node; +} + +/** + * @brief check if a node is the list's tail + * + * @param list the doubly-linked list to operate on + * @param node the node to check + * + * @return 1 if node is the tail, 0 otherwise + */ + +static inline int sys_dlist_is_tail(sys_dlist_t *list, sys_dnode_t *node) +{ + return list->tail == node; +} + +/** + * @brief check if the list is empty + * + * @param list the doubly-linked list to operate on + * + * @return 1 if empty, 0 otherwise + */ + +static inline int sys_dlist_is_empty(sys_dlist_t *list) +{ + return list->head == list; +} + +/** + * @brief check if more than one node present + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * + * @return 1 if multiple nodes, 0 otherwise + */ + +static inline int sys_dlist_has_multiple_nodes(sys_dlist_t *list) +{ + return list->head != list->tail; +} + +/** + * @brief get a reference to the head item in the list + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the head element, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_peek_head(sys_dlist_t *list) +{ + return sys_dlist_is_empty(list) ? NULL : list->head; +} + +/** + * @brief get a reference to the head item in the list + * + * The list must be known to be non-empty. + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the head element + */ + +static inline sys_dnode_t *sys_dlist_peek_head_not_empty(sys_dlist_t *list) +{ + return list->head; +} + +/** + * @brief get a reference to the next item in the list, node is not NULL + * + * Faster than sys_dlist_peek_next() if node is known not to be NULL. + * + * @param list the doubly-linked list to operate on + * @param node the node from which to get the next element in the list + * + * @return a pointer to the next element from a node, NULL if node is the tail + */ + +static inline sys_dnode_t *sys_dlist_peek_next_no_check(sys_dlist_t *list, + sys_dnode_t *node) +{ + return (node == list->tail) ? NULL : node->next; +} + +/** + * @brief get a reference to the next item in the list + * + * @param list the doubly-linked list to operate on + * @param node the node from which to get the next element in the list + * + * @return a pointer to the next element from a node, NULL if node is the tail + * or NULL (when node comes from reading the head of an empty list). + */ + +static inline sys_dnode_t *sys_dlist_peek_next(sys_dlist_t *list, + sys_dnode_t *node) +{ + return node ? sys_dlist_peek_next_no_check(list, node) : NULL; +} + +/** + * @brief get a reference to the tail item in the list + * + * @param list the doubly-linked list to operate on + * + * @return a pointer to the tail element, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_peek_tail(sys_dlist_t *list) +{ + return sys_dlist_is_empty(list) ? NULL : list->tail; +} + +/** + * @brief add node to tail of list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_append(sys_dlist_t *list, sys_dnode_t *node) +{ + node->next = list; + node->prev = list->tail; + + list->tail->next = node; + list->tail = node; +} + +/** + * @brief add node to head of list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_prepend(sys_dlist_t *list, sys_dnode_t *node) +{ + node->next = list->head; + node->prev = list; + + list->head->prev = node; + list->head = node; +} + +/** + * @brief insert node after a node + * + * Insert a node after a specified node in a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param insert_point the insert point in the list: if NULL, insert at head + * @param node the element to append + * + * @return N/A + */ + +static inline void sys_dlist_insert_after(sys_dlist_t *list, + sys_dnode_t *insert_point, sys_dnode_t *node) +{ + if (!insert_point) { + sys_dlist_prepend(list, node); + } else { + node->next = insert_point->next; + node->prev = insert_point; + insert_point->next->prev = node; + insert_point->next = node; + } +} + +/** + * @brief insert node before a node + * + * Insert a node before a specified node in a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param insert_point the insert point in the list: if NULL, insert at tail + * @param node the element to insert + * + * @return N/A + */ + +static inline void sys_dlist_insert_before(sys_dlist_t *list, + sys_dnode_t *insert_point, sys_dnode_t *node) +{ + if (!insert_point) { + sys_dlist_append(list, node); + } else { + node->prev = insert_point->prev; + node->next = insert_point; + insert_point->prev->next = node; + insert_point->prev = node; + } +} + +/** + * @brief insert node at position + * + * Insert a node in a location depending on a external condition. The cond() + * function checks if the node is to be inserted _before_ the current node + * against which it is checked. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * @param node the element to insert + * @param cond a function that determines if the current node is the correct + * insert point + * @param data parameter to cond() + * + * @return N/A + */ + +static inline void sys_dlist_insert_at(sys_dlist_t *list, sys_dnode_t *node, + int (*cond)(sys_dnode_t *, void *), void *data) +{ + if (sys_dlist_is_empty(list)) { + sys_dlist_append(list, node); + } else { + sys_dnode_t *pos = sys_dlist_peek_head(list); + + while (pos && !cond(pos, data)) { + pos = sys_dlist_peek_next(list, pos); + } + sys_dlist_insert_before(list, pos, node); + } +} + +/** + * @brief remove a specific node from a list + * + * The list is implicit from the node. The node must be part of a list. + * This and other sys_dlist_*() functions are not thread safe. + * + * @param node the node to remove + * + * @return N/A + */ + +static inline void sys_dlist_remove(sys_dnode_t *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +/** + * @brief get the first node in a list + * + * This and other sys_dlist_*() functions are not thread safe. + * + * @param list the doubly-linked list to operate on + * + * @return the first node in the list, NULL if list is empty + */ + +static inline sys_dnode_t *sys_dlist_get(sys_dlist_t *list) +{ + sys_dnode_t *node; + + if (sys_dlist_is_empty(list)) { + return NULL; + } + + node = list->head; + sys_dlist_remove(node); + return node; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _misc_dlist__h_ */ diff --git a/components/ble/ble_stack/common/include/misc/printk.h b/components/ble/ble_stack/common/include/misc/printk.h new file mode 100644 index 00000000..0762ac64 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/printk.h @@ -0,0 +1,29 @@ +/* printk.h - low-level debug output */ + +/* + * Copyright (c) 2010-2012, 2014 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _PRINTK_H_ +#define _PRINTK_H_ + +#include +#include +#include + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#define snprintk snprintf +#define printk printf + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/ble/ble_stack/common/include/misc/slist.h b/components/ble/ble_stack/common/include/misc/slist.h new file mode 100644 index 00000000..d95a9963 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/slist.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Single-linked list implementation + * + * Single-linked list implementation using inline macros/functions. + * This API is not thread safe, and thus if a list is used across threads, + * calls to functions must be protected with synchronization primitives. + */ + +#ifndef __SLIST_H__ +#define __SLIST_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct _snode { + struct _snode *next; +}; + +typedef struct _snode sys_snode_t; + +struct _slist { + sys_snode_t *head; + sys_snode_t *tail; +}; + +typedef struct _slist sys_slist_t; + +/** + * @brief Provide the primitive to iterate on a list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE(l, n) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + */ +#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \ + for (__sn = sys_slist_peek_head(__sl); __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to iterate on a list, from a node in the list + * Note: the loop is unsafe and thus __sn should not be removed + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_ITERATE_FROM_NODE(l, n) { + * + * } + * + * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list + * where to start searching for the next entry from. If NULL, it starts from + * the head. + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * it contains the starting node, or NULL to start from the head + */ +#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \ + for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \ + : sys_slist_peek_head(__sl); \ + __sn; \ + __sn = sys_slist_peek_next(__sn)) + +/** + * @brief Provide the primitive to safely iterate on a list + * Note: __sn can be removed, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) { + * + * } + * + * This and other SYS_SLIST_*() macros are not thread safe. + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __sn A sys_snode_t pointer to peek each node of the list + * @param __sns A sys_snode_t pointer for the loop to run safely + */ +#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \ + for (__sn = sys_slist_peek_head(__sl), \ + __sns = sys_slist_peek_next(__sn); \ + __sn; __sn = __sns, \ + __sns = sys_slist_peek_next(__sn)) + +/* + * @brief Provide the primitive to resolve the container of a list node + * Note: it is safe to use with NULL pointer nodes + * + * @param __ln A pointer on a sys_node_t to get its container + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \ + ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL) +/* + * @brief Provide the primitive to peek container of the list head + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek container of the list tail + * + * @param __sl A pointer on a sys_slist_t to peek + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \ + SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n) + +/* + * @brief Provide the primitive to peek the next container + * + * @param __cn Container struct type pointer + * @param __n The field name of sys_node_t within the container struct + */ + +#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \ + ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \ + __cn, __n) : NULL) + +/** + * @brief Provide the primitive to iterate on a list under a container + * Note: the loop is unsafe and thus __cn should not be detached + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \ + __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Provide the primitive to safely iterate on a list under a container + * Note: __cn can be detached, it will not break the loop. + * + * User _MUST_ add the loop statement curly braces enclosing its own code: + * + * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) { + * + * } + * + * @param __sl A pointer on a sys_slist_t to iterate on + * @param __cn A pointer to peek each entry of the list + * @param __cns A pointer for the loop to run safely + * @param __n The field name of sys_node_t within the container struct + */ +#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \ + for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \ + __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \ + __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n)) + +/** + * @brief Initialize a list + * + * @param list A pointer on the list to initialize + */ +static inline void sys_slist_init(sys_slist_t *list) +{ + list->head = NULL; + list->tail = NULL; +} + +#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL} + +/** + * @brief Test if the given list is empty + * + * @param list A pointer on the list to test + * + * @return a boolean, true if it's empty, false otherwise + */ +static inline bool sys_slist_is_empty(sys_slist_t *list) +{ + return (!list->head); +} + +/** + * @brief Peek the first node from the list + * + * @param list A point on the list to peek the first node from + * + * @return A pointer on the first node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list) +{ + return list->head; +} + +/** + * @brief Peek the last node from the list + * + * @param list A point on the list to peek the last node from + * + * @return A pointer on the last node of the list (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list) +{ + return list->tail; +} + +/** + * @brief Peek the next node from current node, node is not NULL + * + * Faster then sys_slist_peek_next() if node is known not to be NULL. + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node) +{ + return node->next; +} + +/** + * @brief Peek the next node from current node + * + * @param node A pointer on the node where to peek the next node + * + * @return a pointer on the next node (or NULL if none) + */ +static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node) +{ + return node ? sys_slist_peek_next_no_check(node) : NULL; +} + +/** + * @brief Prepend a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to prepend + */ +static inline void sys_slist_prepend(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = list->head; + list->head = node; + + if (!list->tail) { + list->tail = list->head; + } +} + +/** + * @brief Append a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to append + */ +static inline void sys_slist_append(sys_slist_t *list, + sys_snode_t *node) +{ + node->next = NULL; + + if (!list->tail) { + list->tail = node; + list->head = node; + } else { + list->tail->next = node; + list->tail = node; + } +} + +/** + * @brief Append a list to the given list + * + * Append a singly-linked, NULL-terminated list consisting of nodes containing + * the pointer to the next node as the first element of a node, to @a list. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param head A pointer to the first element of the list to append + * @param tail A pointer to the last element of the list to append + */ +static inline void sys_slist_append_list(sys_slist_t *list, + void *head, void *tail) +{ + if (!list->tail) { + list->head = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } else { + list->tail->next = (sys_snode_t *)head; + list->tail = (sys_snode_t *)tail; + } +} + +/** + * @brief merge two slists, appending the second one to the first + * + * When the operation is completed, the appending list is empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param list_to_append A pointer to the list to append. + */ +static inline void sys_slist_merge_slist(sys_slist_t *list, + sys_slist_t *list_to_append) +{ + sys_slist_append_list(list, list_to_append->head, + list_to_append->tail); + sys_slist_init(list_to_append); +} + +/** + * @brief Insert a node to the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev A pointer on the previous node + * @param node A pointer on the node to insert + */ +static inline void sys_slist_insert(sys_slist_t *list, + sys_snode_t *prev, + sys_snode_t *node) +{ + if (!prev) { + sys_slist_prepend(list, node); + } else if (!prev->next) { + sys_slist_append(list, node); + } else { + node->next = prev->next; + prev->next = node; + } +} + +/** + * @brief Fetch and remove the first node of the given list + * + * List must be known to be non-empty. + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list + */ +static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list) +{ + sys_snode_t *node = list->head; + + list->head = node->next; + if (list->tail == node) { + list->tail = list->head; + } + + return node; +} + +/** + * @brief Fetch and remove the first node of the given list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * + * @return A pointer to the first node of the list (or NULL if empty) + */ +static inline sys_snode_t *sys_slist_get(sys_slist_t *list) +{ + return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list); +} + +/** + * @brief Remove a node + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param prev_node A pointer on the previous node + * (can be NULL, which means the node is the list's head) + * @param node A pointer on the node to remove + */ +static inline void sys_slist_remove(sys_slist_t *list, + sys_snode_t *prev_node, + sys_snode_t *node) +{ + if (!prev_node) { + list->head = node->next; + + /* Was node also the tail? */ + if (list->tail == node) { + list->tail = list->head; + } + } else { + prev_node->next = node->next; + + /* Was node the tail? */ + if (list->tail == node) { + list->tail = prev_node; + } + } + + node->next = NULL; +} + +/** + * @brief Find and remove a node from a list + * + * This and other sys_slist_*() functions are not thread safe. + * + * @param list A pointer on the list to affect + * @param node A pointer on the node to remove from the list + * + * @return true if node was removed + */ +static inline bool sys_slist_find_and_remove(sys_slist_t *list, + sys_snode_t *node) +{ + sys_snode_t *prev = NULL; + sys_snode_t *test; + + SYS_SLIST_FOR_EACH_NODE(list, test) { + if (test == node) { + sys_slist_remove(list, prev, node); + return true; + } + + prev = test; + } + + return false; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* __SLIST_H__ */ diff --git a/components/ble/ble_stack/common/include/misc/stack.h b/components/ble/ble_stack/common/include/misc/stack.h new file mode 100644 index 00000000..6a745535 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/stack.h @@ -0,0 +1,87 @@ +/** + * @file stack.h + * Stack usage analysis helpers + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _MISC_STACK_H_ +#define _MISC_STACK_H_ + +#include + +#if defined(CONFIG_INIT_STACKS) +static inline size_t stack_unused_space_get(const char *stack, size_t size) +{ + size_t unused = 0; + int i; + +#ifdef CONFIG_STACK_SENTINEL + /* First 4 bytes of the stack buffer reserved for the sentinel + * value, it won't be 0xAAAAAAAA for thread stacks. + */ + stack += 4; +#endif + + /* TODO Currently all supported platforms have stack growth down and + * there is no Kconfig option to configure it so this always build + * "else" branch. When support for platform with stack direction up + * (or configurable direction) is added this check should be confirmed + * that correct Kconfig option is used. + */ +#if defined(STACK_GROWS_UP) + for (i = size - 1; i >= 0; i--) { + if ((unsigned char)stack[i] == 0xaa) { + unused++; + } else { + break; + } + } +#else + for (i = 0; i < size; i++) { + if ((unsigned char)stack[i] == 0xaa) { + unused++; + } else { + break; + } + } +#endif + return unused; +} +#else +static inline size_t stack_unused_space_get(const char *stack, size_t size) +{ + return 0; +} +#endif + +#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_PRINTK) +static inline void stack_analyze(const char *name, const char *stack, + unsigned int size) +{ + unsigned int pcnt, unused = 0; + + unused = stack_unused_space_get(stack, size); + + /* Calculate the real size reserved for the stack */ + pcnt = ((size - unused) * 100) / size; + + printk("%s (real size %u):\tunused %u\tusage %u / %u (%u %%)\n", name, + size, unused, size - unused, size, pcnt); +} +#else +static inline void stack_analyze(const char *name, const char *stack, + unsigned int size) +{ +} +#endif + +#define STACK_ANALYZE(name, sym) \ + stack_analyze(name, K_THREAD_STACK_BUFFER(sym), \ + K_THREAD_STACK_SIZEOF(sym)) + +#endif /* _MISC_STACK_H_ */ diff --git a/components/ble/ble_stack/common/include/misc/util.h b/components/ble/ble_stack/common/include/misc/util.h new file mode 100644 index 00000000..d5142c43 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/util.h @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Misc utilities + * + * Misc utilities usable by the kernel and application code. + */ + +#ifndef _UTIL__H_ +#define _UTIL__H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ASMLANGUAGE + +#include +#if defined(BFLB_BLE) +#include +#include "utils_string.h" +#endif + +/* Helper to pass a int as a pointer or vice-versa. + * Those are available for 32 bits architectures: + */ +#define POINTER_TO_UINT(x) ((u32_t) (x)) +#define UINT_TO_POINTER(x) ((void *) (x)) +#define POINTER_TO_INT(x) ((s32_t) (x)) +#define INT_TO_POINTER(x) ((void *) (x)) + +/* Evaluates to 0 if cond is true-ish; compile error otherwise */ +#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1) + +/* Evaluates to 0 if array is an array; compile error if not array (e.g. + * pointer) + */ +#define IS_ARRAY(array) \ + ZERO_OR_COMPILE_ERROR( \ + !__builtin_types_compatible_p(__typeof__(array), \ + __typeof__(&(array)[0]))) + +/* Evaluates to number of elements in an array; compile error if not + * an array (e.g. pointer) + */ +#define ARRAY_SIZE(array) \ + ((unsigned long) (IS_ARRAY(array) + \ + (sizeof(array) / sizeof((array)[0])))) + +/* Evaluates to 1 if ptr is part of array, 0 otherwise; compile error if + * "array" argument is not an array (e.g. "ptr" and "array" mixed up) + */ +#define PART_OF_ARRAY(array, ptr) \ + ((ptr) && ((ptr) >= &array[0] && (ptr) < &array[ARRAY_SIZE(array)])) + +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + +/* round "x" up/down to next multiple of "align" (which must be a power of 2) */ +#define ROUND_UP(x, align) \ + (((unsigned long)(x) + ((unsigned long)align - 1)) & \ + ~((unsigned long)align - 1)) +#define ROUND_DOWN(x, align) ((unsigned long)(x) & ~((unsigned long)align - 1)) + +#define ceiling_fraction(numerator, divider) \ + (((numerator) + ((divider) - 1)) / (divider)) + +#ifdef INLINED +#define INLINE inline +#else +#define INLINE +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +void get_bytearray_from_string(char** params, uint8_t *result,int array_size); +void get_uint8_from_string(char** params, uint8_t *result); +void get_uint16_from_string(char** params, uint16_t *result); +void get_uint32_from_string(char** params, uint32_t *result); +void reverse_bytearray(uint8_t *src, uint8_t *result, int array_size); +void reverse_bytearray(uint8_t *src, uint8_t *result, int array_size); +unsigned int find_lsb_set(uint32_t data); + +static inline int is_power_of_two(unsigned int x) +{ + return (x != 0) && !(x & (x - 1)); +} + +static inline s64_t arithmetic_shift_right(s64_t value, u8_t shift) +{ + s64_t sign_ext; + + if (shift == 0) { + return value; + } + + /* extract sign bit */ + sign_ext = (value >> 63) & 1; + + /* make all bits of sign_ext be the same as the value's sign bit */ + sign_ext = -sign_ext; + + /* shift value and fill opened bit positions with sign bit */ + return (value >> shift) | (sign_ext << (64 - shift)); +} + +#endif /* !_ASMLANGUAGE */ + +/* KB, MB, GB */ +#define KB(x) ((x) << 10) +#define MB(x) (KB(x) << 10) +#define GB(x) (MB(x) << 10) + +/* KHZ, MHZ */ +#define KHZ(x) ((x) * 1000) +#define MHZ(x) (KHZ(x) * 1000) + +#define BIT_MASK(n) (BIT(n) - 1) + +/** + * @brief Check for macro definition in compiler-visible expressions + * + * This trick was pioneered in Linux as the config_enabled() macro. + * The madness has the effect of taking a macro value that may be + * defined to "1" (e.g. CONFIG_MYFEATURE), or may not be defined at + * all and turning it into a literal expression that can be used at + * "runtime". That is, it works similarly to + * "defined(CONFIG_MYFEATURE)" does except that it is an expansion + * that can exist in a standard expression and be seen by the compiler + * and optimizer. Thus much ifdef usage can be replaced with cleaner + * expressions like: + * + * if (IS_ENABLED(CONFIG_MYFEATURE)) + * myfeature_enable(); + * + * INTERNAL + * First pass just to expand any existing macros, we need the macro + * value to be e.g. a literal "1" at expansion time in the next macro, + * not "(1)", etc... Standard recursive expansion does not work. + */ +#define IS_ENABLED(config_macro) _IS_ENABLED1(config_macro) + +/* Now stick on a "_XXXX" prefix, it will now be "_XXXX1" if config_macro + * is "1", or just "_XXXX" if it's undefined. + * ENABLED: _IS_ENABLED2(_XXXX1) + * DISABLED _IS_ENABLED2(_XXXX) + */ +#define _IS_ENABLED1(config_macro) _IS_ENABLED2(_XXXX##config_macro) + +/* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string + * with a trailing comma), so it has the effect of making this a + * two-argument tuple to the preprocessor only in the case where the + * value is defined to "1" + * ENABLED: _YYYY, <--- note comma! + * DISABLED: _XXXX + */ +#define _XXXX1 _YYYY, + +/* Then we append an extra argument to fool the gcc preprocessor into + * accepting it as a varargs macro. + * arg1 arg2 arg3 + * ENABLED: _IS_ENABLED3(_YYYY, 1, 0) + * DISABLED _IS_ENABLED3(_XXXX 1, 0) + */ +#define _IS_ENABLED2(one_or_two_args) _IS_ENABLED3(one_or_two_args 1, 0) + +/* And our second argument is thus now cooked to be 1 in the case + * where the value is defined to 1, and 0 if not: + */ +#define _IS_ENABLED3(ignore_this, val, ...) val + +/** + * Macros for doing code-generation with the preprocessor. + * + * Generally it is better to generate code with the preprocessor than + * to copy-paste code or to generate code with the build system / + * python script's etc. + * + * http://stackoverflow.com/a/12540675 + */ +#define UTIL_EMPTY(...) +#define UTIL_DEFER(...) __VA_ARGS__ UTIL_EMPTY() +#define UTIL_OBSTRUCT(...) __VA_ARGS__ UTIL_DEFER(UTIL_EMPTY)() +#define UTIL_EXPAND(...) __VA_ARGS__ + +#define UTIL_EVAL(...) UTIL_EVAL1(UTIL_EVAL1(UTIL_EVAL1(__VA_ARGS__))) +#define UTIL_EVAL1(...) UTIL_EVAL2(UTIL_EVAL2(UTIL_EVAL2(__VA_ARGS__))) +#define UTIL_EVAL2(...) UTIL_EVAL3(UTIL_EVAL3(UTIL_EVAL3(__VA_ARGS__))) +#define UTIL_EVAL3(...) UTIL_EVAL4(UTIL_EVAL4(UTIL_EVAL4(__VA_ARGS__))) +#define UTIL_EVAL4(...) UTIL_EVAL5(UTIL_EVAL5(UTIL_EVAL5(__VA_ARGS__))) +#define UTIL_EVAL5(...) __VA_ARGS__ + +#define UTIL_CAT(a, ...) UTIL_PRIMITIVE_CAT(a, __VA_ARGS__) +#define UTIL_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ + +#define UTIL_INC(x) UTIL_PRIMITIVE_CAT(UTIL_INC_, x) +#define UTIL_INC_0 1 +#define UTIL_INC_1 2 +#define UTIL_INC_2 3 +#define UTIL_INC_3 4 +#define UTIL_INC_4 5 +#define UTIL_INC_5 6 +#define UTIL_INC_6 7 +#define UTIL_INC_7 8 +#define UTIL_INC_8 9 +#define UTIL_INC_9 10 +#define UTIL_INC_10 11 +#define UTIL_INC_11 12 +#define UTIL_INC_12 13 +#define UTIL_INC_13 14 +#define UTIL_INC_14 15 +#define UTIL_INC_15 16 +#define UTIL_INC_16 17 +#define UTIL_INC_17 18 +#define UTIL_INC_18 19 +#define UTIL_INC_19 19 + +#define UTIL_DEC(x) UTIL_PRIMITIVE_CAT(UTIL_DEC_, x) +#define UTIL_DEC_0 0 +#define UTIL_DEC_1 0 +#define UTIL_DEC_2 1 +#define UTIL_DEC_3 2 +#define UTIL_DEC_4 3 +#define UTIL_DEC_5 4 +#define UTIL_DEC_6 5 +#define UTIL_DEC_7 6 +#define UTIL_DEC_8 7 +#define UTIL_DEC_9 8 +#define UTIL_DEC_10 9 +#define UTIL_DEC_11 10 +#define UTIL_DEC_12 11 +#define UTIL_DEC_13 12 +#define UTIL_DEC_14 13 +#define UTIL_DEC_15 14 +#define UTIL_DEC_16 15 +#define UTIL_DEC_17 16 +#define UTIL_DEC_18 17 +#define UTIL_DEC_19 18 + +#define UTIL_CHECK_N(x, n, ...) n +#define UTIL_CHECK(...) UTIL_CHECK_N(__VA_ARGS__, 0,) + +#define UTIL_NOT(x) UTIL_CHECK(UTIL_PRIMITIVE_CAT(UTIL_NOT_, x)) +#define UTIL_NOT_0 ~, 1, + +#define UTIL_COMPL(b) UTIL_PRIMITIVE_CAT(UTIL_COMPL_, b) +#define UTIL_COMPL_0 1 +#define UTIL_COMPL_1 0 + +#define UTIL_BOOL(x) UTIL_COMPL(UTIL_NOT(x)) + +#define UTIL_IIF(c) UTIL_PRIMITIVE_CAT(UTIL_IIF_, c) +#define UTIL_IIF_0(t, ...) __VA_ARGS__ +#define UTIL_IIF_1(t, ...) t + +#define UTIL_IF(c) UTIL_IIF(UTIL_BOOL(c)) + +#define UTIL_EAT(...) +#define UTIL_EXPAND(...) __VA_ARGS__ +#define UTIL_WHEN(c) UTIL_IF(c)(UTIL_EXPAND, UTIL_EAT) + +#define UTIL_REPEAT(count, macro, ...) \ + UTIL_WHEN(count) \ + ( \ + UTIL_OBSTRUCT(UTIL_REPEAT_INDIRECT) () \ + ( \ + UTIL_DEC(count), macro, __VA_ARGS__ \ + ) \ + UTIL_OBSTRUCT(macro) \ + ( \ + UTIL_DEC(count), __VA_ARGS__ \ + ) \ + ) +#define UTIL_REPEAT_INDIRECT() UTIL_REPEAT + +/** + * Generates a sequence of code. + * Useful for generating code like; + * + * NRF_PWM0, NRF_PWM1, NRF_PWM2, + * + * @arg LEN: The length of the sequence. Must be defined and less than + * 20. + * + * @arg F(i, F_ARG): A macro function that accepts two arguments. + * F is called repeatedly, the first argument + * is the index in the sequence, and the second argument is the third + * argument given to UTIL_LISTIFY. + * + * Example: + * + * \#define FOO(i, _) NRF_PWM ## i , + * { UTIL_LISTIFY(PWM_COUNT, FOO) } + * // The above two lines will generate the below: + * { NRF_PWM0 , NRF_PWM1 , } + * + * @note Calling UTIL_LISTIFY with undefined arguments has undefined + * behaviour. + */ +#define UTIL_LISTIFY(LEN, F, F_ARG) UTIL_EVAL(UTIL_REPEAT(LEN, F, F_ARG)) + +#if defined(BFLB_BLE) +/** + * @brief Convert a single character into a hexadecimal nibble. + * + * @param[in] c The character to convert + * @param x The address of storage for the converted number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int char2hex(char c, u8_t *x); + +/** + * @brief Convert a single hexadecimal nibble into a character. + * + * @param[in] c The number to convert + * @param x The address of storage for the converted character. + * + * @return Zero on success or (negative) error code otherwise. + */ +int hex2char(u8_t x, char *c); + +/** + * @brief Convert a binary array into string representation. + * + * @param[in] buf The binary array to convert + * @param[in] buflen The length of the binary array to convert + * @param[out] hex Address of where to store the string representation. + * @param[in] hexlen Size of the storage area for string representation. + * + * @return The length of the converted string, or 0 if an error occurred. + */ +size_t bin2hex(const u8_t *buf, size_t buflen, char *hex, size_t hexlen); + +/* + * Convert hex string to byte string + * Return number of bytes written to buf, or 0 on error + * @return The length of the converted array, or 0 if an error occurred. + */ + +/** + * @brief Convert a hexadecimal string into a binary array. + * + * @param[in] hex The hexadecimal string to convert + * @param[in] hexlen The length of the hexadecimal string to convert. + * @param[out] buf Address of where to store the binary data + * @param[in] buflen Size of the storage area for binary data + * + * @return The length of the binary array , or 0 if an error occurred. + */ +size_t hex2bin(const char *hex, size_t hexlen, u8_t *buf, size_t buflen); + +/** + * @brief Convert a u8_t into decimal string representation. + * + * Convert a u8_t value into ASCII decimal string representation. + * The string is terminated if there is enough space in buf. + * + * @param[out] buf Address of where to store the string representation. + * @param[in] buflen Size of the storage area for string representation. + * @param[in] value The value to convert to decimal string + * + * @return The length of the converted string (excluding terminator if + * any), or 0 if an error occurred. + */ +u8_t u8_to_dec(char *buf, u8_t buflen, u8_t value); +#endif //#if defined(BFLB_BLE) +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL__H_ */ diff --git a/components/ble/ble_stack/common/include/misc/utils_string.h b/components/ble/ble_stack/common/include/misc/utils_string.h new file mode 100644 index 00000000..2d9fecb8 --- /dev/null +++ b/components/ble/ble_stack/common/include/misc/utils_string.h @@ -0,0 +1,7 @@ +#ifndef __UTILS_STRING_H__ +#define __UTILS_STRING_H__ +void get_bytearray_from_string(char** params, uint8_t *result,int array_size); +void get_uint8_from_string(char** params, uint8_t *result); +void get_uint16_from_string(char** params, uint16_t *result); +void get_uint32_from_string(char** params, uint32_t *result); +#endif diff --git a/components/ble/ble_stack/common/include/net/buf.h b/components/ble/ble_stack/common/include/net/buf.h new file mode 100644 index 00000000..aada4b70 --- /dev/null +++ b/components/ble/ble_stack/common/include/net/buf.h @@ -0,0 +1,1542 @@ +/** @file + * @brief Buffer management. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __NET_BUF_H +#define __NET_BUF_H + +#include +#include +#include +#include +#include "../../port/include/config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Network buffer library + * @defgroup net_buf Network Buffer Library + * @ingroup networking + * @{ + */ + +/* Alignment needed for various parts of the buffer definition */ +#define __net_buf_align __aligned(sizeof(int)) + +/** @def NET_BUF_SIMPLE_DEFINE + * @brief Define a net_buf_simple stack variable. + * + * This is a helper macro which is used to define a net_buf_simple object + * on the stack. + * + * @param _name Name of the net_buf_simple object. + * @param _size Maximum data storage for the buffer. + */ +#define NET_BUF_SIMPLE_DEFINE(_name, _size) \ + u8_t net_buf_data_##_name[_size]; \ + struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } + +/** @def NET_BUF_SIMPLE_DEFINE_STATIC + * @brief Define a static net_buf_simple variable. + * + * This is a helper macro which is used to define a static net_buf_simple + * object. + * + * @param _name Name of the net_buf_simple object. + * @param _size Maximum data storage for the buffer. + */ +#define NET_BUF_SIMPLE_DEFINE_STATIC(_name, _size) \ + static /*__noinit*/ u8_t net_buf_data_##_name[_size]; \ + static struct net_buf_simple _name = { \ + .data = net_buf_data_##_name, \ + .len = 0, \ + .size = _size, \ + .__buf = net_buf_data_##_name, \ + } + +/** @brief Simple network buffer representation. + * + * This is a simpler variant of the net_buf object (in fact net_buf uses + * net_buf_simple internally). It doesn't provide any kind of reference + * counting, user data, dynamic allocation, or in general the ability to + * pass through kernel objects such as FIFOs. + * + * The main use of this is for scenarios where the meta-data of the normal + * net_buf isn't needed and causes too much overhead. This could be e.g. + * when the buffer only needs to be allocated on the stack or when the + * access to and lifetime of the buffer is well controlled and constrained. + * + */ +struct net_buf_simple { + /** Pointer to the start of data in the buffer. */ + u8_t *data; + + /** Length of the data behind the data pointer. */ + u16_t len; + + /** Amount of data that this buffer can store. */ + u16_t size; + + /** Start of the data storage. Not to be accessed directly + * (the data pointer should be used instead). + */ + u8_t *__buf; +}; + +/** @def NET_BUF_SIMPLE + * @brief Define a net_buf_simple stack variable and get a pointer to it. + * + * This is a helper macro which is used to define a net_buf_simple object on + * the stack and the get a pointer to it as follows: + * + * struct net_buf_simple *my_buf = NET_BUF_SIMPLE(10); + * + * After creating the object it needs to be initialized by calling + * net_buf_simple_init(). + * + * @param _size Maximum data storage for the buffer. + * + * @return Pointer to stack-allocated net_buf_simple object. + */ +#define NET_BUF_SIMPLE(_size) \ + ((struct net_buf_simple *)(&(struct { \ + struct net_buf_simple buf; \ + u8_t data[_size] __net_buf_align; \ + }) { \ + .buf.size = _size, \ + })) + +/** @brief Initialize a net_buf_simple object. + * + * This needs to be called after creating a net_buf_simple object using + * the NET_BUF_SIMPLE macro. + * + * @param buf Buffer to initialize. + * @param reserve_head Headroom to reserve. + */ +static inline void net_buf_simple_init(struct net_buf_simple *buf, + size_t reserve_head) +{ + if (!buf->__buf) { + buf->__buf = (u8_t *)buf + sizeof(*buf); + } + + buf->data = buf->__buf + reserve_head; + buf->len = 0; +} + +/** + * @brief Initialize a net_buf_simple object with data. + * + * Initialized buffer object with external data. + * + * @param buf Buffer to initialize. + * @param data External data pointer + * @param size Amount of data the pointed data buffer if able to fit. + */ +void net_buf_simple_init_with_data(struct net_buf_simple *buf, + void *data, size_t size); + +/** + + * @brief Reset buffer + * + * Reset buffer data so it can be reused for other purposes. + * + * @param buf Buffer to reset. + */ +static inline void net_buf_simple_reset(struct net_buf_simple *buf) +{ + buf->len = 0; + buf->data = buf->__buf; +} + +/** + * Clone buffer state, using the same data buffer. + * + * Initializes a buffer to point to the same data as an existing buffer. + * Allows operations on the same data without altering the length and + * offset of the original. + * + * @param original Buffer to clone. + * @param clone The new clone. + */ +void net_buf_simple_clone(const struct net_buf_simple *original, + struct net_buf_simple *clone); + +/** + * @brief Prepare data to be added at the end of the buffer + * + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param len Number of bytes to increment the length with. + * + * @return The original tail of the buffer. + */ +void *net_buf_simple_add(struct net_buf_simple *buf, size_t len); + +/** + * @brief Copy bytes from memory to the end of the buffer + * + * Copies the given number of bytes to the end of the buffer. Increments the + * data length of the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param mem Location of data to be added. + * @param len Length of data to be added + * + * @return The original tail of the buffer. + */ +void *net_buf_simple_add_mem(struct net_buf_simple *buf, const void *mem, + size_t len); + +/** + * @brief Add (8-bit) byte at the end of the buffer + * + * Adds a byte at the end of the buffer. Increments the data length of + * the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param val byte value to be added. + * + * @return Pointer to the value added + */ +u8_t *net_buf_simple_add_u8(struct net_buf_simple *buf, u8_t val); + +/** + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +void net_buf_simple_add_le16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +void net_buf_simple_add_be16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Add 24-bit value at the end of the buffer + * + * Adds 24-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 24-bit value to be added. + */ +void net_buf_simple_add_le24(struct net_buf_simple *buf, uint32_t val); + +/** + * @brief Add 24-bit value at the end of the buffer + * + * Adds 24-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 24-bit value to be added. + */ +void net_buf_simple_add_be24(struct net_buf_simple *buf, uint32_t val); + +/** + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +void net_buf_simple_add_le32(struct net_buf_simple *buf, u32_t val); + +/** + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +void net_buf_simple_add_be32(struct net_buf_simple *buf, u32_t val); + +/** + * @brief Push data to the beginning of the buffer. + * + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * @param buf Buffer to update. + * @param len Number of bytes to add to the beginning. + * + * @return The new beginning of the buffer data. + */ +void *net_buf_simple_push(struct net_buf_simple *buf, size_t len); + +/** + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_le16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in big endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_be16(struct net_buf_simple *buf, u16_t val); + +/** + * @brief Push 8-bit value to the beginning of the buffer + * + * Adds 8-bit value the beginning of the buffer. + * + * @param buf Buffer to update. + * @param val 8-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_u8(struct net_buf_simple *buf, u8_t val); + +/** + * @brief Push 24-bit value to the beginning of the buffer + * + * Adds 24-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 24-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_le24(struct net_buf_simple *buf, uint32_t val); + +/** + * @brief Push 24-bit value to the beginning of the buffer + * + * Adds 24-bit value in big endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 24-bit value to be pushed to the buffer. + */ +void net_buf_simple_push_be24(struct net_buf_simple *buf, uint32_t val); + +/** + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return New beginning of the buffer data. + */ +void *net_buf_simple_pull(struct net_buf_simple *buf, size_t len); + +/** + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return Pointer to the old location of the buffer data. + */ +void *net_buf_simple_pull_mem(struct net_buf_simple *buf, size_t len); + +/** + * @brief Remove a 8-bit value from the beginning of the buffer + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 8-bit values. + * + * @param buf A valid pointer on a buffer. + * + * @return The 8-bit removed value + */ +u8_t net_buf_simple_pull_u8(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 16-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from little endian to host endian. + */ +u16_t net_buf_simple_pull_le16(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 16-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from big endian to host endian. + */ +u16_t net_buf_simple_pull_be16(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 32-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from little endian to host endian. + */ +u32_t net_buf_simple_pull_le32(struct net_buf_simple *buf); + +/** + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_simple_pull(), but a helper for operating + * on 32-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from big endian to host endian. + */ +u32_t net_buf_simple_pull_be32(struct net_buf_simple *buf); + +/** + * @brief Get the tail pointer for a buffer. + * + * Get a pointer to the end of the data in a buffer. + * + * @param buf Buffer. + * + * @return Tail pointer for the buffer. + */ +static inline u8_t *net_buf_simple_tail(struct net_buf_simple *buf) +{ + return buf->data + buf->len; +} + +/** + * @brief Check buffer headroom. + * + * Check how much free space there is in the beginning of the buffer. + * + * buf A valid pointer on a buffer + * + * @return Number of bytes available in the beginning of the buffer. + */ +size_t net_buf_simple_headroom(struct net_buf_simple *buf); + +/** + * @brief Check buffer tailroom. + * + * Check how much free space there is at the end of the buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Number of bytes available at the end of the buffer. + */ +size_t net_buf_simple_tailroom(struct net_buf_simple *buf); + +/** + * @brief Parsing state of a buffer. + * + * This is used for temporarily storing the parsing state of a buffer + * while giving control of the parsing to a routine which we don't + * control. + */ +struct net_buf_simple_state { + /** Offset of the data pointer from the beginning of the storage */ + u16_t offset; + /** Length of data */ + u16_t len; +}; + +/** + * @brief Save the parsing state of a buffer. + * + * Saves the parsing state of a buffer so it can be restored later. + * + * @param buf Buffer from which the state should be saved. + * @param state Storage for the state. + */ +static inline void net_buf_simple_save(struct net_buf_simple *buf, + struct net_buf_simple_state *state) +{ + state->offset = net_buf_simple_headroom(buf); + state->len = buf->len; +} + +/** + * @brief Restore the parsing state of a buffer. + * + * Restores the parsing state of a buffer from a state previously stored + * by net_buf_simple_save(). + * + * @param buf Buffer to which the state should be restored. + * @param state Stored state. + */ +static inline void net_buf_simple_restore(struct net_buf_simple *buf, + struct net_buf_simple_state *state) +{ + buf->data = buf->__buf + state->offset; + buf->len = state->len; +} + +/** Flag indicating that the buffer has associated fragments. Only used + * internally by the buffer handling code while the buffer is inside a + * FIFO, meaning this never needs to be explicitly set or unset by the + * net_buf API user. As long as the buffer is outside of a FIFO, i.e. + * in practice always for the user for this API, the buf->frags pointer + * should be used instead. + */ +#define NET_BUF_FRAGS BIT(0) +/** Flag indicating that the buffer's associated data pointer, points to + * externally allocated memory. Therefore once ref goes down to zero, the + * pointed data will not need to be deallocated. This never needs to be + * explicitly set or unet by the net_buf API user. Such net_buf is + * exclusively instantiated via net_buf_alloc_with_data() function. + * Reference count mechanism however will behave the same way, and ref + * count going to 0 will free the net_buf but no the data pointer in it. + */ +#define NET_BUF_EXTERNAL_DATA BIT(1) + +/** @brief Network buffer representation. + * + * This struct is used to represent network buffers. Such buffers are + * normally defined through the NET_BUF_POOL_*_DEFINE() APIs and allocated + * using the net_buf_alloc() API. + */ +struct net_buf { + union { + /** Allow placing the buffer into sys_slist_t */ + sys_snode_t node; + + /** Fragments associated with this buffer. */ + struct net_buf *frags; + }; + + /** Reference count. */ + u8_t ref; + + /** Bit-field of buffer flags. */ + u8_t flags; + + /** Where the buffer should go when freed up. */ + u8_t pool_id; + + /* Union for convenience access to the net_buf_simple members, also + * preserving the old API. + */ + union { + /* The ABI of this struct must match net_buf_simple */ + struct { + /** Pointer to the start of data in the buffer. */ + u8_t *data; + + /** Length of the data behind the data pointer. */ + u16_t len; + + /** Amount of data that this buffer can store. */ + u16_t size; + + /** Start of the data storage. Not to be accessed + * directly (the data pointer should be used + * instead). + */ + u8_t *__buf; + }; + + struct net_buf_simple b; + }; + + /** System metadata for this buffer. */ + u8_t user_data[CONFIG_NET_BUF_USER_DATA_SIZE] __net_buf_align; +}; + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +typedef void (*destroy_cb_t)(struct net_buf *buf); +#endif + +struct net_buf_data_cb { + u8_t * (*alloc)(struct net_buf *buf, size_t *size, s32_t timeout); + u8_t * (*ref)(struct net_buf *buf, u8_t *data); + void (*unref)(struct net_buf *buf, u8_t *data); +}; + +struct net_buf_data_alloc { + const struct net_buf_data_cb *cb; + void *alloc_data; +}; + +struct net_buf_pool { + /** LIFO to place the buffer into when free */ + struct k_lifo free; + + /** Number of buffers in pool */ + #if defined(BFLB_DYNAMIC_ALLOC_MEM) + u16_t buf_count; + #else + const u16_t buf_count; + #endif + /** Number of uninitialized buffers */ + u16_t uninit_count; + +#if defined(CONFIG_NET_BUF_POOL_USAGE) + /** Amount of available buffers in the pool. */ + s16_t avail_count; + + /** Total size of the pool. */ + const u16_t pool_size; + + /** Name of the pool. Used when printing pool information. */ + const char *name; +#endif /* CONFIG_NET_BUF_POOL_USAGE */ + #if defined(BFLB_DYNAMIC_ALLOC_MEM) + /** Optional destroy callback when buffer is freed. */ + void (*destroy)(struct net_buf *buf); + + /** Data allocation handlers. */ + struct net_buf_data_alloc *alloc; + + /** Start of buffer storage array */ + struct net_buf * __bufs; + #else + /** Optional destroy callback when buffer is freed. */ + void (*const destroy)(struct net_buf *buf); + + /** Data allocation handlers. */ + const struct net_buf_data_alloc *alloc; + + /** Start of buffer storage array */ + struct net_buf * const __bufs; + #endif +}; + +#if defined(CONFIG_NET_BUF_POOL_USAGE) +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ + { \ + .alloc = _alloc, \ + .free = _K_LIFO_INITIALIZER(_pool.free), \ + .__bufs = _bufs, \ + .buf_count = _count, \ + .uninit_count = _count, \ + .avail_count = _count, \ + .destroy = _destroy, \ + .name = STRINGIFY(_pool), \ + } +#else +#define NET_BUF_POOL_INITIALIZER(_pool, _alloc, _bufs, _count, _destroy) \ + { \ + .alloc = _alloc, \ + .free = _K_LIFO_INITIALIZER(_pool.free), \ + .__bufs = _bufs, \ + .buf_count = _count, \ + .uninit_count = _count, \ + .destroy = _destroy, \ + } +#endif /* CONFIG_NET_BUF_POOL_USAGE */ + +extern const struct net_buf_data_alloc net_buf_heap_alloc; + +/** @def NET_BUF_POOL_HEAP_DEFINE + * @brief Define a new pool for buffers using the heap for the data. + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this, the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * The data payload of the buffers will be allocated from the heap using + * k_malloc, so CONFIG_HEAP_MEM_POOL_SIZE must be set to a positive value. + * This kind of pool does not support blocking on the data allocation, so + * the timeout passed to net_buf_alloc will be always treated as K_NO_WAIT + * when trying to allocate the data. This means that allocation failures, + * i.e. NULL returns, must always be handled cleanly. + * + * If provided with a custom destroy callback, this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_HEAP_DEFINE(_name, _count, _destroy) \ + static struct net_buf net_buf_##_name[_count] __noinit; \ + struct net_buf_pool _name __net_buf_align \ + __in_section(_net_buf_pool, static, _name) = \ + NET_BUF_POOL_INITIALIZER(_name, &net_buf_heap_alloc, \ + net_buf_##_name, _count, _destroy) + +struct net_buf_pool_fixed { + size_t data_size; + u8_t *data_pool; +}; + +extern const struct net_buf_data_cb net_buf_fixed_cb; + +/** @def NET_BUF_POOL_FIXED_DEFINE + * @brief Define a new pool for buffers based on fixed-size data + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this, the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * The data payload of the buffers will be allocated from a byte array + * of fixed sized chunks. This kind of pool does not support blocking on + * the data allocation, so the timeout passed to net_buf_alloc will be + * always treated as K_NO_WAIT when trying to allocate the data. This means + * that allocation failures, i.e. NULL returns, must always be handled + * cleanly. + * + * If provided with a custom destroy callback, this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _data_size Maximum data payload per buffer. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +#define NET_BUF_POOL_FIXED_DEFINE(_name, _count, _data_size, _destroy) \ + static struct net_buf net_buf_##_name[_count]; \ + static u8_t net_buf_data_##_name[_count][_data_size]; \ + static const struct net_buf_pool_fixed net_buf_fixed_##_name = { \ + .data_size = _data_size, \ + .data_pool = (u8_t *)net_buf_data_##_name, \ + }; \ + static const struct net_buf_data_alloc net_buf_fixed_alloc_##_name = {\ + .cb = &net_buf_fixed_cb, \ + .alloc_data = (void *)&net_buf_fixed_##_name, \ + }; \ + struct net_buf_pool _name __net_buf_align \ + __in_section(_net_buf_pool, static, _name) = \ + NET_BUF_POOL_INITIALIZER(_name, &net_buf_fixed_alloc_##_name, \ + net_buf_##_name, _count, _destroy) +#endif + +#if (!BFLB_BLE) +extern const struct net_buf_data_cb net_buf_var_cb; + +/** @def NET_BUF_POOL_VAR_DEFINE + * @brief Define a new pool for buffers with variable size payloads + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this, the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * The data payload of the buffers will be based on a memory pool from which + * variable size payloads may be allocated. + * + * If provided with a custom destroy callback, this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _data_size Total amount of memory available for data payloads. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_VAR_DEFINE(_name, _count, _data_size, _destroy) \ + static struct net_buf _net_buf_##_name[_count] __noinit; \ + K_MEM_POOL_DEFINE(net_buf_mem_pool_##_name, 16, _data_size, 1, 4); \ + static const struct net_buf_data_alloc net_buf_data_alloc_##_name = { \ + .cb = &net_buf_var_cb, \ + .alloc_data = &net_buf_mem_pool_##_name, \ + }; \ + struct net_buf_pool _name __net_buf_align \ + __in_section(_net_buf_pool, static, _name) = \ + NET_BUF_POOL_INITIALIZER(_name, &net_buf_data_alloc_##_name, \ + _net_buf_##_name, _count, _destroy) +#endif + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +/** @def NET_BUF_POOL_DEFINE + * @brief Define a new pool for buffers + * + * Defines a net_buf_pool struct and the necessary memory storage (array of + * structs) for the needed amount of buffers. After this,the buffers can be + * accessed from the pool through net_buf_alloc. The pool is defined as a + * static variable, so if it needs to be exported outside the current module + * this needs to happen with the help of a separate pointer rather than an + * extern declaration. + * + * If provided with a custom destroy callback this callback is + * responsible for eventually calling net_buf_destroy() to complete the + * process of returning the buffer to the pool. + * + * @param _name Name of the pool variable. + * @param _count Number of buffers in the pool. + * @param _size Maximum data size for each buffer. + * @param _ud_size Amount of user data space to reserve. + * @param _destroy Optional destroy callback when buffer is freed. + */ +#define NET_BUF_POOL_DEFINE(_name, _count, _size, _ud_size, _destroy) \ + BUILD_ASSERT(_ud_size <= CONFIG_NET_BUF_USER_DATA_SIZE); \ + NET_BUF_POOL_FIXED_DEFINE(_name, _count, _size, _destroy) +#endif + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +void net_buf_init(struct net_buf_pool *buf_pool, u16_t buf_count, size_t data_size, destroy_cb_t destroy); +void net_buf_deinit(struct net_buf_pool *buf_pool); +#endif +/** + * @brief Looks up a pool based on its ID. + * + * @param id Pool ID (e.g. from buf->pool_id). + * + * @return Pointer to pool. + */ +struct net_buf_pool *net_buf_pool_get(int id); + +/** + * @brief Get a zero-based index for a buffer. + * + * This function will translate a buffer into a zero-based index, + * based on its placement in its buffer pool. This can be useful if you + * want to associate an external array of meta-data contexts with the + * buffers of a pool. + * + * @param buf Network buffer. + * + * @return Zero-based index for the buffer. + */ +int net_buf_id(struct net_buf *buf); + +/** + * @brief Allocate a new buffer from a pool. + * + * Allocate a new buffer from a pool. + * + * @param pool Which pool to allocate the buffer from. + * @param timeout Affects the action taken should the pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. Note that some types + * of data allocators do not support blocking (such as the HEAP + * type). In this case it's still possible for net_buf_alloc() to + * fail (return NULL) even if it was given K_FOREVER. + * + * @return New buffer or NULL if out of buffers. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_fixed_debug(struct net_buf_pool *pool, + s32_t timeout, const char *func, + int line); +#define net_buf_alloc_fixed(_pool, _timeout) \ + net_buf_alloc_fixed_debug(_pool, _timeout, __func__, __LINE__) +#else +struct net_buf *net_buf_alloc_fixed(struct net_buf_pool *pool, s32_t timeout); +#endif + +#define net_buf_alloc(_pool, _timeout) net_buf_alloc_fixed(_pool, _timeout) + +/** + * @brief Allocate a new buffer from a pool. + * + * Allocate a new buffer from a pool. + * + * @param pool Which pool to allocate the buffer from. + * @param size Amount of data the buffer must be able to fit. + * @param timeout Affects the action taken should the pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. Note that some types + * of data allocators do not support blocking (such as the HEAP + * type). In this case it's still possible for net_buf_alloc() to + * fail (return NULL) even if it was given K_FOREVER. + * + * @return New buffer or NULL if out of buffers. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_len_debug(struct net_buf_pool *pool, size_t size, + s32_t timeout, const char *func, + int line); +#define net_buf_alloc_len(_pool, _size, _timeout) \ + net_buf_alloc_len_debug(_pool, _size, _timeout, __func__, __LINE__) +#else +struct net_buf *net_buf_alloc_len(struct net_buf_pool *pool, size_t size, + s32_t timeout); +#endif + +/** + * @brief Allocate a new buffer from a pool but with external data pointer. + * + * Allocate a new buffer from a pool, where the data pointer comes from the + * user and not from the pool. + * + * @param pool Which pool to allocate the buffer from. + * @param data External data pointer + * @param size Amount of data the pointed data buffer if able to fit. + * @param timeout Affects the action taken should the pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. Note that some types + * of data allocators do not support blocking (such as the HEAP + * type). In this case it's still possible for net_buf_alloc() to + * fail (return NULL) even if it was given K_FOREVER. + * + * @return New buffer or NULL if out of buffers. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_alloc_with_data_debug(struct net_buf_pool *pool, + void *data, size_t size, + s32_t timeout, const char *func, + int line); +#define net_buf_alloc_with_data(_pool, _data_, _size, _timeout) \ + net_buf_alloc_with_data_debug(_pool, _data_, _size, _timeout, \ + __func__, __LINE__) +#else +struct net_buf *net_buf_alloc_with_data(struct net_buf_pool *pool, + void *data, size_t size, + s32_t timeout); +#endif + +/** + * @brief Get a buffer from a FIFO. + * + * Get buffer from a FIFO. + * + * @param fifo Which FIFO to take the buffer from. + * @param timeout Affects the action taken should the FIFO be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then wait as + * long as necessary. Otherwise, wait up to the specified number of + * milliseconds before timing out. + * + * @return New buffer or NULL if the FIFO is empty. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_get_debug(struct k_fifo *fifo, s32_t timeout, + const char *func, int line); +#define net_buf_get(_fifo, _timeout) \ + net_buf_get_debug(_fifo, _timeout, __func__, __LINE__) +#else +struct net_buf *net_buf_get(struct k_fifo *fifo, s32_t timeout); +#endif + +/** + * @brief Destroy buffer from custom destroy callback + * + * This helper is only intended to be used from custom destroy callbacks. + * If no custom destroy callback is given to NET_BUF_POOL_*_DEFINE() then + * there is no need to use this API. + * + * @param buf Buffer to destroy. + */ +static inline void net_buf_destroy(struct net_buf *buf) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + + k_lifo_put(&pool->free, buf); +} + +/** + * @brief Reset buffer + * + * Reset buffer data and flags so it can be reused for other purposes. + * + * @param buf Buffer to reset. + */ +void net_buf_reset(struct net_buf *buf); + +/** + * @brief Initialize buffer with the given headroom. + * + * Initializes a buffer with a given headroom. The buffer is not expected to + * contain any data when this API is called. + * + * @param buf Buffer to initialize. + * @param reserve How much headroom to reserve. + */ +void net_buf_simple_reserve(struct net_buf_simple *buf, size_t reserve); + +/** + * @brief Put a buffer into a list + * + * Put a buffer to the end of a list. If the buffer contains follow-up + * fragments this function will take care of inserting them as well + * into the list. + * + * @param list Which list to append the buffer to. + * @param buf Buffer. + */ +void net_buf_slist_put(sys_slist_t *list, struct net_buf *buf); + +/** + * @brief Get a buffer from a list. + * + * Get buffer from a list. If the buffer had any fragments, these will + * automatically be recovered from the list as well and be placed to + * the buffer's fragment list. + * + * @param list Which list to take the buffer from. + * + * @return New buffer or NULL if the FIFO is empty. + */ +struct net_buf *net_buf_slist_get(sys_slist_t *list); + +/** + * @brief Put a buffer into a FIFO + * + * Put a buffer to the end of a FIFO. If the buffer contains follow-up + * fragments this function will take care of inserting them as well + * into the FIFO. + * + * @param fifo Which FIFO to put the buffer to. + * @param buf Buffer. + */ +void net_buf_put(struct k_fifo *fifo, struct net_buf *buf); + +/** + * @brief Decrements the reference count of a buffer. + * + * Decrements the reference count of a buffer and puts it back into the + * pool if the count reaches zero. + * + * @param buf A valid pointer on a buffer + */ +#if defined(CONFIG_NET_BUF_LOG) +void net_buf_unref_debug(struct net_buf *buf, const char *func, int line); +#define net_buf_unref(_buf) \ + net_buf_unref_debug(_buf, __func__, __LINE__) +#else +void net_buf_unref(struct net_buf *buf); +#endif + +/** + * @brief Increment the reference count of a buffer. + * + * @param buf A valid pointer on a buffer + * + * @return the buffer newly referenced + */ +struct net_buf *net_buf_ref(struct net_buf *buf); + +/** + * @brief Duplicate buffer + * + * Duplicate given buffer including any data and headers currently stored. + * + * @param buf A valid pointer on a buffer + * @param timeout Affects the action taken should the pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. + * + * @return Duplicated buffer or NULL if out of buffers. + */ +struct net_buf *net_buf_clone(struct net_buf *buf, s32_t timeout); + +/** + * @brief Get a pointer to the user data of a buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Pointer to the user data of the buffer. + */ +static inline void *net_buf_user_data(const struct net_buf *buf) +{ + return (void *)buf->user_data; +} + +/** @def net_buf_reserve + * @brief Initialize buffer with the given headroom. + * + * Initializes a buffer with a given headroom. The buffer is not expected to + * contain any data when this API is called. + * + * @param buf Buffer to initialize. + * @param reserve How much headroom to reserve. + */ +#define net_buf_reserve(buf, reserve) net_buf_simple_reserve(&(buf)->b, \ + reserve) + +/** + * @def net_buf_add + * @brief Prepare data to be added at the end of the buffer + * + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param len Number of bytes to increment the length with. + * + * @return The original tail of the buffer. + */ +#define net_buf_add(buf, len) net_buf_simple_add(&(buf)->b, len) + +/** + * @def net_buf_add_mem + * @brief Copy bytes from memory to the end of the buffer + * + * Copies the given number of bytes to the end of the buffer. Increments the + * data length of the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param mem Location of data to be added. + * @param len Length of data to be added + * + * @return The original tail of the buffer. + */ +#define net_buf_add_mem(buf, mem, len) net_buf_simple_add_mem(&(buf)->b, \ + mem, len) + +/** + * @def net_buf_add_u8 + * @brief Add (8-bit) byte at the end of the buffer + * + * Adds a byte at the end of the buffer. Increments the data length of + * the buffer to account for more data at the end. + * + * @param buf Buffer to update. + * @param val byte value to be added. + * + * @return Pointer to the value added + */ +#define net_buf_add_u8(buf, val) net_buf_simple_add_u8(&(buf)->b, val) + +/** + * @def net_buf_add_le16 + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +#define net_buf_add_le16(buf, val) net_buf_simple_add_le16(&(buf)->b, val) + +/** + * @def net_buf_add_be16 + * @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 16-bit value to be added. + */ +#define net_buf_add_be16(buf, val) net_buf_simple_add_be16(&(buf)->b, val) + +/** + * @def net_buf_add_le32 + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +#define net_buf_add_le32(buf, val) net_buf_simple_add_le32(&(buf)->b, val) + +/** + * @def net_buf_add_be32 + * @brief Add 32-bit value at the end of the buffer + * + * Adds 32-bit value in big endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param val 32-bit value to be added. + */ +#define net_buf_add_be32(buf, val) net_buf_simple_add_be32(&(buf)->b, val) + +/** + * @def net_buf_push + * @brief Push data to the beginning of the buffer. + * + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * @param buf Buffer to update. + * @param len Number of bytes to add to the beginning. + * + * @return The new beginning of the buffer data. + */ +#define net_buf_push(buf, len) net_buf_simple_push(&(buf)->b, len) + +/** + * @def net_buf_push_le16 + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +#define net_buf_push_le16(buf, val) net_buf_simple_push_le16(&(buf)->b, val) + +/** + * @def net_buf_push_be16 + * @brief Push 16-bit value to the beginning of the buffer + * + * Adds 16-bit value in little endian format to the beginning of the + * buffer. + * + * @param buf Buffer to update. + * @param val 16-bit value to be pushed to the buffer. + */ +#define net_buf_push_be16(buf, val) net_buf_simple_push_be16(&(buf)->b, val) + +/** + * @def net_buf_push_u8 + * @brief Push 8-bit value to the beginning of the buffer + * + * Adds 8-bit value the beginning of the buffer. + * + * @param buf Buffer to update. + * @param val 8-bit value to be pushed to the buffer. + */ +#define net_buf_push_u8(buf, val) net_buf_simple_push_u8(&(buf)->b, val) + +/** + * @def net_buf_pull + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return New beginning of the buffer data. + */ +#define net_buf_pull(buf, len) net_buf_simple_pull(&(buf)->b, len) + +/** + * @def net_buf_pull_mem + * @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginning of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return Pointer to the old beginning of the buffer data. + */ +#define net_buf_pull_mem(buf, len) net_buf_simple_pull_mem(&(buf)->b, len) + +/** + * @def net_buf_pull_u8 + * @brief Remove a 8-bit value from the beginning of the buffer + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 8-bit values. + * + * @param buf A valid pointer on a buffer. + * + * @return The 8-bit removed value + */ +#define net_buf_pull_u8(buf) net_buf_simple_pull_u8(&(buf)->b) + +/** + * @def net_buf_pull_le16 + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 16-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from little endian to host endian. + */ +#define net_buf_pull_le16(buf) net_buf_simple_pull_le16(&(buf)->b) + +/** + * @def net_buf_pull_be16 + * @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 16-bit big endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 16-bit value converted from big endian to host endian. + */ +#define net_buf_pull_be16(buf) net_buf_simple_pull_be16(&(buf)->b) + +/** + * @def net_buf_pull_le32 + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 32-bit little endian data. + * + * @param buf A valid pointer on a buffer. + * + * @return 32-bit value converted from little endian to host endian. + */ +#define net_buf_pull_le32(buf) net_buf_simple_pull_le32(&(buf)->b) + +/** + * @def net_buf_pull_be32 + * @brief Remove and convert 32 bits from the beginning of the buffer. + * + * Same idea as with net_buf_pull(), but a helper for operating on + * 32-bit big endian data. + * + * @param buf A valid pointer on a buffer + * + * @return 32-bit value converted from big endian to host endian. + */ +#define net_buf_pull_be32(buf) net_buf_simple_pull_be32(&(buf)->b) + +/** + * @def net_buf_tailroom + * @brief Check buffer tailroom. + * + * Check how much free space there is at the end of the buffer. + * + * @param buf A valid pointer on a buffer + * + * @return Number of bytes available at the end of the buffer. + */ +#define net_buf_tailroom(buf) net_buf_simple_tailroom(&(buf)->b) + +/** + * @def net_buf_headroom + * @brief Check buffer headroom. + * + * Check how much free space there is in the beginning of the buffer. + * + * buf A valid pointer on a buffer + * + * @return Number of bytes available in the beginning of the buffer. + */ +#define net_buf_headroom(buf) net_buf_simple_headroom(&(buf)->b) + +/** + * @def net_buf_tail + * @brief Get the tail pointer for a buffer. + * + * Get a pointer to the end of the data in a buffer. + * + * @param buf Buffer. + * + * @return Tail pointer for the buffer. + */ +#define net_buf_tail(buf) net_buf_simple_tail(&(buf)->b) + +/** @brief Find the last fragment in the fragment list. + * + * @return Pointer to last fragment in the list. + */ +struct net_buf *net_buf_frag_last(struct net_buf *frags); + +/** @brief Insert a new fragment to a chain of bufs. + * + * Insert a new fragment into the buffer fragments list after the parent. + * + * Note: This function takes ownership of the fragment reference so the + * caller is not required to unref. + * + * @param parent Parent buffer/fragment. + * @param frag Fragment to insert. + */ +void net_buf_frag_insert(struct net_buf *parent, struct net_buf *frag); + +/** @brief Add a new fragment to the end of a chain of bufs. + * + * Append a new fragment into the buffer fragments list. + * + * Note: This function takes ownership of the fragment reference so the + * caller is not required to unref. + * + * @param head Head of the fragment chain. + * @param frag Fragment to add. + * + * @return New head of the fragment chain. Either head (if head + * was non-NULL) or frag (if head was NULL). + */ +struct net_buf *net_buf_frag_add(struct net_buf *head, struct net_buf *frag); + +/** @brief Delete existing fragment from a chain of bufs. + * + * @param parent Parent buffer/fragment, or NULL if there is no parent. + * @param frag Fragment to delete. + * + * @return Pointer to the buffer following the fragment, or NULL if it + * had no further fragments. + */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *net_buf_frag_del_debug(struct net_buf *parent, + struct net_buf *frag, + const char *func, int line); +#define net_buf_frag_del(_parent, _frag) \ + net_buf_frag_del_debug(_parent, _frag, __func__, __LINE__) +#else +struct net_buf *net_buf_frag_del(struct net_buf *parent, struct net_buf *frag); +#endif + +/** + * @brief Copy len bytes from src starting from offset to dst buffer + * + * This routine assumes that dst is large enough to store @a len bytes + * starting from offset at src. + * + * @param dst Destination buffer + * @param dst_len Destination buffer max length + * @param src Source buffer that may be fragmented + * @param offset Starting point to copy from + * @param len Number of bytes to copy + * @return number of bytes copied if everything is ok + * @return -ENOMEM on error + */ +size_t net_buf_linearize(void *dst, size_t dst_len, struct net_buf *src, + size_t offset, size_t len); + +/** + * @typedef net_buf_allocator_cb + * @brief Network buffer allocator callback. + * + * @details The allocator callback is called when net_buf_append_bytes + * needs to allocate a new net_buf. + * + * @param timeout Affects the action taken should the net buf pool be empty. + * If K_NO_WAIT, then return immediately. If K_FOREVER, then + * wait as long as necessary. Otherwise, wait up to the specified + * number of milliseconds before timing out. + * @param user_data The user data given in net_buf_append_bytes call. + * @return pointer to allocated net_buf or NULL on error. + */ +typedef struct net_buf *(*net_buf_allocator_cb)(s32_t timeout, void *user_data); + +/** + * @brief Append data to a list of net_buf + * + * @details Append data to a net_buf. If there is not enough space in the + * net_buf then more net_buf will be added, unless there are no free net_buf + * and timeout occurs. + * + * @param buf Network buffer. + * @param len Total length of input data + * @param value Data to be added + * @param timeout Timeout is passed to the net_buf allocator callback. + * @param allocate_cb When a new net_buf is required, use this callback. + * @param user_data A user data pointer to be supplied to the allocate_cb. + * This pointer is can be anything from a mem_pool or a net_pkt, the + * logic is left up to the allocate_cb function. + * + * @return Length of data actually added. This may be less than input + * length if other timeout than K_FOREVER was used, and there + * were no free fragments in a pool to accommodate all data. + */ +size_t net_buf_append_bytes(struct net_buf *buf, size_t len, + const void *value, s32_t timeout, + net_buf_allocator_cb allocate_cb, void *user_data); + +/** + * @brief Skip N number of bytes in a net_buf + * + * @details Skip N number of bytes starting from fragment's offset. If the total + * length of data is placed in multiple fragments, this function will skip from + * all fragments until it reaches N number of bytes. Any fully skipped buffers + * are removed from the net_buf list. + * + * @param buf Network buffer. + * @param len Total length of data to be skipped. + * + * @return Pointer to the fragment or + * NULL and pos is 0 after successful skip, + * NULL and pos is 0xffff otherwise. + */ +static inline struct net_buf *net_buf_skip(struct net_buf *buf, u16_t len) +{ + while (buf && len--) { + net_buf_pull_u8(buf); + if (!buf->len) { + buf = net_buf_frag_del(NULL, buf); + } + } + + return buf; +} + +/** @brief Calculate amount of bytes stored in fragments. + * + * Calculates the total amount of data stored in the given buffer and the + * fragments linked to it. + * + * @param buf Buffer to start off with. + * + * @return Number of bytes in the buffer and its fragments. + */ +static inline size_t net_buf_frags_len(struct net_buf *buf) +{ + size_t bytes = 0; + + while (buf) { + bytes += buf->len; + buf = buf->frags; + } + + return bytes; +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __NET_BUF_H */ diff --git a/components/ble/ble_stack/common/include/toolchain.h b/components/ble/ble_stack/common/include/toolchain.h new file mode 100644 index 00000000..3bea0dbc --- /dev/null +++ b/components/ble/ble_stack/common/include/toolchain.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2014, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Macros to abstract toolchain specific capabilities + * + * This file contains various macros to abstract compiler capabilities that + * utilize toolchain specific attributes and/or pragmas. + */ + +#ifndef _TOOLCHAIN_H +#define _TOOLCHAIN_H + +#if defined(__XCC__) +#include +#elif defined(__GNUC__) || (defined(_LINKER) && defined(__GCC_LINKER_CMD__)) +#include +#else +#include +#endif + +#endif /* _TOOLCHAIN_H */ diff --git a/components/ble/ble_stack/common/include/toolchain/common.h b/components/ble/ble_stack/common/include/toolchain/common.h new file mode 100644 index 00000000..ea2b318b --- /dev/null +++ b/components/ble/ble_stack/common/include/toolchain/common.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2010-2014 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_ +#define ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_ +/** + * @file + * @brief Common toolchain abstraction + * + * Macros to abstract compiler capabilities (common to all toolchains). + */ + +/* Abstract use of extern keyword for compatibility between C and C++ */ +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C extern +#endif + +/* Use TASK_ENTRY_CPP to tag task entry points defined in C++ files. */ + +#ifdef __cplusplus +#define TASK_ENTRY_CPP extern "C" +#endif + +/* + * Generate a reference to an external symbol. + * The reference indicates to the linker that the symbol is required + * by the module containing the reference and should be included + * in the image if the module is in the image. + * + * The assembler directive ".set" is used to define a local symbol. + * No memory is allocated, and the local symbol does not appear in + * the symbol table. + */ + +#ifdef _ASMLANGUAGE + #define REQUIRES(sym) .set sym ## _Requires, sym +#else + #define REQUIRES(sym) __asm__ (".set " # sym "_Requires, " # sym "\n\t"); +#endif + +#ifdef _ASMLANGUAGE + #define SECTION .section +#endif + +#define CONFIG_RISCV 1 +/* + * If the project is being built for speed (i.e. not for minimum size) then + * align functions and branches in executable sections to improve performance. + */ + +#ifdef _ASMLANGUAGE + + #if defined(CONFIG_X86) + + #ifdef PERF_OPT + #define PERFOPT_ALIGN .balign 16 + #else + #define PERFOPT_ALIGN .balign 1 + #endif + + #elif defined(CONFIG_ARM) + + #define PERFOPT_ALIGN .balign 4 + + #elif defined(CONFIG_ARC) + + #define PERFOPT_ALIGN .balign 4 + + #elif defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) || \ + defined(CONFIG_XTENSA) + #define PERFOPT_ALIGN .balign 4 + + #elif defined(CONFIG_ARCH_POSIX) + + #else + + #error Architecture unsupported + + #endif + + #define GC_SECTION(sym) SECTION .text.##sym, "ax" + +#endif /* _ASMLANGUAGE */ + +/* force inlining a function */ + +#if !defined(_ASMLANGUAGE) + #ifdef CONFIG_COVERAGE + /* + * The always_inline attribute forces a function to be inlined, + * even ignoring -fno-inline. So for code coverage, do not + * force inlining of these functions to keep their bodies around + * so their number of executions can be counted. + * + * Note that "inline" is kept here for kobject_hash.c and + * priv_stacks_hash.c. These are built without compiler flags + * used for coverage. ALWAYS_INLINE cannot be empty as compiler + * would complain about unused functions. Attaching unused + * attribute would result in their text sections ballon more than + * 10 times in size, as those functions are kept in text section. + * So just keep "inline" here. + */ + #define ALWAYS_INLINE inline + #else + #define ALWAYS_INLINE inline __attribute__((always_inline)) + #endif +#endif + +#define Z_STRINGIFY(x) #x +#define STRINGIFY(s) Z_STRINGIFY(s) + +/* concatenate the values of the arguments into one */ +#define _DO_CONCAT(x, y) x ## y +#define _CONCAT(x, y) _DO_CONCAT(x, y) + +/* Additionally used as a sentinel by gen_syscalls.py to identify what + * functions are system calls + * + * Note POSIX unit tests don't still generate the system call stubs, so + * until https://github.com/zephyrproject-rtos/zephyr/issues/5006 is + * fixed via possibly #4174, we introduce this hack -- which will + * disallow us to test system calls in POSIX unit testing (currently + * not used). + */ +#ifndef ZTEST_UNITTEST +#define __syscall static inline +#else +#define __syscall +#endif /* #ifndef ZTEST_UNITTEST */ + +#ifndef BUILD_ASSERT +/* compile-time assertion that makes the build fail */ +#define BUILD_ASSERT(EXPR) \ + enum _CONCAT(__build_assert_enum, __COUNTER__) { \ + _CONCAT(__build_assert, __COUNTER__) = 1 / !!(EXPR) \ + } +#endif +#ifndef BUILD_ASSERT_MSG +/* build assertion with message -- common implementation swallows message. */ +#define BUILD_ASSERT_MSG(EXPR, MSG) BUILD_ASSERT(EXPR) +#endif + +/* + * This is meant to be used in conjunction with __in_section() and similar + * where scattered structure instances are concatened together by the linker + * and walked by the code at run time just like a contiguous array of such + * structures. + * + * Assemblers and linkers may insert alignment padding by default whose + * size is larger than the natural alignment for those structures when + * gathering various section segments together, messing up the array walk. + * To prevent this, we need to provide an explicit alignment not to rely + * on the default that might just work by luck. + * + * Alignment statements in linker scripts are not sufficient as + * the assembler may add padding by itself to each segment when switching + * between sections within the same file even if it merges many such segments + * into a single section in the end. + */ +#define Z_DECL_ALIGN(type) __aligned(__alignof(type)) type + +/* + * Convenience helper combining __in_section() and Z_DECL_ALIGN(). + * The section name is the struct type prepended with an underscore. + * The subsection is "static" and the subsubsection is the variable name. + */ +#define Z_STRUCT_SECTION_ITERABLE(struct_type, name) \ + Z_DECL_ALIGN(struct struct_type) name \ + __in_section(_##struct_type, static, name) __used + +/* + * Itterator for structure instances gathered by Z_STRUCT_SECTION_ITERABLE(). + * The linker must provide a __list_start symbol and a + * __list_end symbol to mark the start and the end of the + * list of struct objects to iterate over. + */ +#define Z_STRUCT_SECTION_FOREACH(struct_type, iterator) \ + extern struct struct_type _CONCAT(_##struct_type, _list_start)[]; \ + extern struct struct_type _CONCAT(_##struct_type, _list_end)[]; \ + for (struct struct_type *iterator = \ + _CONCAT(_##struct_type, _list_start); \ + ({ __ASSERT(iterator <= _CONCAT(_##struct_type, _list_end), \ + "unexpected list end location"); \ + iterator < _CONCAT(_##struct_type, _list_end); }); \ + iterator++) + +#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_ */ diff --git a/components/ble/ble_stack/common/include/toolchain/gcc.h b/components/ble/ble_stack/common/include/toolchain/gcc.h new file mode 100644 index 00000000..10b5c7b6 --- /dev/null +++ b/components/ble/ble_stack/common/include/toolchain/gcc.h @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2010-2014,2017 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_ +#define ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_ + +/** + * @file + * @brief GCC toolchain abstraction + * + * Macros to abstract compiler capabilities for GCC toolchain. + */ + +/* + * Older versions of GCC do not define __BYTE_ORDER__, so it must be manually + * detected and defined using arch-specific definitions. + */ + +#ifndef _LINKER + +#ifndef __ORDER_BIG_ENDIAN__ +#define __ORDER_BIG_ENDIAN__ (1) +#endif + +#ifndef __ORDER_LITTLE_ENDIAN__ +#define __ORDER_LITTLE_ENDIAN__ (2) +#endif + +#ifndef __BYTE_ORDER__ +#if defined(__BIG_ENDIAN__) || defined(__ARMEB__) || \ + defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(__MIPSEB__) || defined(__TC32EB__) + +#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__ + +#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || \ + defined(__MIPSEL__) || defined(__TC32EL__) + +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ + +#else +#error "__BYTE_ORDER__ is not defined and cannot be automatically resolved" +#endif +#endif + + +/* C++11 has static_assert built in */ +#ifdef __cplusplus +#define BUILD_ASSERT(EXPR) static_assert(EXPR, "") +#define BUILD_ASSERT_MSG(EXPR, MSG) static_assert(EXPR, MSG) +/* + * GCC 4.6 and higher have the C11 _Static_assert built in, and its + * output is easier to understand than the common BUILD_ASSERT macros. + */ +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) || \ + (__STDC_VERSION__) >= 201100 +#define BUILD_ASSERT(EXPR) _Static_assert(EXPR, "") +#define BUILD_ASSERT_MSG(EXPR, MSG) _Static_assert(EXPR, MSG) +#endif + +#include +#include + +#define ALIAS_OF(of) __attribute__((alias(#of))) + +#define FUNC_ALIAS(real_func, new_alias, return_type) \ + return_type new_alias() ALIAS_OF(real_func) + +#if defined(CONFIG_ARCH_POSIX) +#include + +/*let's not segfault if this were to happen for some reason*/ +#define CODE_UNREACHABLE \ +{\ + posix_print_error_and_exit("CODE_UNREACHABLE reached from %s:%d\n",\ + __FILE__, __LINE__);\ + __builtin_unreachable(); \ +} +#else +#define CODE_UNREACHABLE __builtin_unreachable() +#endif +#define FUNC_NORETURN __attribute__((__noreturn__)) + +/* The GNU assembler for Cortex-M3 uses # for immediate values, not + * comments, so the @nobits# trick does not work. + */ +#if defined(CONFIG_ARM) +#define _NODATA_SECTION(segment) __attribute__((section(#segment))) +#else +#define _NODATA_SECTION(segment) \ + __attribute__((section(#segment ",\"wa\",@nobits#"))) +#endif + +/* Unaligned access */ +#define UNALIGNED_GET(p) \ +__extension__ ({ \ + struct __attribute__((__packed__)) { \ + __typeof__(*(p)) __v; \ + } *__p = (__typeof__(__p)) (p); \ + __p->__v; \ +}) + + +#if __GNUC__ >= 7 && defined(CONFIG_ARM) + +/* Version of UNALIGNED_PUT() which issues a compiler_barrier() after + * the store. It is required to workaround an apparent optimization + * bug in GCC for ARM Cortex-M3 and higher targets, when multiple + * byte, half-word and word stores (strb, strh, str instructions), + * which support unaligned access, can be coalesced into store double + * (strd) instruction, which doesn't support unaligned access (the + * compilers in question do this optimization ignoring __packed__ + * attribute). + */ +#define UNALIGNED_PUT(v, p) \ +do { \ + struct __attribute__((__packed__)) { \ + __typeof__(*p) __v; \ + } *__p = (__typeof__(__p)) (p); \ + __p->__v = (v); \ + compiler_barrier(); \ +} while (false) + +#else + +#define UNALIGNED_PUT(v, p) \ +do { \ + struct __attribute__((__packed__)) { \ + __typeof__(*p) __v; \ + } *__p = (__typeof__(__p)) (p); \ + __p->__v = (v); \ +} while (false) + +#endif + +/* Double indirection to ensure section names are expanded before + * stringification + */ +#define __GENERIC_SECTION(segment) __attribute__((section(STRINGIFY(segment)))) +#define Z_GENERIC_SECTION(segment) __GENERIC_SECTION(segment) + +#define ___in_section(a, b, c) \ + __attribute__((section("." Z_STRINGIFY(a) \ + "." Z_STRINGIFY(b) \ + "." Z_STRINGIFY(c)))) +#define __in_section(a, b, c) ___in_section(a, b, c) + +#define __in_section_unique(seg) ___in_section(seg, __FILE__, __COUNTER__) + +/* When using XIP, using '__ramfunc' places a function into RAM instead + * of FLASH. Make sure '__ramfunc' is defined only when + * CONFIG_ARCH_HAS_RAMFUNC_SUPPORT is defined, so that the compiler can + * report an error if '__ramfunc' is used but the architecture does not + * support it. + */ +#if !defined(CONFIG_XIP) +#define __ramfunc +#elif defined(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT) +#define __ramfunc __attribute__((noinline)) \ + __attribute__((long_call, section(".ramfunc"))) +#endif /* !CONFIG_XIP */ + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif +#ifndef __aligned +#define __aligned(x) __attribute__((__aligned__(x))) +#endif +#define __may_alias __attribute__((__may_alias__)) +#ifndef __printf_like +#define __printf_like(f, a) __attribute__((format (printf, f, a))) +#endif +#define __used __attribute__((__used__)) +#ifndef __deprecated +#define __deprecated __attribute__((deprecated)) +#endif +#define ARG_UNUSED(x) (void)(x) + +#ifndef likely +#define likely(x) __builtin_expect((bool)!!(x), true) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect((bool)!!(x), false) +#endif + + +#define popcount(x) __builtin_popcount(x) + +#ifndef __weak +#define __weak __attribute__((__weak__)) +#endif +#define __unused __attribute__((__unused__)) + +/* Builtins with availability that depend on the compiler version. */ +#if __GNUC__ >= 5 +#define HAS_BUILTIN___builtin_add_overflow 1 +#define HAS_BUILTIN___builtin_sub_overflow 1 +#define HAS_BUILTIN___builtin_mul_overflow 1 +#define HAS_BUILTIN___builtin_div_overflow 1 +#endif +#if __GNUC__ >= 4 +#define HAS_BUILTIN___builtin_clz 1 +#define HAS_BUILTIN___builtin_clzl 1 +#define HAS_BUILTIN___builtin_clzll 1 +#define HAS_BUILTIN___builtin_ctz 1 +#define HAS_BUILTIN___builtin_ctzl 1 +#define HAS_BUILTIN___builtin_ctzll 1 +#endif + +/* Be *very* careful with this, you cannot filter out with -wno-deprecated, + * which has implications for -Werror + */ +#define __DEPRECATED_MACRO _Pragma("GCC warning \"Macro is deprecated\"") + +/* These macros allow having ARM asm functions callable from thumb */ + +#if defined(_ASMLANGUAGE) + +#ifdef CONFIG_ARM + +#if defined(CONFIG_ISA_THUMB2) + +#define FUNC_CODE() .thumb; +#define FUNC_INSTR(a) + +#elif defined(CONFIG_ISA_ARM) + +#define FUNC_CODE() .code 32 +#define FUNC_INSTR(a) + +#else + +#error unknown instruction set + +#endif /* ISA */ + +#else + +#define FUNC_CODE() +#define FUNC_INSTR(a) + +#endif /* !CONFIG_ARM */ + +#endif /* _ASMLANGUAGE */ + +/* + * These macros are used to declare assembly language symbols that need + * to be typed properly(func or data) to be visible to the OMF tool. + * So that the build tool could mark them as an entry point to be linked + * correctly. This is an elfism. Use #if 0 for a.out. + */ + +#if defined(_ASMLANGUAGE) + +#if defined(CONFIG_ARM) || defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) \ + || defined(CONFIG_XTENSA) +#define GTEXT(sym) .global sym; .type sym, %function +#define GDATA(sym) .global sym; .type sym, %object +#define WTEXT(sym) .weak sym; .type sym, %function +#define WDATA(sym) .weak sym; .type sym, %object +#elif defined(CONFIG_ARC) +/* + * Need to use assembly macros because ';' is interpreted as the start of + * a single line comment in the ARC assembler. + */ + +.macro glbl_text symbol + .globl \symbol + .type \symbol, %function +.endm + +.macro glbl_data symbol + .globl \symbol + .type \symbol, %object +.endm + +.macro weak_data symbol + .weak \symbol + .type \symbol, %object +.endm + +#define GTEXT(sym) glbl_text sym +#define GDATA(sym) glbl_data sym +#define WDATA(sym) weak_data sym + +#else /* !CONFIG_ARM && !CONFIG_ARC */ +#define GTEXT(sym) .globl sym; .type sym, @function +#define GDATA(sym) .globl sym; .type sym, @object +#endif + +/* + * These macros specify the section in which a given function or variable + * resides. + * + * - SECTION_FUNC allows only one function to reside in a sub-section + * - SECTION_SUBSEC_FUNC allows multiple functions to reside in a sub-section + * This ensures that garbage collection only discards the section + * if all functions in the sub-section are not referenced. + */ + +#if defined(CONFIG_ARC) +/* + * Need to use assembly macros because ';' is interpreted as the start of + * a single line comment in the ARC assembler. + * + * Also, '\()' is needed in the .section directive of these macros for + * correct substitution of the 'section' variable. + */ + +.macro section_var section, symbol + .section .\section\().\symbol + \symbol : +.endm + +.macro section_func section, symbol + .section .\section\().\symbol, "ax" + FUNC_CODE() + PERFOPT_ALIGN + \symbol : + FUNC_INSTR(\symbol) +.endm + +.macro section_subsec_func section, subsection, symbol + .section .\section\().\subsection, "ax" + PERFOPT_ALIGN + \symbol : +.endm + +#define SECTION_VAR(sect, sym) section_var sect, sym +#define SECTION_FUNC(sect, sym) section_func sect, sym +#define SECTION_SUBSEC_FUNC(sect, subsec, sym) \ + section_subsec_func sect, subsec, sym +#else /* !CONFIG_ARC */ + +#define SECTION_VAR(sect, sym) .section .sect.##sym; sym : +#define SECTION_FUNC(sect, sym) \ + .section .sect.sym, "ax"; \ + FUNC_CODE() \ + PERFOPT_ALIGN; sym : \ + FUNC_INSTR(sym) +#define SECTION_SUBSEC_FUNC(sect, subsec, sym) \ + .section .sect.subsec, "ax"; PERFOPT_ALIGN; sym : + +#endif /* CONFIG_ARC */ + +#endif /* _ASMLANGUAGE */ + +#if defined(CONFIG_ARM) && defined(_ASMLANGUAGE) +#if defined(CONFIG_ISA_THUMB2) +/* '.syntax unified' is a gcc-ism used in thumb-2 asm files */ +#define _ASM_FILE_PROLOGUE .text; .syntax unified; .thumb +#else +#define _ASM_FILE_PROLOGUE .text; .code 32 +#endif +#endif + +/* + * These macros generate absolute symbols for GCC + */ + +/* create an extern reference to the absolute symbol */ + +#define GEN_OFFSET_EXTERN(name) extern const char name[] + +#define GEN_ABS_SYM_BEGIN(name) \ + EXTERN_C void name(void); \ + void name(void) \ + { + +#define GEN_ABS_SYM_END } + +#if defined(CONFIG_ARM) + +/* + * GNU/ARM backend does not have a proper operand modifier which does not + * produces prefix # followed by value, such as %0 for PowerPC, Intel, and + * MIPS. The workaround performed here is using %B0 which converts + * the value to ~(value). Thus "n"(~(value)) is set in operand constraint + * to output (value) in the ARM specific GEN_OFFSET macro. + */ + +#define GEN_ABSOLUTE_SYM(name, value) \ + __asm__(".globl\t" #name "\n\t.equ\t" #name \ + ",%B0" \ + "\n\t.type\t" #name ",%%object" : : "n"(~(value))) + +#elif defined(CONFIG_X86) || defined(CONFIG_ARC) + +#define GEN_ABSOLUTE_SYM(name, value) \ + __asm__(".globl\t" #name "\n\t.equ\t" #name \ + ",%c0" \ + "\n\t.type\t" #name ",@object" : : "n"(value)) + +#elif defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) || defined(CONFIG_XTENSA) + +/* No special prefixes necessary for constants in this arch AFAICT */ +#define GEN_ABSOLUTE_SYM(name, value) \ + __asm__(".globl\t" #name "\n\t.equ\t" #name \ + ",%0" \ + "\n\t.type\t" #name ",%%object" : : "n"(value)) + +#elif defined(CONFIG_ARCH_POSIX) +#define GEN_ABSOLUTE_SYM(name, value) \ + __asm__(".globl\t" #name "\n\t.equ\t" #name \ + ",%c0" \ + "\n\t.type\t" #name ",@object" : : "n"(value)) +#else +#error processor architecture not supported +#endif + +#define compiler_barrier() do { \ + __asm__ __volatile__ ("" ::: "memory"); \ +} while (false) + +/** @brief Return larger value of two provided expressions. + * + * Macro ensures that expressions are evaluated only once. + * + * @note Macro has limited usage compared to the standard macro as it cannot be + * used: + * - to generate constant integer, e.g. __aligned(Z_MAX(4,5)) + * - static variable, e.g. array like static u8_t array[Z_MAX(...)]; + */ +#define Z_MAX(a, b) ({ \ + /* random suffix to avoid naming conflict */ \ + __typeof__(a) _value_a_ = (a); \ + __typeof__(b) _value_b_ = (b); \ + _value_a_ > _value_b_ ? _value_a_ : _value_b_; \ + }) + +/** @brief Return smaller value of two provided expressions. + * + * Macro ensures that expressions are evaluated only once. See @ref Z_MAX for + * macro limitations. + */ +#define Z_MIN(a, b) ({ \ + /* random suffix to avoid naming conflict */ \ + __typeof__(a) _value_a_ = (a); \ + __typeof__(b) _value_b_ = (b); \ + _value_a_ < _value_b_ ? _value_a_ : _value_b_; \ + }) + +#endif /* !_LINKER */ +#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_ */ diff --git a/components/ble/ble_stack/common/include/toolchain/xcc.h b/components/ble/ble_stack/common/include/toolchain/xcc.h new file mode 100644 index 00000000..4c084edf --- /dev/null +++ b/components/ble/ble_stack/common/include/toolchain/xcc.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_XCC_H_ +#define ZEPHYR_INCLUDE_TOOLCHAIN_XCC_H_ + +/* toolchain/gcc.h errors out if __BYTE_ORDER__ cannot be determined + * there. However, __BYTE_ORDER__ is actually being defined later in + * this file. So define __BYTE_ORDER__ to skip the check in gcc.h + * and undefine after including gcc.h. + */ +#define __BYTE_ORDER__ +#include +#undef __BYTE_ORDER__ + +#include + +/* XCC doesn't support __COUNTER__ but this should be good enough */ +#define __COUNTER__ __LINE__ + +#undef __in_section_unique +#define __in_section_unique(seg) \ + __attribute__((section("." STRINGIFY(seg) "." STRINGIFY(__COUNTER__)))) + +#ifndef __GCC_LINKER_CMD__ +#include + +/* + * XCC does not define the following macros with the expected names, but the + * HAL defines similar ones. Thus we include it and define the missing macros + * ourselves. + */ +#if XCHAL_MEMORY_ORDER == XTHAL_BIGENDIAN +#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__ +#elif XCHAL_MEMORY_ORDER == XTHAL_LITTLEENDIAN +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#else +#error "Cannot determine __BYTE_ORDER__" +#endif + +#endif /* __GCC_LINKER_CMD__ */ + +#define __builtin_unreachable() do { __ASSERT(false, "Unreachable code"); } \ + while (true) + +#endif diff --git a/components/ble/ble_stack/common/include/work_q.h b/components/ble/ble_stack/common/include/work_q.h new file mode 100644 index 00000000..cc4b33cf --- /dev/null +++ b/components/ble/ble_stack/common/include/work_q.h @@ -0,0 +1,61 @@ +#ifndef WORK_Q_H +#define WORK_Q_H +#include "atomic.h" +#include "zephyr.h" + +#if defined(BFLB_BLE) +struct k_work_q { + struct k_fifo fifo; +}; + +typedef struct{ + bl_timer_t timer; + struct k_delayed_work *delay_work; +}timer_rec_d; + +int k_work_q_start(); + +enum { + K_WORK_STATE_PENDING, + K_WORK_STATE_PERIODIC, +}; +struct k_work; +/* work define*/ +typedef void (*k_work_handler_t)(struct k_work *work); +struct k_work { + void *_reserved; + k_work_handler_t handler; + atomic_t flags[1]; +}; + +#define _K_WORK_INITIALIZER(work_handler) \ + { \ + ._reserved = NULL, \ + .handler = work_handler, \ + .flags = { 0 } \ + } + +#define K_WORK_INITIALIZER __DEPRECATED_MACRO _K_WORK_INITIALIZER + + +int k_work_init(struct k_work *work, k_work_handler_t handler); +void k_work_submit(struct k_work *work); + +/*delay work define*/ +struct k_delayed_work { + struct k_work work; + struct k_work_q *work_q; + k_timer_t timer; +}; + +void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler); +int k_delayed_work_submit(struct k_delayed_work *work, uint32_t delay); +/* Added by bouffalolab */ +int k_delayed_work_submit_periodic(struct k_delayed_work *work, s32_t period); +int k_delayed_work_cancel(struct k_delayed_work *work); +s32_t k_delayed_work_remaining_get(struct k_delayed_work *work); +void k_delayed_work_del_timer(struct k_delayed_work *work); +/* Added by bouffalolab */ +int k_delayed_work_free(struct k_delayed_work *work); +#endif +#endif /* WORK_Q_H */ diff --git a/components/ble/ble_stack/common/include/zephyr/types.h b/components/ble/ble_stack/common/include/zephyr/types.h new file mode 100644 index 00000000..5ae80b0f --- /dev/null +++ b/components/ble/ble_stack/common/include/zephyr/types.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __Z_TYPES_H__ +#define __Z_TYPES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef signed char s8_t; +typedef signed short s16_t; +typedef signed int s32_t; +typedef signed long long s64_t; + +typedef unsigned char u8_t; +typedef unsigned short u16_t; +typedef unsigned int u32_t; +typedef unsigned long long u64_t; + +#ifdef __cplusplus +} +#endif + +#endif /* __Z_TYPES_H__ */ diff --git a/components/ble/ble_stack/common/log.c b/components/ble/ble_stack/common/log.c new file mode 100644 index 00000000..8fe2f618 --- /dev/null +++ b/components/ble/ble_stack/common/log.c @@ -0,0 +1,63 @@ +/* log.c - logging helpers */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Helper for printk parameters to convert from binary to hex. + * We declare multiple buffers so the helper can be used multiple times + * in a single printk call. + */ + +#include +#include +#include +#include +#include +#include + +const char *bt_hex_real(const void *buf, size_t len) +{ + static const char hex[] = "0123456789abcdef"; + #if defined (CONFIG_BT_DEBUG_MONITOR) + static char str[255]; + #else + static char str[128]; + #endif + const u8_t *b = buf; + int i; + + len = MIN(len, (sizeof(str) - 1) / 2); + + for (i = 0; i < len; i++) { + str[i * 2] = hex[b[i] >> 4]; + str[i * 2 + 1] = hex[b[i] & 0xf]; + } + + str[i * 2] = '\0'; + + return str; +} + +const char *bt_addr_str_real(const bt_addr_t *addr) +{ + static char str[BT_ADDR_STR_LEN]; + + bt_addr_to_str(addr, str, sizeof(str)); + + return str; +} + +const char *bt_addr_le_str_real(const bt_addr_le_t *addr) +{ + static char str[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, str, sizeof(str)); + + return str; +} + + diff --git a/components/ble/ble_stack/common/log.h b/components/ble/ble_stack/common/log.h new file mode 100644 index 00000000..0f0e8b59 --- /dev/null +++ b/components/ble/ble_stack/common/log.h @@ -0,0 +1,139 @@ +/** @file + * @brief Bluetooth subsystem logging helpers. + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_LOG_H +#define __BT_LOG_H + +#if defined(BL_MCU_SDK) +#include "bflb_platform.h" +#endif + +#include + +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "FreeRTOSConfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(BT_DBG_ENABLED) +#define BT_DBG_ENABLED 1 +#endif + +#if BT_DBG_ENABLED +#define LOG_LEVEL LOG_LEVEL_DBG +#else +#define LOG_LEVEL CONFIG_BT_LOG_LEVEL +#endif + +//LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL); + +#if defined(BFLB_BLE) + +#if defined(BL_MCU_SDK) +#define BT_DBG(fmt, ...) //bflb_platform_printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#define BT_ERR(fmt, ...) bflb_platform_printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#else +#define BT_DBG(fmt, ...) //printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#define BT_ERR(fmt, ...) printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#endif + +#if defined(CONFIG_BT_STACK_PTS) || defined(CONFIG_BT_MESH_PTS) +#if defined(BL_MCU_SDK) +#define BT_PTS(fmt, ...) bflb_platform_printf(fmt"\r\n", ##__VA_ARGS__) +#else +#define BT_PTS(fmt, ...) printf(fmt"\r\n", ##__VA_ARGS__) +#endif + +#endif +#if defined(BL_MCU_SDK) +#define BT_WARN(fmt, ...) bflb_platform_printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#define BT_INFO(fmt, ...) //bflb_platform_printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#else +#define BT_WARN(fmt, ...) printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#define BT_INFO(fmt, ...) //printf(fmt", %s\r\n", ##__VA_ARGS__, __func__) +#endif + +#else /*BFLB_BLE*/ + +#define BT_DBG(fmt, ...) LOG_DBG(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) LOG_ERR(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) LOG_WRN(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) LOG_INF(fmt, ##__VA_ARGS__) + +#if defined(CONFIG_BT_ASSERT_VERBOSE) +#define BT_ASSERT_PRINT(fmt, ...) printk(fmt, ##__VA_ARGS__) +#else +#define BT_ASSERT_PRINT(fmt, ...) +#endif /* CONFIG_BT_ASSERT_VERBOSE */ + +#if defined(CONFIG_BT_ASSERT_PANIC) +#define BT_ASSERT_DIE k_panic +#else +#define BT_ASSERT_DIE k_oops +#endif /* CONFIG_BT_ASSERT_PANIC */ + +#endif /*BFLB_BLE*/ + +#if defined(CONFIG_BT_ASSERT) +#if defined(BFLB_BLE) +extern void user_vAssertCalled(void); +#define BT_ASSERT(cond) if( ( cond ) == 0 ) user_vAssertCalled() +#else +#define BT_ASSERT(cond) if (!(cond)) { \ + BT_ASSERT_PRINT("assert: '" #cond \ + "' failed\n"); \ + BT_ASSERT_DIE(); \ + } +#endif/*BFLB_BLE*/ +#else +#if defined(BFLB_BLE) +#define BT_ASSERT(cond) +#else +#define BT_ASSERT(cond) __ASSERT_NO_MSG(cond) +#endif /*BFLB_BLE*/ +#endif/* CONFIG_BT_ASSERT*/ + +#define BT_HEXDUMP_DBG(_data, _length, _str) \ + LOG_HEXDUMP_DBG((const u8_t *)_data, _length, _str) + +#if defined(BFLB_BLE) +static inline char *log_strdup(const char *str) +{ + return (char *)str; +} +#endif + +/* NOTE: These helper functions always encodes into the same buffer storage. + * It is the responsibility of the user of this function to copy the information + * in this string if needed. + */ +const char *bt_hex_real(const void *buf, size_t len); +const char *bt_addr_str_real(const bt_addr_t *addr); +const char *bt_addr_le_str_real(const bt_addr_le_t *addr); + +/* NOTE: log_strdup does not guarantee a duplication of the string. + * It is therefore still the responsibility of the user to handle the + * restrictions in the underlying function call. + */ +#define bt_hex(buf, len) log_strdup(bt_hex_real(buf, len)) +#define bt_addr_str(addr) log_strdup(bt_addr_str_real(addr)) +#define bt_addr_le_str(addr) log_strdup(bt_addr_le_str_real(addr)) + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_LOG_H */ diff --git a/components/ble/ble_stack/common/poll.c b/components/ble/ble_stack/common/poll.c new file mode 100644 index 00000000..8511878c --- /dev/null +++ b/components/ble/ble_stack/common/poll.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2017 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief Kernel asynchronous event polling interface. + * + * This polling mechanism allows waiting on multiple events concurrently, + * either events triggered directly, or from kernel objects or other kernel + * constructs. + */ + +#include + +#include +#include +#include +#include +#include + +struct k_sem g_poll_sem; + +void k_poll_event_init(struct k_poll_event *event, u32_t type, + int mode, void *obj) +{ + __ASSERT(mode == K_POLL_MODE_NOTIFY_ONLY, + "only NOTIFY_ONLY mode is supported\n"); + __ASSERT(type < (1 << _POLL_NUM_TYPES), "invalid type\n"); + __ASSERT(obj, "must provide an object\n"); + + event->poller = NULL; + /* event->tag is left uninitialized: the user will set it if needed */ + event->type = type; + event->state = K_POLL_STATE_NOT_READY; + event->mode = mode; + event->unused = 0; + event->obj = obj; +} + +/* must be called with interrupts locked */ +static inline int is_condition_met(struct k_poll_event *event, u32_t *state) +{ + switch (event->type) { + case K_POLL_TYPE_SEM_AVAILABLE: + if (k_sem_count_get(event->sem) > 0) { + *state = K_POLL_STATE_SEM_AVAILABLE; + return 1; + } + break; + case K_POLL_TYPE_DATA_AVAILABLE: + if (!k_queue_is_empty(event->queue)) { + *state = K_POLL_STATE_FIFO_DATA_AVAILABLE; + return 1; + } + break; + case K_POLL_TYPE_SIGNAL: + if (event->signal->signaled) { + *state = K_POLL_STATE_SIGNALED; + return 1; + } + break; + case K_POLL_TYPE_IGNORE: + return 0; + default: + __ASSERT(0, "invalid event type (0x%x)\n", event->type); + break; + } + + return 0; +} + +static inline void add_event(sys_dlist_t *events, struct k_poll_event *event, + struct _poller *poller) +{ + sys_dlist_append(events, &event->_node); +} + +/* must be called with interrupts locked */ +static inline int register_event(struct k_poll_event *event, + struct _poller *poller) +{ + switch (event->type) { + case K_POLL_TYPE_SEM_AVAILABLE: + __ASSERT(event->sem, "invalid semaphore\n"); + add_event(&event->sem->poll_events, event, poller); + break; + case K_POLL_TYPE_DATA_AVAILABLE: + __ASSERT(event->queue, "invalid queue\n"); + add_event(&event->queue->poll_events, event, poller); + break; + case K_POLL_TYPE_SIGNAL: + __ASSERT(event->signal, "invalid poll signal\n"); + add_event(&event->signal->poll_events, event, poller); + break; + case K_POLL_TYPE_IGNORE: + /* nothing to do */ + break; + default: + __ASSERT(0, "invalid event type\n"); + break; + } + + event->poller = poller; + + return 0; +} + +/* must be called with interrupts locked */ +static inline void clear_event_registration(struct k_poll_event *event) +{ + event->poller = NULL; + + switch (event->type) { + case K_POLL_TYPE_SEM_AVAILABLE: + __ASSERT(event->sem, "invalid semaphore\n"); + sys_dlist_remove(&event->_node); + break; + case K_POLL_TYPE_DATA_AVAILABLE: + __ASSERT(event->queue, "invalid queue\n"); + sys_dlist_remove(&event->_node); + break; + case K_POLL_TYPE_SIGNAL: + __ASSERT(event->signal, "invalid poll signal\n"); + sys_dlist_remove(&event->_node); + break; + case K_POLL_TYPE_IGNORE: + /* nothing to do */ + break; + default: + __ASSERT(0, "invalid event type\n"); + break; + } +} + +/* must be called with interrupts locked */ +static inline void clear_event_registrations(struct k_poll_event *events, + int last_registered, + unsigned int key) +{ + for (; last_registered >= 0; last_registered--) { + clear_event_registration(&events[last_registered]); + irq_unlock(key); + key = irq_lock(); + } +} + +static inline void set_event_ready(struct k_poll_event *event, u32_t state) +{ + event->poller = NULL; + event->state |= state; +} + +static bool polling_events(struct k_poll_event *events, int num_events, + s32_t timeout, int *last_registered) +{ + int rc; + bool polling = true; + unsigned int key; + + for (int ii = 0; ii < num_events; ii++) { + u32_t state; + key = irq_lock(); + if (is_condition_met(&events[ii], &state)) { + set_event_ready(&events[ii], state); + polling = false; + } else if (timeout != K_NO_WAIT && polling) { + rc = register_event(&events[ii], NULL); + if (rc == 0) { + ++(*last_registered); + } else { + __ASSERT(0, "unexpected return code\n"); + } + } + irq_unlock(key); + } + return polling; +} + +int k_poll(struct k_poll_event *events, int num_events, s32_t timeout) +{ + __ASSERT(events, "NULL events\n"); + __ASSERT(num_events > 0, "zero events\n"); + + int last_registered = -1; + unsigned int key; + bool polling = true; + + /* find events whose condition is already fulfilled */ + polling = polling_events(events, num_events, timeout, &last_registered); + + if (polling == false) { + goto exit; + } + + k_sem_take(&g_poll_sem, timeout); + + last_registered = -1; + polling_events(events, num_events, timeout, &last_registered); +exit: + key = irq_lock(); + clear_event_registrations(events, last_registered, key); + irq_unlock(key); + return 0; +} + +/* must be called with interrupts locked */ +static int _signal_poll_event(struct k_poll_event *event, u32_t state, + int *must_reschedule) +{ + *must_reschedule = 0; + set_event_ready(event, state); + return 0; +} + +int k_poll_signal_raise(struct k_poll_signal *signal, int result) +{ + unsigned int key = irq_lock(); + struct k_poll_event *poll_event; + int must_reschedule; + + signal->result = result; + signal->signaled = 1; + + poll_event = (struct k_poll_event *)sys_dlist_get(&signal->poll_events); + if (!poll_event) { + irq_unlock(key); + return 0; + } + + int rc = _signal_poll_event(poll_event, K_POLL_STATE_SIGNALED, + &must_reschedule); + + k_sem_give(&g_poll_sem); + irq_unlock(key); + return rc; +} diff --git a/components/ble/ble_stack/common/rpa.c b/components/ble/ble_stack/common/rpa.c new file mode 100644 index 00000000..345486b4 --- /dev/null +++ b/components/ble/ble_stack/common/rpa.c @@ -0,0 +1,106 @@ +/** + * @file rpa.c + * Resolvable Private Address Generation and Resolution + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include <../include/bluetooth/crypto.h> + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_RPA) +#define LOG_MODULE_NAME bt_rpa +#include "log.h" + +#if defined(CONFIG_BT_CTLR_PRIVACY) || defined(CONFIG_BT_PRIVACY) || defined(CONFIG_BT_SMP) +static int ah(const u8_t irk[16], const u8_t r[3], u8_t out[3]) +{ + u8_t res[16]; + int err; + + BT_DBG("irk %s", bt_hex(irk, 16)); + BT_DBG("r %s", bt_hex(r, 3)); + + /* r' = padding || r */ + memcpy(res, r, 3); + (void)memset(res + 3, 0, 13); + + err = bt_encrypt_le(irk, res, res); + if (err) { + return err; + } + + /* The output of the random address function ah is: + * ah(h, r) = e(k, r') mod 2^24 + * The output of the security function e is then truncated to 24 bits + * by taking the least significant 24 bits of the output of e as the + * result of ah. + */ + memcpy(out, res, 3); + + return 0; +} +#endif + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_CTLR_PRIVACY) +bool bt_rpa_irk_matches(const u8_t irk[16], const bt_addr_t *addr) +{ + u8_t hash[3]; + int err; + + BT_DBG("IRK %s bdaddr %s", bt_hex(irk, 16), bt_addr_str(addr)); + + err = ah(irk, addr->val + 3, hash); + if (err) { + return false; + } + + return !memcmp(addr->val, hash, 3); +} +#endif + +#if defined(CONFIG_BT_PRIVACY) || defined(CONFIG_BT_CTLR_PRIVACY) +int bt_rpa_create(const u8_t irk[16], bt_addr_t *rpa) +{ + int err; + + err = bt_rand(rpa->val + 3, 3); + if (err) { + return err; + } + + BT_ADDR_SET_RPA(rpa); + + err = ah(irk, rpa->val + 3, rpa->val); + if (err) { + return err; + } + + BT_DBG("Created RPA %s", bt_addr_str((bt_addr_t *)rpa->val)); + + return 0; +} +#else +int bt_rpa_create(const u8_t irk[16], bt_addr_t *rpa) +{ + return -ENOTSUP; +} +#endif /* CONFIG_BT_PRIVACY */ + diff --git a/components/ble/ble_stack/common/rpa.h b/components/ble/ble_stack/common/rpa.h new file mode 100644 index 00000000..dca68f15 --- /dev/null +++ b/components/ble/ble_stack/common/rpa.h @@ -0,0 +1,16 @@ +/* rpa.h - Bluetooth Resolvable Private Addresses (RPA) generation and + * resolution + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bluetooth.h" +#include "hci_host.h" + +bool bt_rpa_irk_matches(const u8_t irk[16], const bt_addr_t *addr); +int bt_rpa_create(const u8_t irk[16], bt_addr_t *rpa); diff --git a/components/ble/ble_stack/common/tinycrypt/Kconfig b/components/ble/ble_stack/common/tinycrypt/Kconfig new file mode 100644 index 00000000..1f62654e --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/Kconfig @@ -0,0 +1,116 @@ +# Kconfig - Cryptography primitive options for TinyCrypt version 2.0 + +# +# Copyright (c) 2015 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config TINYCRYPT + bool + prompt "TinyCrypt Support" + default n + help + This option enables the TinyCrypt cryptography library. + +config TINYCRYPT_CTR_PRNG + bool + prompt "PRNG in counter mode" + depends on TINYCRYPT + default n + help + This option enables support for the pseudo-random number + generator in counter mode. + +config TINYCRYPT_SHA256 + bool + prompt "SHA-256 Hash function support" + depends on TINYCRYPT + default n + help + This option enables support for SHA-256 + hash function primitive. + +config TINYCRYPT_SHA256_HMAC + bool + prompt "HMAC (via SHA256) message auth support" + depends on TINYCRYPT_SHA256 + default n + help + This option enables support for HMAC using SHA-256 + message authentication code. + +config TINYCRYPT_SHA256_HMAC_PRNG + bool + prompt "PRNG (via HMAC-SHA256) support" + depends on TINYCRYPT_SHA256_HMAC + default n + help + This option enables support for pseudo-random number + generator. + +config TINYCRYPT_ECC_DH + bool + prompt "ECC_DH anonymous key agreement protocol" + depends on TINYCRYPT + select ENTROPY_GENERATOR + default n + help + This option enables support for the Elliptic curve + Diffie-Hellman anonymous key agreement protocol. + + Enabling ECC requires a cryptographically secure random number + generator. + +config TINYCRYPT_ECC_DSA + bool + prompt "ECC_DSA digital signature algorithm" + depends on TINYCRYPT + select ENTROPY_GENERATOR + default n + help + This option enables support for the Elliptic Curve Digital + Signature Algorithm (ECDSA). + + Enabling ECC requires a cryptographically secure random number + generator. + +config TINYCRYPT_AES + bool + prompt "AES-128 decrypt/encrypt" + depends on TINYCRYPT + default n + help + This option enables support for AES-128 decrypt and encrypt. + +config TINYCRYPT_AES_CBC + bool + prompt "AES-128 block cipher" + depends on TINYCRYPT_AES + default n + help + This option enables support for AES-128 block cipher mode. + +config TINYCRYPT_AES_CTR + bool + prompt "AES-128 counter mode" + depends on TINYCRYPT_AES + default n + help + This option enables support for AES-128 counter mode. + +config TINYCRYPT_AES_CCM + bool + prompt "AES-128 CCM mode" + depends on TINYCRYPT_AES + default n + help + This option enables support for AES-128 CCM mode. + +config TINYCRYPT_AES_CMAC + bool + prompt "AES-128 CMAC mode" + depends on TINYCRYPT_AES + default n + help + This option enables support for AES-128 CMAC mode. diff --git a/components/ble/ble_stack/common/tinycrypt/README b/components/ble/ble_stack/common/tinycrypt/README new file mode 100644 index 00000000..20cbc5ab --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/README @@ -0,0 +1,71 @@ +The TinyCrypt library in Zephyr is a downstream of an externally maintained +open source project. The original upstream code can be found at: + +https://github.com/01org/tinycrypt + +At revision c214460d7f760e2a75908cb41000afcc0bfca282, version 0.2.7 + +Any changes to the local version should include Zephyr's TinyCrypt +maintainer in the review. That can be found via the git history. + +The following is the license information for this code: + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ + +Copyright (c) 2013, Kenneth MacKay +All rights reserved. + +https://github.com/kmackay/micro-ecc + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h new file mode 100644 index 00000000..b612213a --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h @@ -0,0 +1,130 @@ +/* aes.h - TinyCrypt interface to an AES-128 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to an AES-128 implementation. + * + * Overview: AES-128 is a NIST approved block cipher specified in + * FIPS 197. Block ciphers are deterministic algorithms that + * perform a transformation specified by a symmetric key in fixed- + * length data sets, also called blocks. + * + * Security: AES-128 provides approximately 128 bits of security. + * + * Usage: 1) call tc_aes128_set_encrypt/decrypt_key to set the key. + * + * 2) call tc_aes_encrypt/decrypt to process the data. + */ + +#ifndef __TC_AES_H__ +#define __TC_AES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Nb (4) /* number of columns (32-bit words) comprising the state */ +#define Nk (4) /* number of 32-bit words comprising the key */ +#define Nr (10) /* number of rounds */ +#define TC_AES_BLOCK_SIZE (Nb*Nk) +#define TC_AES_KEY_SIZE (Nb*Nk) + +typedef struct tc_aes_key_sched_struct { + unsigned int words[Nb*(Nr+1)]; +} *TCAesKeySched_t; + +/** + * @brief Set AES-128 encryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This implementation skips the additional steps required for keys + * larger than 128 bits, and must not be used for AES-192 or + * AES-256 key schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Encrypts contents of in buffer into out buffer under key; + * schedule s + * @note Assumes s was initialized by aes_set_encrypt_key; + * out and in point to 16 byte buffers + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out == NULL or in == NULL or s == NULL + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +/** + * @brief Set the AES-128 decryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This is the implementation of the straightforward inverse cipher + * using the cipher documented in FIPS-197 figure 12, not the + * equivalent inverse cipher presented in Figure 15 + * @warning This routine skips the additional steps required for keys larger + * than 128, and must not be used for AES-192 or AES-256 key + * schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Decrypts in buffer into out buffer under key schedule s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out is NULL or in is NULL or s is NULL + * @note Assumes s was initialized by aes_set_encrypt_key + * out and in point to 16 byte buffers + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_AES_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h new file mode 100644 index 00000000..a53318ee --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h @@ -0,0 +1,151 @@ +/* cbc_mode.h - TinyCrypt interface to a CBC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CBC mode implementation. + * + * Overview: CBC (for "cipher block chaining") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any block + * cipher to provide confidentiality of strings whose lengths are + * multiples of the block_size of the underlying block cipher. + * TinyCrypt hard codes AES as the block cipher. + * + * Security: CBC mode provides data confidentiality given that the maximum + * number q of blocks encrypted under a single key satisfies + * q < 2^63, which is not a practical constraint (it is considered a + * good practice to replace the encryption when q == 2^56). CBC mode + * provides NO data integrity. + * + * CBC mode assumes that the IV value input into the + * tc_cbc_mode_encrypt is randomly generated. The TinyCrypt library + * provides HMAC-PRNG module, which generates suitable IVs. Other + * methods for generating IVs are acceptable, provided that the + * values of the IVs generated appear random to any adversary, + * including someone with complete knowledge of the system design. + * + * The randomness property on which CBC mode's security depends is + * the unpredictability of the IV. Since it is unpredictable, this + * means in practice that CBC mode requires that the IV is stored + * somehow with the ciphertext in order to recover the plaintext. + * + * TinyCrypt CBC encryption prepends the IV to the ciphertext, + * because this affords a more efficient (few buffers) decryption. + * Hence tc_cbc_mode_encrypt assumes the ciphertext buffer is always + * 16 bytes larger than the plaintext buffer. + * + * Requires: AES-128 + * + * Usage: 1) call tc_cbc_mode_encrypt to encrypt data. + * + * 2) call tc_cbc_mode_decrypt to decrypt data. + * + */ + +#ifndef __TC_CBC_MODE_H__ +#define __TC_CBC_MODE_H__ + +#include "aes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CBC encryption procedure + * CBC encrypts inlen bytes of the in buffer into the out buffer + * using the encryption key schedule provided, prepends iv to out + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes: - sched has been configured by aes_set_encrypt_key + * - iv contains a 16 byte random string + * - out buffer is large enough to hold the ciphertext + iv + * - out buffer is a contiguous buffer + * - in holds the plaintext and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive the ciphertext + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- plaintext to encrypt + * @param inlen IN -- length of plaintext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this encrypt + */ +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +/** + * @brief CBC decryption procedure + * CBC decrypts inlen bytes of the in buffer into the out buffer + * using the provided encryption key schedule + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes:- in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption + * algorithm that would not otherwise be possible + * - sched was configured by aes_set_decrypt_key + * - out buffer is large enough to hold the decrypted plaintext + * and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive decrypted data + * @param outlen IN -- length of plaintext buffer in bytes + * @param in IN -- ciphertext to decrypt, including IV + * @param inlen IN -- length of ciphertext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this decrypt + * + */ +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CBC_MODE_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h new file mode 100644 index 00000000..c22ac08d --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h @@ -0,0 +1,211 @@ +/* ccm_mode.h - TinyCrypt interface to a CCM mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CCM mode implementation. + * + * Overview: CCM (for "Counter with CBC-MAC") mode is a NIST approved mode of + * operation defined in SP 800-38C. + * + * TinyCrypt CCM implementation accepts: + * + * 1) Both non-empty payload and associated data (it encrypts and + * authenticates the payload and also authenticates the associated + * data); + * 2) Non-empty payload and empty associated data (it encrypts and + * authenticates the payload); + * 3) Non-empty associated data and empty payload (it degenerates to + * an authentication mode on the associated data). + * + * TinyCrypt CCM implementation accepts associated data of any length + * between 0 and (2^16 - 2^8) bytes. + * + * Security: The mac length parameter is an important parameter to estimate the + * security against collision attacks (that aim at finding different + * messages that produce the same authentication tag). TinyCrypt CCM + * implementation accepts any even integer between 4 and 16, as + * suggested in SP 800-38C. + * + * RFC-3610, which also specifies CCM, presents a few relevant + * security suggestions, such as: it is recommended for most + * applications to use a mac length greater than 8. Besides, the + * usage of the same nonce for two different messages which are + * encrypted with the same key destroys the security of CCM mode. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ccm_config to configure. + * + * 2) call tc_ccm_mode_encrypt to encrypt data and generate tag. + * + * 3) call tc_ccm_mode_decrypt to decrypt data and verify tag. + */ + +#ifndef __TC_CCM_MODE_H__ +#define __TC_CCM_MODE_H__ + +#include "aes.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* max additional authenticated size in bytes: 2^16 - 2^8 = 65280 */ +#define TC_CCM_AAD_MAX_BYTES 0xff00 + +/* max message size in bytes: 2^(8L) = 2^16 = 65536 */ +#define TC_CCM_PAYLOAD_MAX_BYTES 0x10000 + +/* struct tc_ccm_mode_struct represents the state of a CCM computation */ +typedef struct tc_ccm_mode_struct { + TCAesKeySched_t sched; /* AES key schedule */ + uint8_t *nonce; /* nonce required by CCM */ + unsigned int mlen; /* mac length in bytes (parameter t in SP-800 38C) */ +} *TCCcmMode_t; + +/** + * @brief CCM configuration procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * c == NULL or + * sched == NULL or + * nonce == NULL or + * mlen != {4, 6, 8, 10, 12, 16} + * @param c -- CCM state + * @param sched IN -- AES key schedule + * @param nonce IN - nonce + * @param nlen -- nonce length in bytes + * @param mlen -- mac length in bytes (parameter t in SP-800 38C) + */ +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen); + +/** + * @brief CCM tag generation and encryption procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen + maclength) + * + * @param out OUT -- encrypted data + * @param olen IN -- output length in bytes + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen + c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c); + +/** + * @brief CCM decryption and tag verification procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen - c->mlen) + * + * @param out OUT -- decrypted data + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen - c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, unsigned int plen, + TCCcmMode_t c); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CCM_MODE_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h new file mode 100644 index 00000000..fa14d2aa --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h @@ -0,0 +1,194 @@ +/* cmac_mode.h -- interface to a CMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CMAC implementation. + * + * Overview: CMAC is defined NIST in SP 800-38B, and is the standard algorithm + * for computing a MAC using a block cipher. It can compute the MAC + * for a byte string of any length. It is distinguished from CBC-MAC + * in the processing of the final message block; CMAC uses a + * different technique to compute the final message block is full + * size or only partial, while CBC-MAC uses the same technique for + * both. This difference permits CMAC to be applied to variable + * length messages, while all messages authenticated by CBC-MAC must + * be the same length. + * + * Security: AES128-CMAC mode of operation offers 64 bits of security against + * collision attacks. Note however that an external attacker cannot + * generate the tags him/herself without knowing the MAC key. In this + * sense, to attack the collision property of AES128-CMAC, an + * external attacker would need the cooperation of the legal user to + * produce an exponentially high number of tags (e.g. 2^64) to + * finally be able to look for collisions and benefit from them. As + * an extra precaution, the current implementation allows to at most + * 2^48 calls to the tc_cmac_update function before re-calling + * tc_cmac_setup (allowing a new key to be set), as suggested in + * Appendix B of SP 800-38B. + * + * Requires: AES-128 + * + * Usage: This implementation provides a "scatter-gather" interface, so that + * the CMAC value can be computed incrementally over a message + * scattered in different segments throughout memory. Experience shows + * this style of interface tends to minimize the burden of programming + * correctly. Like all symmetric key operations, it is session + * oriented. + * + * To begin a CMAC session, use tc_cmac_setup to initialize a struct + * tc_cmac_struct with encryption key and buffer. Our implementation + * always assume that the AES key to be the same size as the block + * cipher block size. Once setup, this data structure can be used for + * many CMAC computations. + * + * Once the state has been setup with a key, computing the CMAC of + * some data requires three steps: + * + * (1) first use tc_cmac_init to initialize a new CMAC computation. + * (2) next mix all of the data into the CMAC computation state using + * tc_cmac_update. If all of the data resides in a single data + * segment then only one tc_cmac_update call is needed; if data + * is scattered throughout memory in n data segments, then n calls + * will be needed. CMAC IS ORDER SENSITIVE, to be able to detect + * attacks that swap bytes, so the order in which data is mixed + * into the state is critical! + * (3) Once all of the data for a message has been mixed, use + * tc_cmac_final to compute the CMAC tag value. + * + * Steps (1)-(3) can be repeated as many times as you want to CMAC + * multiple messages. A practical limit is 2^48 1K messages before you + * have to change the key. + * + * Once you are done computing CMAC with a key, it is a good idea to + * destroy the state so an attacker cannot recover the key; use + * tc_cmac_erase to accomplish this. + */ + +#ifndef __TC_CMAC_MODE_H__ +#define __TC_CMAC_MODE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* padding for last message block */ +#define TC_CMAC_PADDING 0x80 + +/* struct tc_cmac_struct represents the state of a CMAC computation */ +typedef struct tc_cmac_struct { +/* initialization vector */ + uint8_t iv[TC_AES_BLOCK_SIZE]; +/* used if message length is a multiple of block_size bytes */ + uint8_t K1[TC_AES_BLOCK_SIZE]; +/* used if message length isn't a multiple block_size bytes */ + uint8_t K2[TC_AES_BLOCK_SIZE]; +/* where to put bytes that didn't fill a block */ + uint8_t leftover[TC_AES_BLOCK_SIZE]; +/* identifies the encryption key */ + unsigned int keyid; +/* next available leftover location */ + unsigned int leftover_offset; +/* AES key schedule */ + TCAesKeySched_t sched; +/* calls to tc_cmac_update left before re-key */ + uint64_t countdown; +} *TCCmacState_t; + +/** + * @brief Configures the CMAC state to use the given AES key + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * key == NULL + * + * @param s IN/OUT -- the state to set up + * @param key IN -- the key to use + * @param sched IN -- AES key schedule + */ +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, + TCAesKeySched_t sched); + +/** + * @brief Erases the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to erase + */ +int tc_cmac_erase(TCCmacState_t s); + +/** + * @brief Initializes a new CMAC computation + * @return returns TC_CRYPTO_SUCCESS (1) after having initialized the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to initialize + */ +int tc_cmac_init(TCCmacState_t s); + +/** + * @brief Incrementally computes CMAC over the next data segment + * @return returns TC_CRYPTO_SUCCESS (1) after successfully updating the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * if data == NULL when dlen > 0 + * + * @param s IN/OUT -- the CMAC state + * @param data IN -- the next data segment to MAC + * @param dlen IN -- the length of data in bytes + */ +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t dlen); + +/** + * @brief Generates the tag from the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after successfully generating the tag + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * s == NULL + * + * @param tag OUT -- the CMAC tag + * @param s IN -- CMAC state + */ +int tc_cmac_final(uint8_t *tag, TCCmacState_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CMAC_MODE_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h new file mode 100644 index 00000000..965490e0 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h @@ -0,0 +1,61 @@ +/* constants.h - TinyCrypt interface to constants */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to constants. + * + */ + +#ifndef __TC_CONSTANTS_H__ +#define __TC_CONSTANTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define TC_CRYPTO_SUCCESS 1 +#define TC_CRYPTO_FAIL 0 + +#define TC_ZERO_BYTE 0x00 + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CONSTANTS_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h new file mode 100644 index 00000000..e2da5b43 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h @@ -0,0 +1,108 @@ +/* ctr_mode.h - TinyCrypt interface to CTR mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to CTR mode. + * + * Overview: CTR (pronounced "counter") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any + * block cipher to provide confidentiality of strings of any + * length. TinyCrypt hard codes AES128 as the block cipher. + * + * Security: CTR mode achieves confidentiality only if the counter value is + * never reused with a same encryption key. If the counter is + * repeated, than an adversary might be able to defeat the scheme. + * + * A usual method to ensure different counter values refers to + * initialize the counter in a given value (0, for example) and + * increases it every time a new block is enciphered. This naturally + * leaves to a limitation on the number q of blocks that can be + * enciphered using a same key: q < 2^(counter size). + * + * TinyCrypt uses a counter of 32 bits. This means that after 2^32 + * block encryptions, the counter will be reused (thus losing CBC + * security). 2^32 block encryptions should be enough for most of + * applications targeting constrained devices. Applications intended + * to encrypt a larger number of blocks must replace the key after + * 2^32 block encryptions. + * + * CTR mode provides NO data integrity. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ctr_mode to process the data to encrypt/decrypt. + * + */ + +#ifndef __TC_CTR_MODE_H__ +#define __TC_CTR_MODE_H__ + +#include "aes.h" +#include "constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CTR mode encryption/decryption procedure. + * CTR mode encrypts (or decrypts) inlen bytes from in buffer into out buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * inlen != outlen + * @note Assumes:- The current value in ctr has NOT been used with sched + * - out points to inlen bytes + * - in points to inlen bytes + * - ctr is an integer counter in littleEndian format + * - sched was initialized by aes_set_encrypt_key + * @param out OUT -- produced ciphertext (plaintext) + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- data to encrypt (or decrypt) + * @param inlen IN -- length of input data in bytes + * @param ctr IN/OUT -- the current counter value + * @param sched IN -- an initialized AES key schedule + */ +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_MODE_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h new file mode 100644 index 00000000..bff7f972 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h @@ -0,0 +1,166 @@ +/* ctr_prng.h - TinyCrypt interface to a CTR-PRNG implementation */ + +/* + * Copyright (c) 2016, Chris Morrison + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a CTR-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the CTR-PRNG one + * which is based on AES. TinyCrypt implements CTR-PRNG with + * AES-128. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (AES-128 + * in this instance). + * + * Requires: - AES-128 + * + * Usage: 1) call tc_ctr_prng_init to seed the prng context + * + * 2) call tc_ctr_prng_reseed to mix in additional entropy into + * the prng context + * + * 3) call tc_ctr_prng_generate to output the pseudo-random data + * + * 4) call tc_ctr_prng_uninstantiate to zero out the prng context + */ + +#ifndef __TC_CTR_PRNG_H__ +#define __TC_CTR_PRNG_H__ + +#include "aes.h" + +#define TC_CTR_PRNG_RESEED_REQ -1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* updated each time another BLOCKLEN_BYTES bytes are produced */ + uint8_t V[TC_AES_BLOCK_SIZE]; + + /* updated whenever the PRNG is reseeded */ + struct tc_aes_key_sched_struct key; + + /* number of requests since initialization/reseeding */ + uint64_t reseedCount; +} TCCtrPrng_t; + + +/** + * @brief CTR-PRNG initialization procedure + * Initializes prng context with entropy and personalization string (if any) + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropyLen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note Only the first (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes of + * both the entropy and personalization inputs are used - + * supplying additional bytes has no effect. + * @param ctx IN/OUT -- the PRNG context to initialize + * @param entropy IN -- entropy used to seed the PRNG + * @param entropyLen IN -- entropy length in bytes + * @param personalization IN -- personalization string used to seed the PRNG + * (may be null) + * @param plen IN -- personalization length in bytes + * + */ +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen); + +/** + * @brief CTR-PRNG reseed procedure + * Mixes entropy and additional_input into the prng context + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropylen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note It is better to reseed an existing prng context rather than + * re-initialise, so that any existing entropy in the context is + * presereved. This offers some protection against undetected failures + * of the entropy source. + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG state + * @param entropy IN -- entropy to mix into the prng + * @param entropylen IN -- length of entropy in bytes + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + */ +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen); + +/** + * @brief CTR-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CTR_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * out == NULL, + * outlen >= 2^16 + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG context + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + */ +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen); + +/** + * @brief CTR-PRNG uninstantiate procedure + * Zeroes the internal state of the supplied prng context + * @return none + * @param ctx IN/OUT -- the PRNG context + */ +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_PRNG_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h new file mode 100644 index 00000000..8abc949c --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h @@ -0,0 +1,545 @@ +/* ecc.h - TinyCrypt interface to common ECC functions */ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to common ECC functions. + * + * Overview: This software is an implementation of common functions + * necessary to elliptic curve cryptography. This implementation uses + * curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + */ + +#ifndef __TC_UECC_H__ +#define __TC_UECC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Word size (4 bytes considering 32-bits architectures) */ +#define uECC_WORD_SIZE 4 + +/* setting max number of calls to prng: */ +#ifndef uECC_RNG_MAX_TRIES +#define uECC_RNG_MAX_TRIES 64 +#endif + +/* defining data types to store word and bit counts: */ +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +/* defining data type for comparison result: */ +typedef int8_t cmpresult_t; +/* defining data type to store ECC coordinate/point in 32bits words: */ +typedef unsigned int uECC_word_t; +/* defining data type to store an ECC coordinate/point in 64bits words: */ +typedef uint64_t uECC_dword_t; + +/* defining masks useful for ecc computations: */ +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +/* Number of words of 32 bits to represent an element of the the curve p-256: */ +#define NUM_ECC_WORDS 8 +/* Number of bytes to represent an element of the the curve p-256: */ +#define NUM_ECC_BYTES (uECC_WORD_SIZE*NUM_ECC_WORDS) + +/* structure that represents an elliptic curve (e.g. p256):*/ +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[NUM_ECC_WORDS]; + uECC_word_t n[NUM_ECC_WORDS]; + uECC_word_t G[NUM_ECC_WORDS * 2]; + uECC_word_t b[NUM_ECC_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * Z1, + uECC_Curve curve); + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +}; + +/* + * @brief computes doubling of point ion jacobian coordinates, in place. + * @param X1 IN/OUT -- x coordinate + * @param Y1 IN/OUT -- y coordinate + * @param Z1 IN/OUT -- z coordinate + * @param curve IN -- elliptic curve + */ +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve); + +/* + * @brief Computes x^3 + ax + b. result must not overlap x. + * @param result OUT -- x^3 + ax + b + * @param x IN -- value of x + * @param curve IN -- elliptic curve + */ +void x_side_default(uECC_word_t *result, const uECC_word_t *x, + uECC_Curve curve); + +/* + * @brief Computes result = product % curve_p + * from http://www.nsa.gov/ia/_files/nist-routines.pdf + * @param result OUT -- product % curve_p + * @param product IN -- value to be reduced mod curve_p + */ +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int *product); + +/* Bytes to words ordering: */ +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a +#define BITS_TO_WORDS(num_bits) \ + ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) +#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) + +/* definition of curve NIST p-256: */ +static const struct uECC_Curve_t curve_secp256r1 = { + NUM_ECC_WORDS, + NUM_ECC_BYTES, + 256, /* num_n_bits */ { + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) + }, { + BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), + BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) + }, { + BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), + BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), + BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), + BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), + + BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), + BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), + BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), + BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) + }, { + BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), + BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), + BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), + BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) + }, + &double_jacobian_default, + &x_side_default, + &vli_mmod_fast_secp256r1 +}; + +uECC_Curve uECC_secp256r1(void); + +/* + * @brief Generates a random integer in the range 0 < random < top. + * Both random and top have num_words words. + * @param random OUT -- random integer in the range 0 < random < top + * @param top IN -- upper limit + * @param num_words IN -- number of words + * @return a random integer in the range 0 < random < top + */ +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words); + + +/* uECC_RNG_Function type + * The RNG function should fill 'size' random bytes into 'dest'. It should + * return 1 if 'dest' was filled with random data, or 0 if the random data could + * not be generated. The filled-in values should be either truly random, or from + * a cryptographically-secure PRNG. + * + * A correctly functioning RNG function must be set (using uECC_set_rng()) + * before calling uECC_make_key() or uECC_sign(). + * + * Setting a correctly functioning RNG function improves the resistance to + * side-channel attacks for uECC_shared_secret(). + * + * A correct RNG function is set by default. If you are building on another + * POSIX-compliant system that supports /dev/random or /dev/urandom, you can + * define uECC_POSIX to use the predefined RNG. + */ +typedef int(*uECC_RNG_Function)(uint8_t *dest, unsigned int size); + +/* + * @brief Set the function that will be used to generate random bytes. The RNG + * function should return 1 if the random data was generated, or 0 if the random + * data could not be generated. + * + * @note On platforms where there is no predefined RNG function, this must be + * called before uECC_make_key() or uECC_sign() are used. + * + * @param rng_function IN -- function that will be used to generate random bytes + */ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* + * @brief provides current uECC_RNG_Function. + * @return Returns the function that will be used to generate random bytes. + */ +uECC_RNG_Function uECC_get_rng(void); + +/* + * @brief computes the size of a private key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return size of a private key for the curve in bytes. + */ +int uECC_curve_private_key_size(uECC_Curve curve); + +/* + * @brief computes the size of a public key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return the size of a public key for the curve in bytes. + */ +int uECC_curve_public_key_size(uECC_Curve curve); + +/* + * @brief Compute the corresponding public key for a private key. + * @param private_key IN -- The private key to compute the public key for + * @param public_key OUT -- Will be filled in with the corresponding public key + * @param curve + * @return Returns 1 if key was computed successfully, 0 if an error occurred. + */ +int uECC_compute_public_key(const uint8_t *private_key, + uint8_t *public_key, uECC_Curve curve); + +/* + * @brief Compute public-key. + * @return corresponding public-key. + * @param result OUT -- public-key + * @param private_key IN -- private-key + * @param curve IN -- elliptic curve + */ +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, uECC_Curve curve); + +/* + * @brief Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. + * @return Regularized k + * @param k IN -- private-key + * @param k0 IN/OUT -- regularized k + * @param k1 IN/OUT -- regularized k + * @param curve IN -- elliptic curve + */ +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve); + +/* + * @brief Point multiplication algorithm using Montgomery's ladder with co-Z + * coordinates. See http://eprint.iacr.org/2011/338.pdf. + * @note Result may overlap point. + * @param result OUT -- returns scalar*point + * @param point IN -- elliptic curve point + * @param scalar IN -- scalar + * @param initial_Z IN -- initial value for z + * @param num_bits IN -- number of bits in scalar + * @param curve IN -- elliptic curve + */ +void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point, + const uECC_word_t * scalar, const uECC_word_t * initial_Z, + bitcount_t num_bits, uECC_Curve curve); + +/* + * @brief Constant-time comparison to zero - secure way to compare long integers + * @param vli IN -- very long integer + * @param num_words IN -- number of words in the vli + * @return 1 if vli == 0, 0 otherwise. + */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief Check if 'point' is the point at infinity + * @param point IN -- elliptic curve point + * @param curve IN -- elliptic curve + * @return if 'point' is the point at infinity, 0 otherwise. + */ +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief computes the sign of left - right, in constant time. + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief computes sign of left - right, not in constant time. + * @note should not be used if inputs are part of a secret + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes result = (left - right) % mod. + * @note Assumes that (left < mod) and (right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left - right) % mod + * @param left IN -- leftright term in modular subtraction + * @param right IN -- right term in modular subtraction + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Computes P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) or + * P => P', Q => P + Q + * @note assumes Input P = (x1, y1, Z), Q = (x2, y2, Z) + * @param X1 IN -- x coordinate of P + * @param Y1 IN -- y coordinate of P + * @param X2 IN -- x coordinate of Q + * @param Y2 IN -- y coordinate of Q + * @param curve IN -- elliptic curve + */ +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * X2, + uECC_word_t * Y2, uECC_Curve curve); + +/* + * @brief Computes (x1 * z^2, y1 * z^3) + * @param X1 IN -- previous x1 coordinate + * @param Y1 IN -- previous y1 coordinate + * @param Z IN -- z value + * @param curve IN -- elliptic curve + */ +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve); + +/* + * @brief Check if bit is set. + * @return Returns nonzero if bit 'bit' of vli is set. + * @warning It is assumed that the value provided in 'bit' is within the + * boundaries of the word-array 'vli'. + * @note The bit ordering layout assumed for vli is: {31, 30, ..., 0}, + * {63, 62, ..., 32}, {95, 94, ..., 64}, {127, 126,..., 96} for a vli consisting + * of 4 uECC_word_t elements. + */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* + * @brief Computes result = product % mod, where product is 2N words long. + * @param result OUT -- product % mod + * @param mod IN -- module + * @param num_words IN -- number of words + * @warning Currently only designed to work for curve_p or curve_n. + */ +void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product, + const uECC_word_t *mod, wordcount_t num_words); + +/* + * @brief Computes modular product (using curve->mmod_fast) + * @param result OUT -- (left * right) mod % curve_p + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param curve IN -- elliptic curve + */ +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve); + +/* + * @brief Computes result = left - right. + * @note Can modify in place. + * @param result OUT -- left - right + * @param left IN -- left term in subtraction + * @param right IN -- right term in subtraction + * @param num_words IN -- number of words + * @return borrow + */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words); + +/* + * @brief Constant-time comparison function(secure way to compare long ints) + * @param left IN -- left term in comparison + * @param right IN -- right term in comparison + * @param num_words IN -- number of words + * @return Returns 0 if left == right, 1 otherwise. + */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes (left * right) % mod + * @param result OUT -- (left * right) % mod + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Computes (1 / input) % mod + * @note All VLIs are the same size. + * @note See "Euclid's GCD to Montgomery Multiplication to the Great Divide" + * @param result OUT -- (1 / input) % mod + * @param input IN -- value to be modular inverted + * @param mod IN -- mod + * @param num_words -- number of words + */ +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words); + +/* + * @brief Sets dest = src. + * @param dest OUT -- destination buffer + * @param src IN -- origin buffer + * @param num_words IN -- number of words + */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words); + +/* + * @brief Computes (left + right) % mod. + * @note Assumes that (left < mod) and right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left + right) % mod. + * @param left IN -- left term in addition + * @param right IN -- right term in addition + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words); + +/* + * @brief Counts the number of bits required to represent vli. + * @param vli IN -- very long integer + * @param max_words IN -- number of words + * @return number of bits in given vli + */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words); + +/* + * @brief Erases (set to 0) vli + * @param vli IN -- very long integer + * @param num_words IN -- number of words + */ +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief check if it is a valid point in the curve + * @param point IN -- point to be checked + * @param curve IN -- elliptic curve + * @return 0 if point is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + */ +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief Check if a public key is valid. + * @param public_key IN -- The public key to be checked. + * @return returns 0 if the public key is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + * @exception returns -4 if public key is the group generator. + * + * @note Note that you are not required to check for a valid public key before + * using any other uECC functions. However, you may wish to avoid spending CPU + * time computing a shared secret or verifying a signature using an invalid + * public key. + */ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + + /* + * @brief Converts an integer in uECC native format to big-endian bytes. + * @param bytes OUT -- bytes representation + * @param num_bytes IN -- number of bytes + * @param native IN -- uECC native representation + */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native); + +/* + * @brief Converts big-endian bytes to an integer in uECC native format. + * @param native OUT -- uECC native representation + * @param bytes IN -- bytes representation + * @param num_bytes IN -- number of bytes + */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UECC_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h new file mode 100644 index 00000000..930e9162 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h @@ -0,0 +1,131 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DH implementation */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to EC-DH implementation. + * + * Overview: This software is an implementation of EC-DH. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + */ + +#ifndef __TC_ECC_DH_H__ +#define __TC_ECC_DH_H__ + +#include "ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a public/private key pair. + * @return returns TC_CRYPTO_SUCCESS (1) if the key pair was generated successfully + * returns TC_CRYPTO_FAIL (0) if error while generating key pair + * + * @param p_public_key OUT -- Will be filled in with the public key. Must be at + * least 2 * the curve size (in bytes) long. For curve secp256r1, p_public_key + * must be 64 bytes long. + * @param p_private_key OUT -- Will be filled in with the private key. Must be as + * long as the curve order (for secp256r1, p_private_key must be 32 bytes long). + * + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_make_key(). + */ +int uECC_make_key(uint8_t *p_public_key, uint8_t *p_private_key, uECC_Curve curve); + +#ifdef ENABLE_TESTS + +/** + * @brief Create a public/private key pair given a specific d. + * + * @note THIS FUNCTION SHOULD BE CALLED ONLY FOR TEST PURPOSES. Refer to + * uECC_make_key() function for real applications. + */ +int uECC_make_key_with_d(uint8_t *p_public_key, uint8_t *p_private_key, + unsigned int *d, uECC_Curve curve); +#endif + +/** + * @brief Compute a shared secret given your secret key and someone else's + * public key. + * @return returns TC_CRYPTO_SUCCESS (1) if the shared secret was computed successfully + * returns TC_CRYPTO_FAIL (0) otherwise + * + * @param p_secret OUT -- Will be filled in with the shared secret value. Must be + * the same size as the curve size (for curve secp256r1, secret must be 32 bytes + * long. + * @param p_public_key IN -- The public key of the remote party. + * @param p_private_key IN -- Your private key. + * + * @warning It is recommended to use the output of uECC_shared_secret() as the + * input of a recommended Key Derivation Function (see NIST SP 800-108) in + * order to produce a cryptographically secure symmetric key. + */ +int uECC_shared_secret(const uint8_t *p_public_key, const uint8_t *p_private_key, + uint8_t *p_secret, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DH_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h new file mode 100644 index 00000000..8cb421b7 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h @@ -0,0 +1,139 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DSA implementation */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief -- Interface to EC-DSA implementation. + * + * Overview: This software is an implementation of EC-DSA. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + * Usage: - To sign: Compute a hash of the data you wish to sign (SHA-2 is + * recommended) and pass it in to ecdsa_sign function along with your + * private key and a random number. You must use a new non-predictable + * random number to generate each new signature. + * - To verify a signature: Compute the hash of the signed data using + * the same hash as the signer and pass it to this function along with + * the signer's public key and the signature values (r and s). + */ + +#ifndef __TC_ECC_DSA_H__ +#define __TC_ECC_DSA_H__ + +#include "ecc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate an ECDSA signature for a given hash value. + * @return returns TC_CRYPTO_SUCCESS (1) if the signature generated successfully + * returns TC_CRYPTO_FAIL (0) if an error occurred. + * + * @param p_private_key IN -- Your private key. + * @param p_message_hash IN -- The hash of the message to sign. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature OUT -- Will be filled in with the signature value. Must be + * at least 2 * curve size long (for secp256r1, signature must be 64 bytes long). + * + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_sign(). + * @note Usage: Compute a hash of the data you wish to sign (SHA-2 is + * recommended) and pass it in to this function along with your private key. + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + */ +int uECC_sign(const uint8_t *p_private_key, const uint8_t *p_message_hash, + unsigned p_hash_size, uint8_t *p_signature, uECC_Curve curve); + +#ifdef ENABLE_TESTS +/* + * THIS FUNCTION SHOULD BE CALLED FOR TEST PURPOSES ONLY. + * Refer to uECC_sign() function for real applications. + */ +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned int hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve); +#endif + +/** + * @brief Verify an ECDSA signature. + * @return returns TC_SUCCESS (1) if the signature is valid + * returns TC_FAIL (0) if the signature is invalid. + * + * @param p_public_key IN -- The signer's public key. + * @param p_message_hash IN -- The hash of the signed data. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature IN -- The signature values. + * + * @note Usage: Compute the hash of the signed data using the same hash as the + * signer and pass it to this function along with the signer's public key and + * the signature values (hash_size and signature). + */ +int uECC_verify(const uint8_t *p_public_key, const uint8_t *p_message_hash, + unsigned int p_hash_size, const uint8_t *p_signature, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DSA_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h new file mode 100644 index 00000000..e2c88235 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h @@ -0,0 +1,81 @@ +/* uECC_platform_specific.h - Interface to platform specific functions*/ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * uECC_platform_specific.h -- Interface to platform specific functions + */ + +#ifndef __UECC_PLATFORM_SPECIFIC_H_ +#define __UECC_PLATFORM_SPECIFIC_H_ + +/* + * The RNG function should fill 'size' random bytes into 'dest'. It should + * return 1 if 'dest' was filled with random data, or 0 if the random data could + * not be generated. The filled-in values should be either truly random, or from + * a cryptographically-secure PRNG. + * + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + * before calling uECC_make_key() or uECC_sign(). + * + * Setting a cryptographically-secure PRNG function improves the resistance to + * side-channel attacks for uECC_shared_secret(). + * + * A correct PRNG function is set by default (default_RNG_defined = 1) and works + * for some platforms, such as Unix and Linux. For other platforms, you may need + * to provide another PRNG function. +*/ +#define default_RNG_defined 1 + +int default_CSPRNG(uint8_t *dest, unsigned int size); + +#endif /* __UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h new file mode 100644 index 00000000..cfa7d38c --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h @@ -0,0 +1,139 @@ +/* hmac.h - TinyCrypt interface to an HMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to an HMAC implementation. + * + * Overview: HMAC is a message authentication code based on hash functions. + * TinyCrypt hard codes SHA-256 as the hash function. A message + * authentication code based on hash functions is also called a + * keyed cryptographic hash function since it performs a + * transformation specified by a key in an arbitrary length data + * set into a fixed length data set (also called tag). + * + * Security: The security of the HMAC depends on the length of the key and + * on the security of the hash function. Note that HMAC primitives + * are much less affected by collision attacks than their + * corresponding hash functions. + * + * Requires: SHA-256 + * + * Usage: 1) call tc_hmac_set_key to set the HMAC key. + * + * 2) call tc_hmac_init to initialize a struct hash_state before + * processing the data. + * + * 3) call tc_hmac_update to process the next input segment; + * tc_hmac_update can be called as many times as needed to process + * all of the segments of the input; the order is important. + * + * 4) call tc_hmac_final to out put the tag. + */ + +#ifndef __TC_HMAC_H__ +#define __TC_HMAC_H__ + +#include "sha256.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tc_hmac_state_struct { + /* the internal state required by h */ + struct tc_sha256_state_struct hash_state; + /* HMAC key schedule */ + uint8_t key[2*TC_SHA256_BLOCK_SIZE]; +}; +typedef struct tc_hmac_state_struct *TCHmacState_t; + +/** + * @brief HMAC set key procedure + * Configures ctx to use key + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if + * ctx == NULL or + * key == NULL or + * key_size == 0 + * @param ctx IN/OUT -- the struct tc_hmac_state_struct to initial + * @param key IN -- the HMAC key to configure + * @param key_size IN -- the HMAC key size + */ +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size); + +/** + * @brief HMAC init procedure + * Initializes ctx to begin the next HMAC operation + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @param ctx IN/OUT -- struct tc_hmac_state_struct buffer to init + */ +int tc_hmac_init(TCHmacState_t ctx); + +/** + * @brief HMAC update procedure + * Mixes data_length bytes addressed by data into state + * @return returns TC_CRYPTO_SUCCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @note Assumes state has been initialized by tc_hmac_init + * @param ctx IN/OUT -- state of HMAC computation so far + * @param data IN -- data to incorporate into state + * @param data_length IN -- size of data in bytes + */ +int tc_hmac_update(TCHmacState_t ctx, const void *data, + unsigned int data_length); + +/** + * @brief HMAC final procedure + * Writes the HMAC tag into the tag buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * ctx == NULL or + * key == NULL or + * taglen != TC_SHA256_DIGEST_SIZE + * @note ctx is erased before exiting. This should never be changed/removed. + * @note Assumes the tag bufer is at least sizeof(hmac_tag_size(state)) bytes + * state has been initialized by tc_hmac_init + * @param tag IN/OUT -- buffer to receive computed HMAC tag + * @param taglen IN -- size of tag in bytes + * @param ctx IN/OUT -- the HMAC state for computing tag + */ +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx); + +#ifdef __cplusplus +} +#endif + +#endif /*__TC_HMAC_H__*/ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h new file mode 100644 index 00000000..24f417e6 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h @@ -0,0 +1,164 @@ +/* hmac_prng.h - TinyCrypt interface to an HMAC-PRNG implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to an HMAC-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the HMAC-PRNG one + * which is based on HMAC. TinyCrypt implements HMAC-PRNG with + * certain modifications from the NIST SP 800-90A spec. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (HMAC and + * SHA256, for TinyCrypt). + * + * The NIST SP 800-90A standard tolerates a null personalization, + * while TinyCrypt requires a non-null personalization. This is + * because a personalization string (the host name concatenated + * with a time stamp, for example) is easily computed and might be + * the last line of defense against failure of the entropy source. + * + * Requires: - SHA-256 + * - HMAC + * + * Usage: 1) call tc_hmac_prng_init to set the HMAC key and process the + * personalization data. + * + * 2) call tc_hmac_prng_reseed to process the seed and additional + * input. + * + * 3) call tc_hmac_prng_generate to out put the pseudo-random data. + */ + +#ifndef __TC_HMAC_PRNG_H__ +#define __TC_HMAC_PRNG_H__ + +#include "sha256.h" +#include "hmac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_HMAC_PRNG_RESEED_REQ -1 + +struct tc_hmac_prng_struct { + /* the HMAC instance for this PRNG */ + struct tc_hmac_state_struct h; + /* the PRNG key */ + uint8_t key[TC_SHA256_DIGEST_SIZE]; + /* PRNG state */ + uint8_t v[TC_SHA256_DIGEST_SIZE]; + /* calls to tc_hmac_prng_generate left before re-seed */ + unsigned int countdown; +}; + +typedef struct tc_hmac_prng_struct *TCHmacPrng_t; + +/** + * @brief HMAC-PRNG initialization procedure + * Initializes prng with personalization, disables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * personalization == NULL, + * plen > MAX_PLEN + * @note Assumes: - personalization != NULL. + * The personalization is a platform unique string (e.g., the host + * name) and is the last line of defense against failure of the + * entropy source + * @warning NIST SP 800-90A specifies 3 items as seed material during + * initialization: entropy seed, personalization, and an optional + * nonce. TinyCrypts requires instead a non-null personalization + * (which is easily computed) and indirectly requires an entropy + * seed (since the reseed function is mandatorily called after + * init) + * @param prng IN/OUT -- the PRNG state to initialize + * @param personalization IN -- personalization string + * @param plen IN -- personalization length in bytes + */ +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen); + +/** + * @brief HMAC-PRNG reseed procedure + * Mixes seed into prng, enables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * seed == NULL, + * seedlen < MIN_SLEN, + * seendlen > MAX_SLEN, + * additional_input != (const uint8_t *) 0 && additionallen == 0, + * additional_input != (const uint8_t *) 0 && additionallen > MAX_ALEN + * @note Assumes:- tc_hmac_prng_init has been called for prng + * - seed has sufficient entropy. + * + * @param prng IN/OUT -- the PRNG state + * @param seed IN -- entropy to mix into the prng + * @param seedlen IN -- length of seed in bytes + * @param additional_input IN -- additional input to the prng + * @param additionallen IN -- additional input length in bytes + */ +int tc_hmac_prng_reseed(TCHmacPrng_t prng, const uint8_t *seed, + unsigned int seedlen, const uint8_t *additional_input, + unsigned int additionallen); + +/** + * @brief HMAC-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_HMAC_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL, + * prng == NULL, + * outlen == 0, + * outlen >= MAX_OUT + * @note Assumes tc_hmac_prng_init has been called for prng + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + * @param prng IN/OUT -- the PRNG state + */ +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_HMAC_PRNG_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h new file mode 100644 index 00000000..af5e8baf --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h @@ -0,0 +1,129 @@ +/* sha256.h - TinyCrypt interface to a SHA-256 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to a SHA-256 implementation. + * + * Overview: SHA-256 is a NIST approved cryptographic hashing algorithm + * specified in FIPS 180. A hash algorithm maps data of arbitrary + * size to data of fixed length. + * + * Security: SHA-256 provides 128 bits of security against collision attacks + * and 256 bits of security against pre-image attacks. SHA-256 does + * NOT behave like a random oracle, but it can be used as one if + * the string being hashed is prefix-free encoded before hashing. + * + * Usage: 1) call tc_sha256_init to initialize a struct + * tc_sha256_state_struct before hashing a new string. + * + * 2) call tc_sha256_update to hash the next string segment; + * tc_sha256_update can be called as many times as needed to hash + * all of the segments of a string; the order is important. + * + * 3) call tc_sha256_final to out put the digest from a hashing + * operation. + */ + +#ifndef __TC_SHA256_H__ +#define __TC_SHA256_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_SHA256_BLOCK_SIZE (64) +#define TC_SHA256_DIGEST_SIZE (32) +#define TC_SHA256_STATE_BLOCKS (TC_SHA256_DIGEST_SIZE/4) + +struct tc_sha256_state_struct { + unsigned int iv[TC_SHA256_STATE_BLOCKS]; + uint64_t bits_hashed; + uint8_t leftover[TC_SHA256_BLOCK_SIZE]; + size_t leftover_offset; +}; + +typedef struct tc_sha256_state_struct *TCSha256State_t; + +/** + * @brief SHA256 initialization procedure + * Initializes s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if s == NULL + * @param s Sha256 state struct + */ +int tc_sha256_init(TCSha256State_t s); + +/** + * @brief SHA256 update procedure + * Hashes data_length bytes addressed by data into state s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * data == NULL + * @note Assumes s has been initialized by tc_sha256_init + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param s Sha256 state struct + * @param data message to hash + * @param datalen length of message to hash + */ +int tc_sha256_update (TCSha256State_t s, const uint8_t *data, size_t datalen); + +/** + * @brief SHA256 final procedure + * Inserts the completed hash computation into digest + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * digest == NULL + * @note Assumes: s has been initialized by tc_sha256_init + * digest points to at least TC_SHA256_DIGEST_SIZE bytes + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param digest unsigned eight bit integer + * @param Sha256 state struct + */ +int tc_sha256_final(uint8_t *digest, TCSha256State_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_SHA256_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h new file mode 100644 index 00000000..6b7b0abf --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h @@ -0,0 +1,121 @@ +/* utils.h - TinyCrypt interface to platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief Interface to platform-dependent run-time operations. + * + */ + +#ifndef __TC_UTILS_H__ +#define __TC_UTILS_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Copy the the buffer 'from' to the buffer 'to'. + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * from_len > to_len. + * + * @param to OUT -- destination buffer + * @param to_len IN -- length of destination buffer + * @param from IN -- origin buffer + * @param from_len IN -- length of origin buffer + */ +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len); + +/** + * @brief Set the value 'val' into the buffer 'to', 'len' times. + * + * @param to OUT -- destination buffer + * @param val IN -- value to be set in 'to' + * @param len IN -- number of times the value will be copied + */ +void _set(void *to, uint8_t val, unsigned int len); + +/** + * @brief Set the value 'val' into the buffer 'to', 'len' times, in a way + * which does not risk getting optimized out by the compiler + * In cases where the compiler does not set __GNUC__ and where the + * optimization level removes the memset, it may be necessary to + * implement a _set_secure function and define the + * TINYCRYPT_ARCH_HAS_SET_SECURE, which then can ensure that the + * memset does not get optimized out. + * + * @param to OUT -- destination buffer + * @param val IN -- value to be set in 'to' + * @param len IN -- number of times the value will be copied + */ +#ifdef TINYCRYPT_ARCH_HAS_SET_SECURE +extern void _set_secure(void *to, uint8_t val, unsigned int len); +#else /* ! TINYCRYPT_ARCH_HAS_SET_SECURE */ +static inline void _set_secure(void *to, uint8_t val, unsigned int len) +{ + (void) memset(to, val, len); +#ifdef __GNUC__ + __asm__ __volatile__("" :: "g"(to) : "memory"); +#endif /* __GNUC__ */ +} +#endif /* TINYCRYPT_ARCH_HAS_SET_SECURE */ + +/* + * @brief AES specific doubling function, which utilizes + * the finite field used by AES. + * @return Returns a^2 + * + * @param a IN/OUT -- value to be doubled + */ +uint8_t _double_byte(uint8_t a); + +/* + * @brief Constant-time algorithm to compare if two sequences of bytes are equal + * @return Returns 0 if equal, and non-zero otherwise + * + * @param a IN -- sequence of bytes a + * @param b IN -- sequence of bytes b + * @param size IN -- size of sequences a and b + */ +int _compare(const uint8_t *a, const uint8_t *b, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UTILS_H__ */ diff --git a/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c b/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c new file mode 100644 index 00000000..19a41c30 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c @@ -0,0 +1,164 @@ +/* aes_decrypt.c - TinyCrypt implementation of AES decryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +static const uint8_t inv_sbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, + 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, + 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, + 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, + 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, + 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, + 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0c, 0x7d +}; + +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + return tc_aes128_set_encrypt_key(s, k); +} + +#define mult8(a)(_double_byte(_double_byte(_double_byte(a)))) +#define mult9(a)(mult8(a)^(a)) +#define multb(a)(mult8(a)^_double_byte(a)^(a)) +#define multd(a)(mult8(a)^_double_byte(_double_byte(a))^(a)) +#define multe(a)(mult8(a)^_double_byte(_double_byte(a))^_double_byte(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = multe(in[0]) ^ multb(in[1]) ^ multd(in[2]) ^ mult9(in[3]); + out[1] = mult9(in[0]) ^ multe(in[1]) ^ multb(in[2]) ^ multd(in[3]); + out[2] = multd(in[0]) ^ mult9(in[1]) ^ multe(in[2]) ^ multb(in[3]); + out[3] = multb(in[0]) ^ multd(in[1]) ^ mult9(in[2]) ^ multe(in[3]); +} + +static inline void inv_mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2*Nb], s+(2*Nb)); + mult_row_column(&t[3*Nb], s+(3*Nb)); + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void inv_sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb*Nk); ++i) { + s[i] = inv_sbox[s[i]]; + } +} + +/* + * This inv_shift_rows also implements the matrix flip required for + * inv_mix_columns, but performs it here to reduce the number of memory + * operations. + */ +static inline void inv_shift_rows(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + t[0] = s[0]; t[1] = s[13]; t[2] = s[10]; t[3] = s[7]; + t[4] = s[4]; t[5] = s[1]; t[6] = s[14]; t[7] = s[11]; + t[8] = s[8]; t[9] = s[5]; t[10] = s[2]; t[11] = s[15]; + t[12] = s[12]; t[13] = s[9]; t[14] = s[6]; t[15] = s[3]; + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + + add_round_key(state, s->words + Nb*Nr); + + for (i = Nr - 1; i > 0; --i) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words + Nb*i); + inv_mix_columns(state); + } + + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /*zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c b/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c new file mode 100644 index 00000000..aa5fa58b --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c @@ -0,0 +1,191 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "aes.h" +#include "utils.h" +#include "constants.h" + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24)|((a) << 8)); +} + +#define subbyte(a, o)(sbox[((a) >> (o))&0xff] << (o)) +#define subword(a)(subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb*i]<<24) | (k[Nb*i+1]<<16) | + (k[Nb*i+2]<<8) | (k[Nb*i+3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i-1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i/Nk]; + } + s->words[i] = s->words[i-Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2 * Nb], s + (2 * Nb)); + mult_row_column(&t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + t[0] = s[0]; t[1] = s[5]; t[2] = s[10]; t[3] = s[15]; + t[4] = s[4]; t[5] = s[9]; t[6] = s[14]; t[7] = s[3]; + t[8] = s[8]; t[9] = s[13]; t[10] = s[2]; t[11] = s[7]; + t[12] = s[12]; t[13] = s[1]; t[14] = s[6]; t[15] = s[11]; + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + add_round_key(state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, s->words + Nb*(i+1)); + } + + sub_bytes(state); + shift_rows(state); + add_round_key(state, s->words + Nb*(i+1)); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /* zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c b/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c new file mode 100644 index 00000000..5b6fd82c --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c @@ -0,0 +1,114 @@ +/* cbc_mode.c - TinyCrypt implementation of CBC mode encryption & decryption */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cbc_mode.h" +#include "constants.h" +#include "utils.h" + +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + unsigned int n, m; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen + TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* copy iv to the buffer */ + (void)_copy(buffer, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + /* copy iv to the output buffer */ + (void)_copy(out, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + + for (n = m = 0; n < inlen; ++n) { + buffer[m++] ^= *in++; + if (m == TC_AES_BLOCK_SIZE) { + (void)tc_aes_encrypt(buffer, buffer, sched); + (void)_copy(out, TC_AES_BLOCK_SIZE, + buffer, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + m = 0; + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + const uint8_t *p; + unsigned int n, m; + + /* sanity check the inputs */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* + * Note that in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption algorithm + * that would not otherwise be possible. + */ + p = iv; + for (n = m = 0; n < outlen; ++n) { + if ((n % TC_AES_BLOCK_SIZE) == 0) { + (void)tc_aes_decrypt(buffer, in, sched); + in += TC_AES_BLOCK_SIZE; + m = 0; + } + *out++ = buffer[m++] ^ *p++; + } + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c b/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c new file mode 100644 index 00000000..9b19994a --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c @@ -0,0 +1,266 @@ +/* ccm_mode.c - TinyCrypt implementation of CCM mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ccm_mode.h" +#include "constants.h" +#include "utils.h" + +#include + +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen) +{ + + /* input sanity check: */ + if (c == (TCCcmMode_t) 0 || + sched == (TCAesKeySched_t) 0 || + nonce == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (nlen != 13) { + return TC_CRYPTO_FAIL; /* The allowed nonce size is: 13. See documentation.*/ + } else if ((mlen < 4) || (mlen > 16) || (mlen & 1)) { + return TC_CRYPTO_FAIL; /* The allowed mac sizes are: 4, 6, 8, 10, 12, 14, 16.*/ + } + + c->mlen = mlen; + c->sched = sched; + c->nonce = nonce; + + return TC_CRYPTO_SUCCESS; +} + +/** + * Variation of CBC-MAC mode used in CCM. + */ +static void ccm_cbc_mac(uint8_t *T, const uint8_t *data, unsigned int dlen, + unsigned int flag, TCAesKeySched_t sched) +{ + + unsigned int i; + + if (flag > 0) { + T[0] ^= (uint8_t)(dlen >> 8); + T[1] ^= (uint8_t)(dlen); + dlen += 2; i = 2; + } else { + i = 0; + } + + while (i < dlen) { + T[i++ % (Nb * Nk)] ^= *data++; + if (((i % (Nb * Nk)) == 0) || dlen == i) { + (void) tc_aes_encrypt(T, T, sched); + } + } +} + +/** + * Variation of CTR mode used in CCM. + * The CTR mode used by CCM is slightly different than the conventional CTR + * mode (the counter is increased before encryption, instead of after + * encryption). Besides, it is assumed that the counter is stored in the last + * 2 bytes of the nonce. + */ +static int ccm_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + uint16_t block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the counter to the nonce */ + (void) _copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 2 bytes of the nonce to be incremented */ + block_num = (uint16_t) ((nonce[14] << 8)|(nonce[15])); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + block_num++; + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + if (!tc_aes_encrypt(buffer, nonce, sched)) { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i % (TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < (plen + c->mlen))) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* GENERATING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0) | (((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i <= 13; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)(plen >> 8); + b[15] = (uint8_t)(plen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(tag, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(tag, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(tag, payload, plen, 0, c->sched); + } + + /* ENCRYPTION: */ + + /* formatting the sequence b for encryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + b[14] = b[15] = TC_ZERO_BYTE; + + /* encrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen, payload, plen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter for ctr_mode (0):*/ + + /* encrypting b and adding the tag to the output: */ + (void) tc_aes_encrypt(b, b, c->sched); + out += plen; + for (i = 0; i < c->mlen; ++i) { + *out++ = tag[i] ^ b[i]; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < plen - c->mlen)) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* DECRYPTION: */ + + /* formatting the sequence b for decryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = b[15] = TC_ZERO_BYTE; /* initial counter value is 0 */ + + /* decrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen - c->mlen, payload, plen - c->mlen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter value (0) */ + + /* encrypting b and restoring the tag from input: */ + (void) tc_aes_encrypt(b, b, c->sched); + for (i = 0; i < c->mlen; ++i) { + tag[i] = *(payload + plen - c->mlen + i) ^ b[i]; + } + + /* VERIFYING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0)|(((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)((plen - c->mlen) >> 8); + b[15] = (uint8_t)(plen - c->mlen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(b, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(b, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(b, out, plen - c->mlen, 0, c->sched); + } + + /* comparing the received tag and the computed one: */ + if (_compare(b, tag, c->mlen) == 0) { + return TC_CRYPTO_SUCCESS; + } else { + /* erase the decrypted buffer in case of mac validation failure: */ + _set(out, 0, plen - c->mlen); + return TC_CRYPTO_FAIL; + } +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c b/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c new file mode 100644 index 00000000..6b96e247 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c @@ -0,0 +1,254 @@ +/* cmac_mode.c - TinyCrypt CMAC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "aes.h" +#include "cmac_mode.h" +#include "constants.h" +#include "utils.h" + +/* max number of calls until change the key (2^48).*/ +static const uint64_t MAX_CALLS = ((uint64_t)1 << 48); + +/* + * gf_wrap -- In our implementation, GF(2^128) is represented as a 16 byte + * array with byte 0 the most significant and byte 15 the least significant. + * High bit carry reduction is based on the primitive polynomial + * + * X^128 + X^7 + X^2 + X + 1, + * + * which leads to the reduction formula X^128 = X^7 + X^2 + X + 1. Indeed, + * since 0 = (X^128 + X^7 + X^2 + 1) mod (X^128 + X^7 + X^2 + X + 1) and since + * addition of polynomials with coefficients in Z/Z(2) is just XOR, we can + * add X^128 to both sides to get + * + * X^128 = (X^7 + X^2 + X + 1) mod (X^128 + X^7 + X^2 + X + 1) + * + * and the coefficients of the polynomial on the right hand side form the + * string 1000 0111 = 0x87, which is the value of gf_wrap. + * + * This gets used in the following way. Doubling in GF(2^128) is just a left + * shift by 1 bit, except when the most significant bit is 1. In the latter + * case, the relation X^128 = X^7 + X^2 + X + 1 says that the high order bit + * that overflows beyond 128 bits can be replaced by addition of + * X^7 + X^2 + X + 1 <--> 0x87 to the low order 128 bits. Since addition + * in GF(2^128) is represented by XOR, we therefore only have to XOR 0x87 + * into the low order byte after a left shift when the starting high order + * bit is 1. + */ +const unsigned char gf_wrap = 0x87; + +/* + * assumes: out != NULL and points to a GF(2^n) value to receive the + * doubled value; + * in != NULL and points to a 16 byte GF(2^n) value + * to double; + * the in and out buffers do not overlap. + * effects: doubles the GF(2^n) value pointed to by "in" and places + * the result in the GF(2^n) value pointed to by "out." + */ +void gf_double(uint8_t *out, uint8_t *in) +{ + + /* start with low order byte */ + uint8_t *x = in + (TC_AES_BLOCK_SIZE - 1); + + /* if msb == 1, we need to add the gf_wrap value, otherwise add 0 */ + uint8_t carry = (in[0] >> 7) ? gf_wrap : 0; + + out += (TC_AES_BLOCK_SIZE - 1); + for (;;) { + *out-- = (*x << 1) ^ carry; + if (x == in) { + break; + } + carry = *x-- >> 7; + } +} + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched) +{ + + /* input sanity check: */ + if (s == (TCCmacState_t) 0 || + key == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + /* put s into a known state */ + _set(s, 0, sizeof(*s)); + s->sched = sched; + + /* configure the encryption key used by the underlying block cipher */ + tc_aes128_set_encrypt_key(s->sched, key); + + /* compute s->K1 and s->K2 from s->iv using s->keyid */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + tc_aes_encrypt(s->iv, s->iv, s->sched); + gf_double (s->K1, s->iv); + gf_double (s->K2, s->K1); + + /* reset s->iv to 0 in case someone wants to compute now */ + tc_cmac_init(s); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_erase(TCCmacState_t s) +{ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_init(TCCmacState_t s) +{ + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* CMAC starts with an all zero initialization vector */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + + /* and the leftover buffer is empty */ + _set(s->leftover, 0, TC_AES_BLOCK_SIZE); + s->leftover_offset = 0; + + /* Set countdown to max number of calls allowed before re-keying: */ + s->countdown = MAX_CALLS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length) +{ + unsigned int i; + + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + if (data_length == 0) { + return TC_CRYPTO_SUCCESS; + } + if (data == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->countdown == 0) { + return TC_CRYPTO_FAIL; + } + + s->countdown--; + + if (s->leftover_offset > 0) { + /* last data added to s didn't end on a TC_AES_BLOCK_SIZE byte boundary */ + size_t remaining_space = TC_AES_BLOCK_SIZE - s->leftover_offset; + + if (data_length < remaining_space) { + /* still not enough data to encrypt this time either */ + _copy(&s->leftover[s->leftover_offset], data_length, data, data_length); + s->leftover_offset += data_length; + return TC_CRYPTO_SUCCESS; + } + /* leftover block is now full; encrypt it first */ + _copy(&s->leftover[s->leftover_offset], + remaining_space, + data, + remaining_space); + data_length -= remaining_space; + data += remaining_space; + s->leftover_offset = 0; + + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + } + + /* CBC encrypt each (except the last) of the data blocks */ + while (data_length > TC_AES_BLOCK_SIZE) { + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= data[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + data += TC_AES_BLOCK_SIZE; + data_length -= TC_AES_BLOCK_SIZE; + } + + if (data_length > 0) { + /* save leftover data for next time */ + _copy(s->leftover, data_length, data, data_length); + s->leftover_offset = data_length; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s) +{ + uint8_t *k; + unsigned int i; + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->leftover_offset == TC_AES_BLOCK_SIZE) { + /* the last message block is a full-sized block */ + k = (uint8_t *) s->K1; + } else { + /* the final message block is not a full-sized block */ + size_t remaining = TC_AES_BLOCK_SIZE - s->leftover_offset; + + _set(&s->leftover[s->leftover_offset], 0, remaining); + s->leftover[s->leftover_offset] = TC_CMAC_PADDING; + k = (uint8_t *) s->K2; + } + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i] ^ k[i]; + } + + tc_aes_encrypt(tag, s->iv, s->sched); + + /* erasing state: */ + tc_cmac_erase(s); + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c b/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c new file mode 100644 index 00000000..f642e983 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c @@ -0,0 +1,85 @@ +/* ctr_mode.c - TinyCrypt CTR mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "constants.h" +#include "ctr_mode.h" +#include "utils.h" + +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + unsigned int block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the ctr to the nonce */ + (void)_copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 4 bytes of the nonce to be incremented */ + block_num = (nonce[12] << 24) | (nonce[13] << 16) | + (nonce[14] << 8) | (nonce[15]); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + /* encrypt data using the current nonce */ + if (tc_aes_encrypt(buffer, nonce, sched)) { + block_num++; + nonce[12] = (uint8_t)(block_num >> 24); + nonce[13] = (uint8_t)(block_num >> 16); + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + } else { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i%(TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[12] = nonce[12]; ctr[13] = nonce[13]; + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c b/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c new file mode 100644 index 00000000..59909600 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c @@ -0,0 +1,283 @@ +/* ctr_prng.c - TinyCrypt implementation of CTR-PRNG */ + +/* + * Copyright (c) 2016, Chris Morrison + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ctr_prng.h" +#include "utils.h" +#include "constants.h" +#include + +/* + * This PRNG is based on the CTR_DRBG described in Recommendation for Random + * Number Generation Using Deterministic Random Bit Generators, + * NIST SP 800-90A Rev. 1. + * + * Annotations to particular steps (e.g. 10.2.1.2 Step 1) refer to the steps + * described in that document. + * + */ + +/** + * @brief Array incrementer + * Treats the supplied array as one contiguous number (MSB in arr[0]), and + * increments it by one + * @return none + * @param arr IN/OUT -- array to be incremented + * @param len IN -- size of arr in bytes + */ +static void arrInc(uint8_t arr[], unsigned int len) +{ + unsigned int i; + if (0 != arr) { + for (i = len; i > 0U; i--) { + if (++arr[i-1] != 0U) { + break; + } + } + } +} + +/** + * @brief CTR PRNG update + * Updates the internal state of supplied the CTR PRNG context + * increments it by one + * @return none + * @note Assumes: providedData is (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes long + * @param ctx IN/OUT -- CTR PRNG state + * @param providedData IN -- data used when updating the internal state + */ +static void tc_ctr_prng_update(TCCtrPrng_t * const ctx, uint8_t const * const providedData) +{ + if (0 != ctx) { + /* 10.2.1.2 step 1 */ + uint8_t temp[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + unsigned int len = 0U; + + /* 10.2.1.2 step 2 */ + while (len < sizeof temp) { + unsigned int blocklen = sizeof(temp) - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.2 step 2.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.2 step 2.2 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.2 step 2.3/step 3 */ + memcpy(&(temp[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.2 step 4 */ + if (0 != providedData) { + unsigned int i; + for (i = 0U; i < sizeof temp; i++) { + temp[i] ^= providedData[i]; + } + } + + /* 10.2.1.2 step 5 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, temp); + + /* 10.2.1.2 step 6 */ + memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE); + } +} + +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen) +{ + int result = TC_CRYPTO_FAIL; + unsigned int i; + uint8_t personalization_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + uint8_t zeroArr[TC_AES_BLOCK_SIZE] = {0U}; + + if (0 != personalization) { + /* 10.2.1.3.1 step 1 */ + unsigned int len = pLen; + if (len > sizeof personalization_buf) { + len = sizeof personalization_buf; + } + + /* 10.2.1.3.1 step 2 */ + memcpy(personalization_buf, personalization, len); + } + + if ((0 != ctx) && (0 != entropy) && (entropyLen >= sizeof seed_material)) { + /* 10.2.1.3.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= personalization_buf[i]; + } + + /* 10.2.1.3.1 step 4 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, zeroArr); + + /* 10.2.1.3.1 step 5 */ + memset(ctx->V, 0x00, sizeof ctx->V); + + /* 10.2.1.3.1 step 6 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.3.1 step 7 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen) +{ + unsigned int i; + int result = TC_CRYPTO_FAIL; + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + + if (0 != additional_input) { + /* 10.2.1.4.1 step 1 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + + /* 10.2.1.4.1 step 2 */ + memcpy(additional_input_buf, additional_input, len); + } + + unsigned int seedlen = (unsigned int)TC_AES_KEY_SIZE + (unsigned int)TC_AES_BLOCK_SIZE; + if ((0 != ctx) && (entropyLen >= seedlen)) { + /* 10.2.1.4.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= additional_input_buf[i]; + } + + /* 10.2.1.4.1 step 4 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.4.1 step 5 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen) +{ + /* 2^48 - see section 10.2.1 */ + static const uint64_t MAX_REQS_BEFORE_RESEED = 0x1000000000000ULL; + + /* 2^19 bits - see section 10.2.1 */ + static const unsigned int MAX_BYTES_PER_REQ = 65536U; + + unsigned int result = TC_CRYPTO_FAIL; + + if ((0 != ctx) && (0 != out) && (outlen < MAX_BYTES_PER_REQ)) { + /* 10.2.1.5.1 step 1 */ + if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) { + result = TC_CTR_PRNG_RESEED_REQ; + } else { + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + if (0 != additional_input) { + /* 10.2.1.5.1 step 2 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + memcpy(additional_input_buf, additional_input, len); + tc_ctr_prng_update(ctx, additional_input_buf); + } + + /* 10.2.1.5.1 step 3 - implicit */ + + /* 10.2.1.5.1 step 4 */ + unsigned int len = 0U; + while (len < outlen) { + unsigned int blocklen = outlen - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.5.1 step 4.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.5.1 step 4.2 */ + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.5.1 step 4.3/step 5 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + memcpy(&(out[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.5.1 step 6 */ + tc_ctr_prng_update(ctx, additional_input_buf); + + /* 10.2.1.5.1 step 7 */ + ctx->reseedCount++; + + /* 10.2.1.5.1 step 8 */ + result = TC_CRYPTO_SUCCESS; + } + } + + return result; +} + +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx) +{ + if (0 != ctx) { + memset(ctx->key.words, 0x00, sizeof ctx->key.words); + memset(ctx->V, 0x00, sizeof ctx->V); + ctx->reseedCount = 0U; + } +} + + + + diff --git a/components/ble/ble_stack/common/tinycrypt/source/ecc.c b/components/ble/ble_stack/common/tinycrypt/source/ecc.c new file mode 100644 index 00000000..baf8e4eb --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ecc.c @@ -0,0 +1,942 @@ +/* ecc.c - TinyCrypt implementation of common ECC functions */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ecc.h" +#include "ecc_platform_specific.h" +#include + +/* IMPORTANT: Make sure a cryptographically-secure PRNG is set and the platform + * has access to enough entropy in order to feed the PRNG regularly. */ +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) +{ + g_rng_function = rng_function; +} + +uECC_RNG_Function uECC_get_rng(void) +{ + return g_rng_function; +} + +int uECC_curve_private_key_size(uECC_Curve curve) +{ + return BITS_TO_BYTES(curve->num_n_bits); +} + +int uECC_curve_public_key_size(uECC_Curve curve) +{ + return 2 * curve->num_bytes; +} + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) +{ + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} + +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) +{ + return (vli[bit >> uECC_WORD_BITS_SHIFT] & + ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + wordcount_t i; + /* Search from the end until we find a non-zero digit. We do it in reverse + * because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words) +{ + + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} + +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) +{ + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + + uECC_word_t diff = 0; + wordcount_t i; + + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return !(diff == 0); +} + +uECC_word_t cond_set(uECC_word_t p_true, uECC_word_t p_false, unsigned int cond) +{ + return (p_true*(cond)) | (p_false*(!cond)); +} + +/* Computes result = left - right, returning borrow, in constant time. + * Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + uECC_word_t val = (diff > left[i]); + borrow = cond_set(val, borrow, (diff != left[i])); + + result[i] = diff; + } + return borrow; +} + +/* Computes result = left + right, returning carry, in constant time. + * Can modify in place. */ +static uECC_word_t uECC_vli_add(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + uECC_word_t val = (sum < left[i]); + carry = cond_set(val, carry, (sum != left[i])); + result[i] = sum; + } + return carry; +} + +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +static void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) +{ + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} + +static void muladd(uECC_word_t a, uECC_word_t b, uECC_word_t *r0, + uECC_word_t *r1, uECC_word_t *r2) +{ + + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; + +} + +/* Computes result = left * right. Result must be 2 * num_words long. */ +static void uECC_vli_mult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words) +{ + + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + for (k = num_words; k < num_words * 2 - 1; ++k) { + + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} + +void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get + * remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + * we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t mod_multiple[2 * NUM_ECC_WORDS]; + uECC_word_t tmp[2 * NUM_ECC_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - + uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + /* Swap the index if there was no borrow */ + index = !(index ^ borrow); + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << + (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, const uECC_word_t *mod, + wordcount_t num_words) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve) +{ + uECC_word_t product[2 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + + curve->mmod_fast(result, product); +} + +static void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) +{ + uECC_vli_modMult_fast(result, left, left, curve); +} + + +#define EVEN(vli) (!(vli[0] & 1)) + +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) +{ + + uECC_word_t carry = 0; + + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words) +{ + uECC_word_t a[NUM_ECC_WORDS], b[NUM_ECC_WORDS]; + uECC_word_t u[NUM_ECC_WORDS], v[NUM_ECC_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve) +{ + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[NUM_ECC_WORDS]; + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + + /* t1 = 3/2*(x1^2 - z1^4) = B */ + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + /* t4 = B * (A - x3) - y1^4 = y3: */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +void x_side_default(uECC_word_t *result, + const uECC_word_t *x, + uECC_Curve curve) +{ + uECC_word_t _3[NUM_ECC_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + /* r = x^3 - 3x + b: */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); +} + +uECC_Curve uECC_secp256r1(void) +{ + return &curve_secp256r1; +} + +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int*product) +{ + unsigned int tmp[NUM_ECC_WORDS]; + int carry; + + /* t */ + uECC_vli_set(result, product, NUM_ECC_WORDS); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, NUM_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, NUM_ECC_WORDS); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + while (carry < 0); + } else { + while (carry || + uECC_vli_cmp_unsafe(curve_secp256r1.p, result, NUM_ECC_WORDS) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + } +} + +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve) +{ + return uECC_vli_isZero(point, curve->num_words * 2); +} + +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve) +{ + uECC_word_t t1[NUM_ECC_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) +{ + uECC_word_t z[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + or P => P - Q, Q => P + Q + */ +static void XYcZ_addC(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + uECC_word_t t6[NUM_ECC_WORDS]; + uECC_word_t t7[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + /* t4 = (y2 - y1)*(B - x3) - E = y3: */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + /* t2 = (y2+y1)*(x3' - B) - E = y3': */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); + + uECC_vli_set(X1, t7, num_words); +} + +void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, uECC_Curve curve) +{ + /* R0 and R1 */ + uECC_word_t Rx[2][NUM_ECC_WORDS]; + uECC_word_t Ry[2][NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0))*/ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + /* Xb * yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve) +{ + + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + bitcount_t num_n_bits = curve->num_n_bits; + + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + + uECC_vli_add(k1, k0, curve->n, num_n_words); + + return carry; +} + +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) +{ + + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. */ + carry = regularize_k(private_key, tmp1, tmp2, curve); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native) +{ + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes) +{ + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words) +{ + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= + mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) +{ + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return -1; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return -2; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + if (uECC_vli_equal(tmp1, tmp2, num_words) != 0) + return -3; + + return 0; +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, + public_key + curve->num_bytes, + curve->num_bytes); + + if (uECC_vli_cmp_unsafe(_public, curve->G, NUM_ECC_WORDS * 2) == 0) { + return -4; + } + + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, + uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative( + _private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(_public, _private, curve)) { + return 0; + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + + curve->num_bytes, curve->num_bytes, _public + curve->num_words); + return 1; +} + + + diff --git a/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c b/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c new file mode 100644 index 00000000..03f03881 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c @@ -0,0 +1,203 @@ +/* ec_dh.c - TinyCrypt implementation of EC-DH */ + +/* + * Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "constants.h" +#include "ecc.h" +#include "ecc_dh.h" +#if defined(BFLB_BLE) +#include "utils.h" +#endif +#include +#if defined(BL_MCU_SDK) +#include "ecc_platform_specific.h" +#endif + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key, + unsigned int *d, uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + /* This function is designed for test purposes-only (such as validating NIST + * test vectors) as it uses a provided value for d instead of generating + * it uniformly at random. */ + memcpy (_private, d, NUM_ECC_BYTES); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer used to store secret: */ + _set_secure(_private, 0, NUM_ECC_BYTES); + + return 1; + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve) +{ + + uECC_word_t _random[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _private uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2 * NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + /* computing modular reduction of _random (see FIPS 186.4 B.4.1): */ + uECC_vli_mmod(_private, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + uECC_vli_nativeToBytes(private_key, + BITS_TO_BYTES(curve->num_n_bits), + _private); + uECC_vli_nativeToBytes(public_key, + curve->num_bytes, + _public); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, + curve->num_bytes, + _public + curve->num_words); + + /* erasing temporary buffer that stored secret: */ + _set_secure(_private, 0, NUM_ECC_BYTES); + + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key, + uint8_t *secret, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {_private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + int r; + + /* Converting buffers to correct bit order: */ + uECC_vli_bytesToNative(_private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(_public, + public_key, + num_bytes); + uECC_vli_bytesToNative(_public + num_words, + public_key + num_bytes, + num_bytes); + + /* Regularize the bitcount for the private key so that attackers cannot use a + * side channel attack to learn the number of leading zeros. */ + carry = regularize_k(_private, _private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to + * improve protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + r = 0; + goto clear_and_out; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, + curve); + + uECC_vli_nativeToBytes(secret, num_bytes, _public); + r = !EccPoint_isZero(_public, curve); + +clear_and_out: + /* erasing temporary buffer used to store secret: */ + _set_secure(p2, 0, sizeof(p2)); + _set_secure(tmp, 0, sizeof(tmp)); + _set_secure(_private, 0, sizeof(_private)); + + return r; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c b/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c new file mode 100644 index 00000000..1d8b2a1b --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c @@ -0,0 +1,298 @@ +/* ec_dsa.c - TinyCrypt implementation of EC-DSA */ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "constants.h" +#include "ecc.h" +#include "ecc_dsa.h" +#if defined(BL_MCU_SDK) +#include "ecc_platform_specific.h" +#endif + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +static void bits2int(uECC_word_t *native, const uint8_t *bits, + unsigned bits_size, uECC_Curve curve) +{ + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + + uECC_vli_clear(native, num_n_words); + uECC_vli_bytesToNative(native, bits, bits_size); + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t tmp[NUM_ECC_WORDS]; + uECC_word_t s[NUM_ECC_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t p[NUM_ECC_WORDS * 2]; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || + uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } + else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + + /* tmp = d: */ + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); + + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } + + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + return 1; +} + +int uECC_sign(const uint8_t *private_key, const uint8_t *message_hash, + unsigned hash_size, uint8_t *signature, uECC_Curve curve) +{ + uECC_word_t _random[2*NUM_ECC_WORDS]; + uECC_word_t k[NUM_ECC_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _random uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2*NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + // computing k as modular reduction of _random (see FIPS 186.4 B.5.1): + uECC_vli_mmod(k, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, + curve)) { + return 1; + } + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) +{ + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, const uint8_t *message_hash, + unsigned hash_size, const uint8_t *signature, + uECC_Curve curve) +{ + + uECC_word_t u1[NUM_ECC_WORDS], u2[NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + uECC_word_t sum[NUM_ECC_WORDS * 2]; + uECC_word_t rx[NUM_ECC_WORDS]; + uECC_word_t ry[NUM_ECC_WORDS]; + uECC_word_t tx[NUM_ECC_WORDS]; + uECC_word_t ty[NUM_ECC_WORDS]; + uECC_word_t tz[NUM_ECC_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t r[NUM_ECC_WORDS], s[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, + curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, _public, num_words); + uECC_vli_set(sum + num_words, _public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + XYcZ_add(tx, ty, sum, sum + num_words, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = _public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words) == 0); +} + diff --git a/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c b/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c new file mode 100644 index 00000000..1867988f --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c @@ -0,0 +1,105 @@ +/* uECC_platform_specific.c - Implementation of platform specific functions*/ + +/* Copyright (c) 2014, Kenneth MacKay + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * uECC_platform_specific.c -- Implementation of platform specific functions + */ + + +#if defined(unix) || defined(__linux__) || defined(__unix__) || \ + defined(__unix) | (defined(__APPLE__) && defined(__MACH__)) || \ + defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#include + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int default_CSPRNG(uint8_t *dest, unsigned int size) { + + /* input sanity check: */ + if (dest == (uint8_t *) 0 || (size <= 0)) + return 0; + + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = (size_t) size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { // read failed + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} + +#endif /* platform */ + diff --git a/components/ble/ble_stack/common/tinycrypt/source/hmac.c b/components/ble/ble_stack/common/tinycrypt/source/hmac.c new file mode 100644 index 00000000..2965aa80 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/hmac.c @@ -0,0 +1,147 @@ +/* hmac.c - TinyCrypt implementation of the HMAC algorithm */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hmac.h" +#include "constants.h" +#include "utils.h" + +static void rekey(uint8_t *key, const uint8_t *new_key, unsigned int key_size) +{ + const uint8_t inner_pad = (uint8_t) 0x36; + const uint8_t outer_pad = (uint8_t) 0x5c; + unsigned int i; + + for (i = 0; i < key_size; ++i) { + key[i] = inner_pad ^ new_key[i]; + key[i + TC_SHA256_BLOCK_SIZE] = outer_pad ^ new_key[i]; + } + for (; i < TC_SHA256_BLOCK_SIZE; ++i) { + key[i] = inner_pad; key[i + TC_SHA256_BLOCK_SIZE] = outer_pad; + } +} + +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size) +{ + /* Input sanity check */ + if (ctx == (TCHmacState_t) 0 || + key == (const uint8_t *) 0 || + key_size == 0) { + return TC_CRYPTO_FAIL; + } + + const uint8_t dummy_key[TC_SHA256_BLOCK_SIZE]; + struct tc_hmac_state_struct dummy_state; + + if (key_size <= TC_SHA256_BLOCK_SIZE) { + /* + * The next three calls are dummy calls just to avoid + * certain timing attacks. Without these dummy calls, + * adversaries would be able to learn whether the key_size is + * greater than TC_SHA256_BLOCK_SIZE by measuring the time + * consumed in this process. + */ + (void)tc_sha256_init(&dummy_state.hash_state); + (void)tc_sha256_update(&dummy_state.hash_state, + dummy_key, + key_size); + (void)tc_sha256_final(&dummy_state.key[TC_SHA256_DIGEST_SIZE], + &dummy_state.hash_state); + + /* Actual code for when key_size <= TC_SHA256_BLOCK_SIZE: */ + rekey(ctx->key, key, key_size); + } else { + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, key, key_size); + (void)tc_sha256_final(&ctx->key[TC_SHA256_DIGEST_SIZE], + &ctx->hash_state); + rekey(ctx->key, + &ctx->key[TC_SHA256_DIGEST_SIZE], + TC_SHA256_DIGEST_SIZE); + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_init(TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_init(&ctx->hash_state); + (void) tc_sha256_update(&ctx->hash_state, ctx->key, TC_SHA256_BLOCK_SIZE); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_update(TCHmacState_t ctx, + const void *data, + unsigned int data_length) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)tc_sha256_update(&ctx->hash_state, data, data_length); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + taglen != TC_SHA256_DIGEST_SIZE || + ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_final(tag, &ctx->hash_state); + + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, + &ctx->key[TC_SHA256_BLOCK_SIZE], + TC_SHA256_BLOCK_SIZE); + (void)tc_sha256_update(&ctx->hash_state, tag, TC_SHA256_DIGEST_SIZE); + (void)tc_sha256_final(tag, &ctx->hash_state); + + /* destroy the current state */ + _set(ctx, 0, sizeof(*ctx)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c b/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c new file mode 100644 index 00000000..9664839a --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c @@ -0,0 +1,230 @@ +/* hmac_prng.c - TinyCrypt implementation of HMAC-PRNG */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hmac_prng.h" +#include "hmac.h" +#include "constants.h" +#include "utils.h" + +/* + * min bytes in the seed string. + * MIN_SLEN*8 must be at least the expected security level. + */ +static const unsigned int MIN_SLEN = 32; + +/* + * max bytes in the seed string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_SLEN = UINT32_MAX; + +/* + * max bytes in the personalization string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_PLEN = UINT32_MAX; + +/* + * max bytes in the additional_info string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_ALEN = UINT32_MAX; + +/* + * max number of generates between re-seeds; + * TinyCrypt accepts up to (2^32 - 1) which is the maximal value of + * a 32-bit unsigned int variable, while SP800-90A specifies a maximum of 2^48. + */ +static const unsigned int MAX_GENS = UINT32_MAX; + +/* + * maximum bytes per generate call; + * SP800-90A specifies a maximum up to 2^19. + */ +static const unsigned int MAX_OUT = (1 << 19); + +/* + * Assumes: prng != NULL + */ +static void update(TCHmacPrng_t prng, const uint8_t *data, unsigned int datalen, const uint8_t *additional_data, unsigned int additional_datalen) +{ + const uint8_t separator0 = 0x00; + const uint8_t separator1 = 0x01; + + /* configure the new prng key into the prng's instance of hmac */ + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use current state, e and separator 0 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator0, sizeof(separator0)); + + if (data && datalen) + (void)tc_hmac_update(&prng->h, data, datalen); + if (additional_data && additional_datalen) + (void)tc_hmac_update(&prng->h, additional_data, additional_datalen); + + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + if (data == 0 || datalen == 0) + return; + + /* configure the new prng key into the prng's instance of hmac */ + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use current state, e and separator 1 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1)); + (void)tc_hmac_update(&prng->h, data, datalen); + if (additional_data && additional_datalen) + (void)tc_hmac_update(&prng->h, additional_data, additional_datalen); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); +} + +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + personalization == (uint8_t *) 0 || + plen > MAX_PLEN) { + return TC_CRYPTO_FAIL; + } + + /* put the generator into a known state: */ + _set(prng->key, 0x00, sizeof(prng->key)); + _set(prng->v, 0x01, sizeof(prng->v)); + + update(prng, personalization, plen, 0, 0); + + /* force a reseed before allowing tc_hmac_prng_generate to succeed: */ + prng->countdown = 0; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_reseed(TCHmacPrng_t prng, + const uint8_t *seed, + unsigned int seedlen, + const uint8_t *additional_input, + unsigned int additionallen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + seed == (const uint8_t *) 0 || + seedlen < MIN_SLEN || + seedlen > MAX_SLEN) { + return TC_CRYPTO_FAIL; + } + + if (additional_input != (const uint8_t *) 0) { + /* + * Abort if additional_input is provided but has inappropriate + * length + */ + if (additionallen == 0 || + additionallen > MAX_ALEN) { + return TC_CRYPTO_FAIL; + } else { + /* call update for the seed and additional_input */ + update(prng, seed, seedlen, additional_input, additionallen); + } + } else { + /* call update only for the seed */ + update(prng, seed, seedlen, 0, 0); + } + + /* ... and enable hmac_prng_generate */ + prng->countdown = MAX_GENS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng) +{ + unsigned int bufferlen; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + prng == (TCHmacPrng_t) 0 || + outlen == 0 || + outlen > MAX_OUT) { + return TC_CRYPTO_FAIL; + } else if (prng->countdown == 0) { + return TC_HMAC_PRNG_RESEED_REQ; + } + + prng->countdown--; + + while (outlen != 0) { + /* configure the new prng key into the prng's instance of hmac */ + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* operate HMAC in OFB mode to create "random" outputs */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + bufferlen = (TC_SHA256_DIGEST_SIZE > outlen) ? + outlen : TC_SHA256_DIGEST_SIZE; + (void)_copy(out, bufferlen, prng->v, bufferlen); + + out += bufferlen; + outlen = (outlen > TC_SHA256_DIGEST_SIZE) ? + (outlen - TC_SHA256_DIGEST_SIZE) : 0; + } + + /* block future PRNG compromises from revealing past state */ + update(prng, 0, 0, 0, 0); + + return TC_CRYPTO_SUCCESS; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/sha256.c b/components/ble/ble_stack/common/tinycrypt/source/sha256.c new file mode 100644 index 00000000..97045df7 --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/sha256.c @@ -0,0 +1,217 @@ +/* sha256.c - TinyCrypt SHA-256 crypto hash algorithm implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sha256.h" +#include "constants.h" +#include "utils.h" + +static void compress(unsigned int *iv, const uint8_t *data); + +int tc_sha256_init(TCSha256State_t s) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* + * Setting the initial state values. + * These values correspond to the first 32 bits of the fractional parts + * of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17 + * and 19. + */ + _set((uint8_t *) s, 0x00, sizeof(*s)); + s->iv[0] = 0x6a09e667; + s->iv[1] = 0xbb67ae85; + s->iv[2] = 0x3c6ef372; + s->iv[3] = 0xa54ff53a; + s->iv[4] = 0x510e527f; + s->iv[5] = 0x9b05688c; + s->iv[6] = 0x1f83d9ab; + s->iv[7] = 0x5be0cd19; + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_update(TCSha256State_t s, const uint8_t *data, size_t datalen) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0 || + data == (void *) 0) { + return TC_CRYPTO_FAIL; + } else if (datalen == 0) { + return TC_CRYPTO_SUCCESS; + } + + while (datalen-- > 0) { + s->leftover[s->leftover_offset++] = *(data++); + if (s->leftover_offset >= TC_SHA256_BLOCK_SIZE) { + compress(s->iv, s->leftover); + s->leftover_offset = 0; + s->bits_hashed += (TC_SHA256_BLOCK_SIZE << 3); + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_final(uint8_t *digest, TCSha256State_t s) +{ + unsigned int i; + + /* input sanity check: */ + if (digest == (uint8_t *) 0 || + s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + s->bits_hashed += (s->leftover_offset << 3); + + s->leftover[s->leftover_offset++] = 0x80; /* always room for one byte */ + if (s->leftover_offset > (sizeof(s->leftover) - 8)) { + /* there is not room for all the padding in this block */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - s->leftover_offset); + compress(s->iv, s->leftover); + s->leftover_offset = 0; + } + + /* add the padding and the length in big-Endian format */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - 8 - s->leftover_offset); + s->leftover[sizeof(s->leftover) - 1] = (uint8_t)(s->bits_hashed); + s->leftover[sizeof(s->leftover) - 2] = (uint8_t)(s->bits_hashed >> 8); + s->leftover[sizeof(s->leftover) - 3] = (uint8_t)(s->bits_hashed >> 16); + s->leftover[sizeof(s->leftover) - 4] = (uint8_t)(s->bits_hashed >> 24); + s->leftover[sizeof(s->leftover) - 5] = (uint8_t)(s->bits_hashed >> 32); + s->leftover[sizeof(s->leftover) - 6] = (uint8_t)(s->bits_hashed >> 40); + s->leftover[sizeof(s->leftover) - 7] = (uint8_t)(s->bits_hashed >> 48); + s->leftover[sizeof(s->leftover) - 8] = (uint8_t)(s->bits_hashed >> 56); + + /* hash the padding and length */ + compress(s->iv, s->leftover); + + /* copy the iv out to digest */ + for (i = 0; i < TC_SHA256_STATE_BLOCKS; ++i) { + unsigned int t = *((unsigned int *) &s->iv[i]); + *digest++ = (uint8_t)(t >> 24); + *digest++ = (uint8_t)(t >> 16); + *digest++ = (uint8_t)(t >> 8); + *digest++ = (uint8_t)(t); + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +/* + * Initializing SHA-256 Hash constant words K. + * These values correspond to the first 32 bits of the fractional parts of the + * cube roots of the first 64 primes between 2 and 311. + */ +static const unsigned int k256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static inline unsigned int ROTR(unsigned int a, unsigned int n) +{ + return (((a) >> n) | ((a) << (32 - n))); +} + +#define Sigma0(a)(ROTR((a), 2) ^ ROTR((a), 13) ^ ROTR((a), 22)) +#define Sigma1(a)(ROTR((a), 6) ^ ROTR((a), 11) ^ ROTR((a), 25)) +#define sigma0(a)(ROTR((a), 7) ^ ROTR((a), 18) ^ ((a) >> 3)) +#define sigma1(a)(ROTR((a), 17) ^ ROTR((a), 19) ^ ((a) >> 10)) + +#define Ch(a, b, c)(((a) & (b)) ^ ((~(a)) & (c))) +#define Maj(a, b, c)(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +static inline unsigned int BigEndian(const uint8_t **c) +{ + unsigned int n = 0; + + n = (((unsigned int)(*((*c)++))) << 24); + n |= ((unsigned int)(*((*c)++)) << 16); + n |= ((unsigned int)(*((*c)++)) << 8); + n |= ((unsigned int)(*((*c)++))); + return n; +} + +static void compress(unsigned int *iv, const uint8_t *data) +{ + unsigned int a, b, c, d, e, f, g, h; + unsigned int s0, s1; + unsigned int t1, t2; + unsigned int work_space[16]; + unsigned int n; + unsigned int i; + + a = iv[0]; b = iv[1]; c = iv[2]; d = iv[3]; + e = iv[4]; f = iv[5]; g = iv[6]; h = iv[7]; + + for (i = 0; i < 16; ++i) { + n = BigEndian(&data); + t1 = work_space[i] = n; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + for ( ; i < 64; ++i) { + s0 = work_space[(i+1)&0x0f]; + s0 = sigma0(s0); + s1 = work_space[(i+14)&0x0f]; + s1 = sigma1(s1); + + t1 = work_space[i&0xf] += s0 + s1 + work_space[(i+9)&0xf]; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + iv[0] += a; iv[1] += b; iv[2] += c; iv[3] += d; + iv[4] += e; iv[5] += f; iv[6] += g; iv[7] += h; +} diff --git a/components/ble/ble_stack/common/tinycrypt/source/utils.c b/components/ble/ble_stack/common/tinycrypt/source/utils.c new file mode 100644 index 00000000..61a567bf --- /dev/null +++ b/components/ble/ble_stack/common/tinycrypt/source/utils.c @@ -0,0 +1,74 @@ +/* utils.c - TinyCrypt platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "utils.h" +#include "constants.h" + +#include + +#define MASK_TWENTY_SEVEN 0x1b + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/components/ble/ble_stack/common/utils.c b/components/ble/ble_stack/common/utils.c new file mode 100644 index 00000000..0f410a2c --- /dev/null +++ b/components/ble/ble_stack/common/utils.c @@ -0,0 +1,134 @@ +/***************************************************************************************** +* +* @file utils.c +* +* @brief entry +* +* Copyright (C) Bouffalo Lab 2019 +* +* History: 2019-11 crealted by Lanlan Gong @ Shanghai +* +*****************************************************************************************/ +#include +#include +#include + +void reverse_bytearray(uint8_t *src, uint8_t *result, int array_size) +{ + for(int i=0; i < array_size;i++){ + result[array_size - i -1] = src[i]; + } +} + +unsigned int find_msb_set(uint32_t data) +{ + uint32_t count = 0; + uint32_t mask = 0x80000000; + + if (!data) { + return 0; + } + while ((data & mask) == 0) { + count += 1u; + mask = mask >> 1u; + } + return (32 - count); +} + +unsigned int find_lsb_set(uint32_t data) +{ + uint32_t count = 0; + uint32_t mask = 0x00000001; + + if (!data) { + return 0; + } + while ((data & mask) == 0) { + count += 1u; + mask = mask << 1u; + } + return (1 + count); +} + +int char2hex(char c, uint8_t *x) +{ + if (c >= '0' && c <= '9') { + *x = c - '0'; + } else if (c >= 'a' && c <= 'f') { + *x = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + *x = c - 'A' + 10; + } else { + return -1; + } + + return 0; +} + +int hex2char(uint8_t x, char *c) +{ + if (x <= 9) { + *c = x + '0'; + } else if (x <= 15) { + *c = x - 10 + 'a'; + } else { + return -1; + } + + return 0; +} + +size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen) +{ + if ((hexlen + 1) < buflen * 2) { + return 0; + } + + for (size_t i = 0; i < buflen; i++) { + if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) { + return 0; + } + if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) { + return 0; + } + } + + hex[2 * buflen] = '\0'; + return 2 * buflen; +} + + +size_t hex2bin(const char *hex, size_t hexlen, uint8_t *buf, size_t buflen) +{ + uint8_t dec; + + if (buflen < hexlen / 2 + hexlen % 2) { + return 0; + } + + /* if hexlen is uneven, insert leading zero nibble */ + if (hexlen % 2) { + if (char2hex(hex[0], &dec) < 0) { + return 0; + } + buf[0] = dec; + hex++; + buf++; + } + + /* regular hex conversion */ + for (size_t i = 0; i < hexlen / 2; i++) { + if (char2hex(hex[2 * i], &dec) < 0) { + return 0; + } + buf[i] = dec << 4; + + if (char2hex(hex[2 * i + 1], &dec) < 0) { + return 0; + } + buf[i] += dec; + } + + return hexlen / 2 + hexlen % 2; +} + diff --git a/components/ble/ble_stack/common/work_q.c b/components/ble/ble_stack/common/work_q.c new file mode 100644 index 00000000..619d3433 --- /dev/null +++ b/components/ble/ble_stack/common/work_q.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2016 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * Workqueue support functions + */ + + +#include +#include +#include "errno.h" + +struct k_thread work_q_thread; +#if !defined(BFLB_BLE) +static BT_STACK_NOINIT(work_q_stack, CONFIG_BT_WORK_QUEUE_STACK_SIZE); +#endif +struct k_work_q g_work_queue_main; + + +static void k_work_submit_to_queue(struct k_work_q *work_q, + struct k_work *work) +{ + if (!atomic_test_and_set_bit(work->flags, K_WORK_STATE_PENDING)) { + k_fifo_put(&work_q->fifo, work); + } +} + +#if defined(BFLB_BLE) +static void work_queue_main(void *p1) +{ + struct k_work *work; + UNUSED(p1); + + while (1) { + work = k_fifo_get(&g_work_queue_main.fifo, K_FOREVER); + + if (atomic_test_and_clear_bit(work->flags, K_WORK_STATE_PENDING)) { + work->handler(work); + } + + k_yield(); + } +} + +int k_work_q_start(void) +{ + k_fifo_init(&g_work_queue_main.fifo, 20); + return k_thread_create(&work_q_thread, "work_q_thread", + CONFIG_BT_WORK_QUEUE_STACK_SIZE, + work_queue_main, CONFIG_BT_WORK_QUEUE_PRIO); +} + +int k_work_init(struct k_work *work, k_work_handler_t handler) +{ + ASSERT(work, "work is NULL"); + + atomic_clear(work->flags); + work->handler = handler; + return 0; +} + +void k_work_submit(struct k_work *work) +{ + k_work_submit_to_queue(&g_work_queue_main, work); +} + +static void work_timeout(void *timer) +{ + /* Parameter timer type is */ + struct k_delayed_work* w = (struct k_delayed_work*)k_timer_get_id(timer); + if(w->work_q == NULL){ + return; + } + + /* submit work to workqueue */ + if(!atomic_test_bit(w->work.flags, K_WORK_STATE_PERIODIC)){ + k_work_submit_to_queue(w->work_q, &w->work); + /* detach from workqueue, for cancel to return appropriate status */ + w->work_q = NULL; + } + else{ + /* For periodic timer, restart it.*/ + k_timer_reset(&w->timer); + k_work_submit_to_queue(w->work_q, &w->work); + } +} + +void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler) +{ + ASSERT(work, "delay work is NULL"); + /* Added by bouffalolab */ + k_work_init(&work->work, handler); + k_timer_init(&work->timer, work_timeout, work); + work->work_q = NULL; +} + +static int k_delayed_work_submit_to_queue(struct k_work_q *work_q, + struct k_delayed_work *work, + uint32_t delay) +{ + int err; + + /* Work cannot be active in multiple queues */ + if (work->work_q && work->work_q != work_q) { + err = -EADDRINUSE; + goto done; + } + + /* Cancel if work has been submitted */ + if (work->work_q == work_q) { + err = k_delayed_work_cancel(work); + + if (err < 0) { + goto done; + } + } + + if (!delay) { + /* Submit work if no ticks is 0 */ + k_work_submit_to_queue(work_q, &work->work); + work->work_q = NULL; + } else { + /* Add timeout */ + /* Attach workqueue so the timeout callback can submit it */ + k_timer_start(&work->timer, delay); + work->work_q = work_q; + } + + err = 0; + +done: + return err; +} + +int k_delayed_work_submit(struct k_delayed_work *work, uint32_t delay) +{ + atomic_clear_bit(work->work.flags, K_WORK_STATE_PERIODIC); + return k_delayed_work_submit_to_queue(&g_work_queue_main, work, delay); +} + +/* Added by bouffalolab */ +int k_delayed_work_submit_periodic(struct k_delayed_work *work, s32_t period) +{ + atomic_set_bit(work->work.flags, K_WORK_STATE_PERIODIC); + return k_delayed_work_submit_to_queue(&g_work_queue_main, work, period); +} + +int k_delayed_work_cancel(struct k_delayed_work *work) +{ + int err = 0; + + if (atomic_test_bit(work->work.flags, K_WORK_STATE_PENDING)) { + err = -EINPROGRESS; + goto exit; + } + + if (!work->work_q) { + err = -EINVAL; + goto exit; + } + + k_timer_stop(&work->timer); + work->work_q = NULL; + work->timer.timeout = 0; + work->timer.start_ms = 0; + +exit: + return err; +} + +s32_t k_delayed_work_remaining_get(struct k_delayed_work *work) +{ + int32_t remain; + k_timer_t *timer; + + if (work == NULL) { + return 0; + } + + timer = &work->timer; + remain = timer->timeout - (k_now_ms() - timer->start_ms); + if (remain < 0) { + remain = 0; + } + return remain; +} + +void k_delayed_work_del_timer(struct k_delayed_work *work) +{ + if(NULL == work || NULL == work->timer.timer.hdl) + return; + + k_timer_delete(&work->timer); + work->timer.timer.hdl = NULL; +} + +/* Added by bouffalolab */ +int k_delayed_work_free(struct k_delayed_work *work) +{ + int err = 0; + + if (atomic_test_bit(work->work.flags, K_WORK_STATE_PENDING)) { + err = -EINPROGRESS; + goto exit; + } + + k_delayed_work_del_timer(work); + work->work_q = NULL; + work->timer.timeout = 0; + work->timer.start_ms = 0; + +exit: + return err; +} + +#else +static void work_q_main(void *work_q_ptr, void *p2, void *p3) +{ + struct k_work_q *work_q = work_q_ptr; + + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + while (1) { + struct k_work *work; + k_work_handler_t handler; + + work = k_queue_get(&work_q->queue, K_FOREVER); + if (!work) { + continue; + } + + handler = work->handler; + + /* Reset pending state so it can be resubmitted by handler */ + if (atomic_test_and_clear_bit(work->flags, + K_WORK_STATE_PENDING)) { + handler(work); + } + + /* Make sure we don't hog up the CPU if the FIFO never (or + * very rarely) gets empty. + */ + k_yield(); + } +} + +void k_work_q_start(struct k_work_q *work_q, k_thread_stack_t *stack, + size_t stack_size, int prio) +{ + k_queue_init(&work_q->queue, 20); + k_thread_create(&work_q->thread, stack, stack_size, work_q_main, + work_q, 0, 0, prio, 0, 0); + _k_object_init(work_q); +} + +#ifdef CONFIG_SYS_CLOCK_EXISTS +static void work_timeout(struct _timeout *t) +{ + struct k_delayed_work *w = CONTAINER_OF(t, struct k_delayed_work, + timeout); + + /* submit work to workqueue */ + k_work_submit_to_queue(w->work_q, &w->work); +} + +void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler) +{ + k_work_init(&work->work, handler); + _init_timeout(&work->timeout, work_timeout); + work->work_q = NULL; + + _k_object_init(work); +} + +int k_delayed_work_submit_to_queue(struct k_work_q *work_q, + struct k_delayed_work *work, + s32_t delay) +{ + unsigned int key = irq_lock(); + int err; + + /* Work cannot be active in multiple queues */ + if (work->work_q && work->work_q != work_q) { + err = -EADDRINUSE; + goto done; + } + + /* Cancel if work has been submitted */ + if (work->work_q == work_q) { + err = k_delayed_work_cancel(work); + if (err < 0) { + goto done; + } + } + + /* Attach workqueue so the timeout callback can submit it */ + work->work_q = work_q; + + if (!delay) { + /* Submit work if no ticks is 0 */ + k_work_submit_to_queue(work_q, &work->work); + } else { + /* Add timeout */ + _add_timeout(NULL, &work->timeout, NULL, + _TICK_ALIGN + _ms_to_ticks(delay)); + } + + err = 0; + +done: + irq_unlock(key); + + return err; +} + +int k_delayed_work_cancel(struct k_delayed_work *work) +{ + unsigned int key = irq_lock(); + + if (!work->work_q) { + irq_unlock(key); + return -EINVAL; + } + + if (k_work_pending(&work->work)) { + /* Remove from the queue if already submitted */ + if (!k_queue_remove(&work->work_q->queue, &work->work)) { + irq_unlock(key); + return -EINVAL; + } + } else { + _abort_timeout(&work->timeout); + } + + /* Detach from workqueue */ + work->work_q = NULL; + + atomic_clear_bit(work->work.flags, K_WORK_STATE_PENDING); + irq_unlock(key); + + return 0; +} +#endif /* CONFIG_SYS_CLOCK_EXISTS */ +#endif /* BFLB_BLE */ \ No newline at end of file diff --git a/components/ble/ble_stack/hci_onchip/hci_driver.c b/components/ble/ble_stack/hci_onchip/hci_driver.c new file mode 100644 index 00000000..bb12859e --- /dev/null +++ b/components/ble/ble_stack/hci_onchip/hci_driver.c @@ -0,0 +1,565 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +//#include +//#include +//#include +//#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_CLOCK_CONTROL_NRF5 +#include +#endif + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER) +#include "log.h" + +//#include "util/util.h" +//#include "hal/ccm.h" +//#include "hal/radio.h" +//#include "ll_sw/pdu.h" +//#include "ll_sw/ctrl.h" +#include "hci_internal.h" +//#include "init.h" +//#include "hal/debug.h" +#if defined(BFLB_BLE) +#include "bl_hci_wrapper.h" +#endif + +#define NODE_RX(_node) CONTAINER_OF(_node, struct radio_pdu_node_rx, \ + hdr.onion.node) + +#if !defined(BFLB_BLE) +static K_SEM_DEFINE(sem_prio_recv, 0, BT_UINT_MAX); +#endif + +K_FIFO_DEFINE(recv_fifo); +#if (BFLB_BLE_CO_THREAD) +extern struct k_sem g_poll_sem; +static int recv_fifo_count = 0; +#endif + +#if !defined(BFLB_BLE) +struct k_thread prio_recv_thread_data; +static BT_STACK_NOINIT(prio_recv_thread_stack, + CONFIG_BT_CTLR_RX_PRIO_STACK_SIZE); +#endif + +struct k_thread recv_thread_data; +#if !defined(BFLB_BLE) +static BT_STACK_NOINIT(recv_thread_stack, CONFIG_BT_RX_STACK_SIZE); +#endif + +#if defined(CONFIG_INIT_STACKS) +static u32_t prio_ts; +static u32_t rx_ts; +#endif + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +static struct k_poll_signal hbuf_signal = + K_POLL_SIGNAL_INITIALIZER(hbuf_signal); +static sys_slist_t hbuf_pend; +static s32_t hbuf_count; +#endif + +#if !defined(BFLB_BLE) +static void prio_recv_thread(void *p1, void *p2, void *p3) +{ + while (1) { + struct radio_pdu_node_rx *node_rx; + u8_t num_cmplt; + u16_t handle; + + while ((num_cmplt = radio_rx_get(&node_rx, &handle))) { +#if defined(CONFIG_BT_CONN) + struct net_buf *buf; + + buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); + hci_num_cmplt_encode(buf, handle, num_cmplt); + BT_DBG("Num Complete: 0x%04x:%u", handle, num_cmplt); + bt_recv_prio(buf); + k_yield(); +#endif + } + + if (node_rx) { + + radio_rx_dequeue(); + + BT_DBG("RX node enqueue"); + k_fifo_put(&recv_fifo, node_rx); + + continue; + } + + BT_DBG("sem take..."); + k_sem_take(&sem_prio_recv, K_FOREVER); + BT_DBG("sem taken"); + +#if defined(CONFIG_INIT_STACKS) + if (k_uptime_get_32() - prio_ts > K_SECONDS(5)) { + STACK_ANALYZE("prio recv thread stack", + prio_recv_thread_stack); + prio_ts = k_uptime_get_32(); + } +#endif + } +} + +static inline struct net_buf *encode_node(struct radio_pdu_node_rx *node_rx, + s8_t class) +{ + struct net_buf *buf = NULL; + + /* Check if we need to generate an HCI event or ACL data */ + switch (class) { + case HCI_CLASS_EVT_DISCARDABLE: + case HCI_CLASS_EVT_REQUIRED: + case HCI_CLASS_EVT_CONNECTION: + if (class == HCI_CLASS_EVT_DISCARDABLE) { + buf = bt_buf_get_rx(BT_BUF_EVT, K_NO_WAIT); + } else { + buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); + } + if (buf) { + hci_evt_encode(node_rx, buf); + } + break; +#if defined(CONFIG_BT_CONN) + case HCI_CLASS_ACL_DATA: + /* generate ACL data */ + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_FOREVER); + hci_acl_encode(node_rx, buf); + break; +#endif + default: + LL_ASSERT(0); + break; + } + + radio_rx_fc_set(node_rx->hdr.handle, 0); + node_rx->hdr.onion.next = 0; + radio_rx_mem_release(&node_rx); + + return buf; +} + +static inline struct net_buf *process_node(struct radio_pdu_node_rx *node_rx) +{ + s8_t class = hci_get_class(node_rx); + struct net_buf *buf = NULL; + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + if (hbuf_count != -1) { + bool pend = !sys_slist_is_empty(&hbuf_pend); + + /* controller to host flow control enabled */ + switch (class) { + case HCI_CLASS_EVT_DISCARDABLE: + case HCI_CLASS_EVT_REQUIRED: + break; + case HCI_CLASS_EVT_CONNECTION: + /* for conn-related events, only pend is relevant */ + hbuf_count = 1; + /* fallthrough */ + case HCI_CLASS_ACL_DATA: + if (pend || !hbuf_count) { + sys_slist_append(&hbuf_pend, + &node_rx->hdr.onion.node); + BT_DBG("FC: Queuing item: %d", class); + return NULL; + } + break; + default: + LL_ASSERT(0); + break; + } + } +#endif + + /* process regular node from radio */ + buf = encode_node(node_rx, class); + + return buf; +} + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +static inline struct net_buf *process_hbuf(struct radio_pdu_node_rx *n) +{ + /* shadow total count in case of preemption */ + struct radio_pdu_node_rx *node_rx = NULL; + s32_t hbuf_total = hci_hbuf_total; + struct net_buf *buf = NULL; + sys_snode_t *node = NULL; + s8_t class; + int reset; + + reset = atomic_test_and_clear_bit(&hci_state_mask, HCI_STATE_BIT_RESET); + if (reset) { + /* flush queue, no need to free, the LL has already done it */ + sys_slist_init(&hbuf_pend); + } + + if (hbuf_total <= 0) { + hbuf_count = -1; + return NULL; + } + + /* available host buffers */ + hbuf_count = hbuf_total - (hci_hbuf_sent - hci_hbuf_acked); + + /* host acked ACL packets, try to dequeue from hbuf */ + node = sys_slist_peek_head(&hbuf_pend); + if (!node) { + return NULL; + } + + /* Return early if this iteration already has a node to process */ + node_rx = NODE_RX(node); + class = hci_get_class(node_rx); + if (n) { + if (class == HCI_CLASS_EVT_CONNECTION || + (class == HCI_CLASS_ACL_DATA && hbuf_count)) { + /* node to process later, schedule an iteration */ + BT_DBG("FC: signalling"); + k_poll_signal_raise(&hbuf_signal, 0x0); + } + return NULL; + } + + switch (class) { + case HCI_CLASS_EVT_CONNECTION: + BT_DBG("FC: dequeueing event"); + (void) sys_slist_get(&hbuf_pend); + break; + case HCI_CLASS_ACL_DATA: + if (hbuf_count) { + BT_DBG("FC: dequeueing ACL data"); + (void) sys_slist_get(&hbuf_pend); + } else { + /* no buffers, HCI will signal */ + node = NULL; + } + break; + case HCI_CLASS_EVT_DISCARDABLE: + case HCI_CLASS_EVT_REQUIRED: + default: + LL_ASSERT(0); + break; + } + + if (node) { + buf = encode_node(node_rx, class); + /* Update host buffers after encoding */ + hbuf_count = hbuf_total - (hci_hbuf_sent - hci_hbuf_acked); + /* next node */ + node = sys_slist_peek_head(&hbuf_pend); + if (node) { + node_rx = NODE_RX(node); + class = hci_get_class(node_rx); + + if (class == HCI_CLASS_EVT_CONNECTION || + (class == HCI_CLASS_ACL_DATA && hbuf_count)) { + /* more to process, schedule an + * iteration + */ + BT_DBG("FC: signalling"); + k_poll_signal_raise(&hbuf_signal, 0x0); + } + } + } + + return buf; +} +#endif +#endif + +#if defined(BFLB_BLE) +#if (BFLB_BLE_CO_THREAD) +void co_rx_thread() +{ + struct net_buf *buf = NULL; + buf = net_buf_get(&recv_fifo, K_NO_WAIT); + if(buf){ + BT_DBG("Calling bt_recv(%p)", buf); + bt_recv(buf); + } +} + +void co_tx_rx_thread(void *p1) +{ + UNUSED(p1); + BT_DBG("using %s\n", __func__); + while (1) { + if (k_sem_count_get(&g_poll_sem) > 0) { + co_tx_thread(); + } + + if (recv_fifo_count > 0) { + recv_fifo_count--; + co_rx_thread(); + } + + k_sleep(portTICK_PERIOD_MS); + k_yield(); + } +} + +#else +static void recv_thread(void *p1) +{ + UNUSED(p1); +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + /* @todo: check if the events structure really needs to be static */ + static struct k_poll_event events[2] = { + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &hbuf_signal, 0), + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &recv_fifo, 0), + }; +#endif + + while (1) { +#if defined(BFLB_BLE) + struct net_buf *buf = NULL; + buf = net_buf_get(&recv_fifo, K_FOREVER); + if(buf){ + BT_DBG("Calling bt_recv(%p)", buf); + bt_recv(buf); + } +#else + struct radio_pdu_node_rx *node_rx = NULL; + struct net_buf *buf = NULL; + + BT_DBG("blocking"); +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + int err; + + err = k_poll(events, 2, K_FOREVER); + LL_ASSERT(err == 0); + if (events[0].state == K_POLL_STATE_SIGNALED) { + events[0].signal->signaled = 0; + } else if (events[1].state == + K_POLL_STATE_FIFO_DATA_AVAILABLE) { + node_rx = k_fifo_get(events[1].fifo, 0); + } + + events[0].state = K_POLL_STATE_NOT_READY; + events[1].state = K_POLL_STATE_NOT_READY; + + /* process host buffers first if any */ + buf = process_hbuf(node_rx); + +#else + node_rx = k_fifo_get(&recv_fifo, K_FOREVER); +#endif + BT_DBG("unblocked"); + + if (node_rx && !buf) { + /* process regular node from radio */ + buf = process_node(node_rx); + } + + if (buf) { + if (buf->len) { + BT_DBG("Packet in: type:%u len:%u", + bt_buf_get_type(buf), buf->len); + bt_recv(buf); + } else { + net_buf_unref(buf); + } + } +#endif + k_yield(); + +#if defined(CONFIG_INIT_STACKS) + if (k_uptime_get_32() - rx_ts > K_SECONDS(5)) { + STACK_ANALYZE("recv thread stack", recv_thread_stack); + rx_ts = k_uptime_get_32(); + } +#endif + } +} +#endif +#endif + +#if !defined(BFLB_BLE) +static int cmd_handle(struct net_buf *buf) +{ + struct net_buf *evt; + + evt = hci_cmd_handle(buf); + if (evt) { + BT_DBG("Replying with event of %u bytes", evt->len); + bt_recv_prio(evt); + } +} + +#if defined(CONFIG_BT_CONN) +static int acl_handle(struct net_buf *buf) +{ + struct net_buf *evt; + int err; + + err = hci_acl_handle(buf, &evt); + if (evt) { + BT_DBG("Replying with event of %u bytes", evt->len); + bt_recv_prio(evt); + } + + return err; +} +#endif /* CONFIG_BT_CONN */ +#endif + +static int hci_driver_send(struct net_buf *buf) +{ +#if !defined(BFLB_BLE) + u8_t type; +#endif + int err; + + BT_DBG("enter"); + + if (!buf->len) { + BT_ERR("Empty HCI packet"); + return -EINVAL; + } + +#if defined(BFLB_BLE) + err = bl_onchiphci_send_2_controller(buf); + net_buf_unref(buf); + return err; +#else + type = bt_buf_get_type(buf); + switch (type) { +#if defined(CONFIG_BT_CONN) + case BT_BUF_ACL_OUT: + err = acl_handle(buf); + break; +#endif /* CONFIG_BT_CONN */ + case BT_BUF_CMD: + err = cmd_handle(buf); + + break; + default: + BT_ERR("Unknown HCI type %u", type); + return -EINVAL; + } + + if (!err) { + + net_buf_unref(buf); + } + else + { + } + + BT_DBG("exit: %d", err); +#endif + return err; +} + +static int hci_driver_open(void) +{ +#if !defined(BFLB_BLE) + u32_t err; + + DEBUG_INIT(); + k_sem_init(&sem_prio_recv, 0, BT_UINT_MAX); + + err = ll_init(&sem_prio_recv); + + if (err) { + BT_ERR("LL initialization failed: %u", err); + return err; + } +#endif + +#if !defined(BFLB_BLE) +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + hci_init(&hbuf_signal); +#else + hci_init(NULL); +#endif +#endif + k_fifo_init(&recv_fifo, 20); + +#if defined(BFLB_BLE) +#if (BFLB_BLE_CO_THREAD) +k_thread_create(&recv_thread_data, "co_tx_rx_thread", + CONFIG_BT_RX_STACK_SIZE, + co_tx_rx_thread, + K_PRIO_COOP(CONFIG_BT_RX_PRIO)); +#else + k_thread_create(&recv_thread_data, "recv_thread", + CONFIG_BT_RX_STACK_SIZE/*K_THREAD_STACK_SIZEOF(recv_thread_stack)*/, + recv_thread, + K_PRIO_COOP(CONFIG_BT_RX_PRIO)); +#endif +#else + k_thread_create(&prio_recv_thread_data, prio_recv_thread_stack, + K_THREAD_STACK_SIZEOF(prio_recv_thread_stack), + prio_recv_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_CTLR_RX_PRIO), 0, K_NO_WAIT); +#endif + + BT_DBG("Success."); + + return 0; +} + +void hci_driver_enque_recvq(struct net_buf *buf) +{ + net_buf_put(&recv_fifo, buf); +#if (BFLB_BLE_CO_THREAD) + recv_fifo_count++; +#endif +} + +static const struct bt_hci_driver drv = { + .name = "Controller", + .bus = BT_HCI_DRIVER_BUS_VIRTUAL, + .open = hci_driver_open, + .send = hci_driver_send, +}; + +#if defined(BFLB_BLE) +int hci_driver_init(void) +{ + + bt_hci_driver_register(&drv); + + return 0; +} +#else +static int _hci_driver_init(struct device *unused) +{ + ARG_UNUSED(unused); + + bt_hci_driver_register(&drv); + + return 0; +} +//SYS_INIT(_hci_driver_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); +#endif + diff --git a/components/ble/ble_stack/hci_onchip/hci_internal.h b/components/ble/ble_stack/hci_onchip/hci_internal.h new file mode 100644 index 00000000..f70a70bd --- /dev/null +++ b/components/ble/ble_stack/hci_onchip/hci_internal.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016 Nordic Semiconductor ASA + * Copyright (c) 2016 Vinayak Kariappa Chettimada + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _HCI_CONTROLLER_H_ +#define _HCI_CONTROLLER_H_ + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +extern s32_t hci_hbuf_total; +extern u32_t hci_hbuf_sent; +extern u32_t hci_hbuf_acked; +extern atomic_t hci_state_mask; + +#define HCI_STATE_BIT_RESET 0 +#endif + +#define HCI_CLASS_EVT_REQUIRED 0 +#define HCI_CLASS_EVT_DISCARDABLE 1 +#define HCI_CLASS_EVT_CONNECTION 2 +#define HCI_CLASS_ACL_DATA 3 + +#if defined(CONFIG_SOC_FAMILY_NRF5) +#define BT_HCI_VS_HW_PLAT BT_HCI_VS_HW_PLAT_NORDIC +#if defined(CONFIG_SOC_SERIES_NRF51X) +#define BT_HCI_VS_HW_VAR BT_HCI_VS_HW_VAR_NORDIC_NRF51X; +#elif defined(CONFIG_SOC_SERIES_NRF52X) +#define BT_HCI_VS_HW_VAR BT_HCI_VS_HW_VAR_NORDIC_NRF52X; +#endif +#else +#define BT_HCI_VS_HW_PLAT 0 +#define BT_HCI_VS_HW_VAR 0 +#endif /* CONFIG_SOC_FAMILY_NRF5 */ + +void hci_init(struct k_poll_signal *signal_host_buf); +struct net_buf *hci_cmd_handle(struct net_buf *cmd); +#if !defined(BFLB_BLE) +void hci_evt_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf); +s8_t hci_get_class(struct radio_pdu_node_rx *node_rx); +#if defined(CONFIG_BT_CONN) +int hci_acl_handle(struct net_buf *acl, struct net_buf **evt); +void hci_acl_encode(struct radio_pdu_node_rx *node_rx, struct net_buf *buf); +void hci_num_cmplt_encode(struct net_buf *buf, u16_t handle, u8_t num); +#endif +#endif//!defined(BFLB_BLE) +#endif /* _HCI_CONTROLLER_H_ */ diff --git a/components/ble/ble_stack/host/a2dp.c b/components/ble/ble_stack/host/a2dp.c new file mode 100644 index 00000000..ff09b32c --- /dev/null +++ b/components/ble/ble_stack/host/a2dp.c @@ -0,0 +1,385 @@ +/** @file + * @brief Advance Audio Distribution Profile. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_A2DP) +#define LOG_MODULE_NAME bt_a2dp +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "avdtp_internal.h" +#include "a2dp_internal.h" +#include "a2dp-codec.h" +#include "oi_codec_sbc.h" + +#define A2DP_NO_SPACE (-1) + +struct bt_a2dp { + struct bt_avdtp session; +}; + +typedef struct { + OI_CODEC_SBC_DECODER_CONTEXT decoder_context; + uint32_t context_data[CODEC_DATA_WORDS(SBC_MAX_CHANNELS, SBC_CODEC_FAST_FILTER_BUFFERS)]; + int16_t decode_buf[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; +} A2DP_SBC_DECODER; + +static A2DP_SBC_DECODER sbc_decoder; + +/* Connections */ +static struct bt_a2dp connection[CONFIG_BT_MAX_CONN]; +static struct bt_avdtp_stream stream[CONFIG_BT_MAX_CONN]; + +static struct bt_sdp_attribute a2dp_attrs[] = { + BT_SDP_NEW_SERVICE, + BT_SDP_LIST( + BT_SDP_ATTR_SVCLASS_ID_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_AUDIO_SINK_SVCLASS) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROTO_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(BT_L2CAP_PSM_AVDTP) + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_L2CAP_PSM_AVDTP) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0102) + }, + ) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROFILE_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_ADVANCED_AUDIO_SVCLASS) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0102) + }, + ) + }, + ) + ), + BT_SDP_SERVICE_NAME("A2DP sink"), +}; + +static struct bt_sdp_record a2dp_rec = BT_SDP_RECORD(a2dp_attrs); + +struct bt_a2dp_endpoint endpoint_1; +struct bt_a2dp_endpoint endpoint_2; + +struct bt_a2dp_codec_sbc_params sbc_info; + +void bt_a2dp_set_sbc_codec_info() +{ + sbc_info.config[0] = + //Sampling Frequency + A2DP_SBC_SAMP_FREQ_48000 | + A2DP_SBC_SAMP_FREQ_44100 | + A2DP_SBC_SAMP_FREQ_32000 | + A2DP_SBC_SAMP_FREQ_16000 | + //Channel Mode + A2DP_SBC_CH_MODE_JOINT | + A2DP_SBC_CH_MODE_STREO | + A2DP_SBC_CH_MODE_DUAL | + A2DP_SBC_CH_MODE_MONO; + sbc_info.config[1] = + //Block Length + A2DP_SBC_BLK_LEN_16 | + A2DP_SBC_BLK_LEN_12 | + A2DP_SBC_BLK_LEN_8 | + A2DP_SBC_BLK_LEN_4 | + //Subbands + A2DP_SBC_SUBBAND_8 | + A2DP_SBC_SUBBAND_4 | + //Allocation Method + A2DP_SBC_ALLOC_MTHD_SNR | + A2DP_SBC_ALLOC_MTHD_LOUDNESS; + sbc_info.min_bitpool = 2; + sbc_info.max_bitpool = 53; +} + +void a2d_reset(struct bt_a2dp *a2dp_conn) +{ + (void)memset(a2dp_conn, 0, sizeof(struct bt_a2dp)); +} + +void stream_reset(struct bt_avdtp_stream *stream_conn) +{ + (void)memset(stream_conn, 0, sizeof(struct bt_avdtp_stream)); +} + +struct bt_a2dp *get_new_connection(struct bt_conn *conn) +{ + int8_t i, free; + + free = A2DP_NO_SPACE; + + if (!conn) { + BT_ERR("Invalid Input (err: %d)", -EINVAL); + return NULL; + } + + /* Find a space */ + for (i = 0; i < CONFIG_BT_MAX_CONN; i++) { + if (connection[i].session.br_chan.chan.conn == conn) { + BT_DBG("Conn already exists"); + if(!connection[i].session.streams->chan.chan.conn) + { + BT_DBG("Create AV stream"); + return &connection[i]; + } + else + { + BT_DBG("A2DP signal stream and AV stream already exists"); + return NULL; + } + } + + if (!connection[i].session.br_chan.chan.conn && + free == A2DP_NO_SPACE) { + BT_DBG("Create signal stream"); + free = i; + } + } + + if (free == A2DP_NO_SPACE) { + BT_DBG("More connection cannot be supported"); + return NULL; + } + + /* Clean the memory area before returning */ + a2d_reset(&connection[free]); + stream_reset(&stream[free]); + connection[free].session.streams = &stream[free]; + + return &connection[free]; +} + +int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session) +{ + struct bt_a2dp *a2dp_conn; + + a2dp_conn = get_new_connection(conn); + if (!a2dp_conn) { + return -ENOMEM; + } + + *session = &(a2dp_conn->session); + BT_DBG("session: %p", &(a2dp_conn->session)); + + return 0; +} + +int a2dp_sbc_decode_init() +{ + OI_STATUS status = OI_CODEC_SBC_DecoderReset(&sbc_decoder.decoder_context, + sbc_decoder.context_data, + sizeof(sbc_decoder.context_data), + 2, + 2, + false, + false); + if (!OI_SUCCESS(status)) + { + BT_ERR("decode init failed with error: %d\n", status); + return status; + } + + return 0; +} + +#if PCM_PRINTF +extern int16_t cool_edit[]; +extern uint32_t byte_index; +#endif +int a2dp_sbc_decode_process(uint8_t media_data[], uint16_t data_len) +{ + //remove media header, expose sbc frame + const OI_BYTE * data = media_data + 12 + 1; + OI_UINT32 data_size = data_len - 12 - 1; + + if (data_size <= 0) { + BT_ERR("empty packet\n"); + return -1; + } + + if(data[0] != 0x9c) + { + BT_ERR("sbc frame syncword error \n"); + } + + OI_INT16* pcm = sbc_decoder.decode_buf; + OI_UINT32 pcm_size = sizeof(sbc_decoder.decode_buf); + + OI_INT16 frame_count = OI_CODEC_SBC_FrameCount((OI_BYTE *)data, data_size); + BT_DBG("frame_count: %d\n", frame_count); + + for (int i = 0; i < frame_count; i++) + { + OI_STATUS status = OI_CODEC_SBC_DecodeFrame(&sbc_decoder.decoder_context, + &data, + &data_size, + pcm, + &pcm_size); + if (!OI_SUCCESS(status)) + { + BT_ERR("decoding failure with error: %d \n", status); + return -1; + } + +#if PCM_PRINTF + memcpy((OI_INT8 *)cool_edit + byte_index, pcm, pcm_size); + byte_index += pcm_size; +#endif + + } + + return 0; +} + + + +/* Callback for incoming requests */ +static struct bt_avdtp_ind_cb cb_ind = { + /*TODO*/ +}; + +/* The above callback structures need to be packed and passed to AVDTP */ +static struct bt_avdtp_event_cb avdtp_cb = { + .ind = &cb_ind, + .accept = a2dp_accept +}; + +int bt_a2dp_init(void) +{ + int err; + + /* Register event handlers with AVDTP */ + err = bt_avdtp_register(&avdtp_cb); + if (err < 0) { + BT_ERR("A2DP registration failed"); + return err; + } + + /* Register SDP record */ + err = bt_sdp_register_service(&a2dp_rec); + if(err < 0) + { + BT_ERR("A2DP regist sdp record failed"); + return err; + } + + int reg_1 = bt_a2dp_register_endpoint(&endpoint_1, BT_A2DP_AUDIO, BT_A2DP_SINK); + int reg_2 = bt_a2dp_register_endpoint(&endpoint_2, BT_A2DP_AUDIO, BT_A2DP_SINK); + if (reg_1 || reg_2) { + BT_ERR("A2DP registration endpoint 1 failed"); + return err; + } + + bt_a2dp_set_sbc_codec_info(); + + err = a2dp_sbc_decode_init(); + if (err < 0) { + BT_ERR("sbc codec init failed"); + return err; + } + + BT_DBG("A2DP Initialized successfully."); + return 0; +} + +struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn) +{ + struct bt_a2dp *a2dp_conn; + int err; + + a2dp_conn = get_new_connection(conn); + if (!a2dp_conn) { + BT_ERR("Cannot allocate memory"); + return NULL; + } + + err = bt_avdtp_connect(conn, &(a2dp_conn->session)); + if (err < 0) { + /* If error occurs, undo the saving and return the error */ + a2d_reset(a2dp_conn); + BT_DBG("AVDTP Connect failed"); + return NULL; + } + + BT_DBG("Connect request sent"); + return a2dp_conn; +} + +int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint, + uint8_t media_type, uint8_t role) +{ + int err; + + BT_ASSERT(endpoint); + + err = bt_avdtp_register_sep(media_type, role, &(endpoint->info)); + if (err < 0) { + return err; + } + + return 0; +} + + diff --git a/components/ble/ble_stack/host/a2dp_internal.h b/components/ble/ble_stack/host/a2dp_internal.h new file mode 100644 index 00000000..08e8b76b --- /dev/null +++ b/components/ble/ble_stack/host/a2dp_internal.h @@ -0,0 +1,12 @@ +/** @file + * @brief Advance Audio Distribution Profile Internal header. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* To be called when first SEP is being registered */ +int bt_a2dp_init(void); diff --git a/components/ble/ble_stack/host/at.c b/components/ble/ble_stack/host/at.c new file mode 100644 index 00000000..9b622b18 --- /dev/null +++ b/components/ble/ble_stack/host/at.c @@ -0,0 +1,537 @@ +/** + * @file at.c + * Generic AT command handling library implementation + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "at.h" + +static void next_list(struct at_client *at) +{ + if (at->buf[at->pos] == ',') { + at->pos++; + } +} + +int at_check_byte(struct net_buf *buf, char check_byte) +{ + const unsigned char *str = buf->data; + + if (*str != check_byte) { + return -EINVAL; + } + net_buf_pull(buf, 1); + + return 0; +} + +static void skip_space(struct at_client *at) +{ + while (at->buf[at->pos] == ' ') { + at->pos++; + } +} + +int at_get_number(struct at_client *at, uint32_t *val) +{ + uint32_t i; + + skip_space(at); + + for (i = 0U, *val = 0U; + isdigit((unsigned char)at->buf[at->pos]); + at->pos++, i++) { + *val = *val * 10U + at->buf[at->pos] - '0'; + } + + if (i == 0U) { + return -ENODATA; + } + + next_list(at); + return 0; +} + +static bool str_has_prefix(const char *str, const char *prefix) +{ + if (strncmp(str, prefix, strlen(prefix)) != 0) { + return false; + } + + return true; +} + +static int at_parse_result(const char *str, struct net_buf *buf, + enum at_result *result) +{ + /* Map the result and check for end lf */ + if ((!strncmp(str, "OK", 2)) && (at_check_byte(buf, '\n') == 0)) { + *result = AT_RESULT_OK; + return 0; + } + + if ((!strncmp(str, "ERROR", 5)) && (at_check_byte(buf, '\n')) == 0) { + *result = AT_RESULT_ERROR; + return 0; + } + + return -ENOMSG; +} + +static int get_cmd_value(struct at_client *at, struct net_buf *buf, + char stop_byte, enum at_cmd_state cmd_state) +{ + int cmd_len = 0; + uint8_t pos = at->pos; + const char *str = (char *)buf->data; + + while (cmd_len < buf->len && at->pos != at->buf_max_len) { + if (*str != stop_byte) { + at->buf[at->pos++] = *str; + cmd_len++; + str++; + pos = at->pos; + } else { + cmd_len++; + at->buf[at->pos] = '\0'; + at->pos = 0U; + at->cmd_state = cmd_state; + break; + } + } + net_buf_pull(buf, cmd_len); + + if (pos == at->buf_max_len) { + return -ENOBUFS; + } + + return 0; +} + +static int get_response_string(struct at_client *at, struct net_buf *buf, + char stop_byte, enum at_state state) +{ + int cmd_len = 0; + uint8_t pos = at->pos; + const char *str = (char *)buf->data; + + while (cmd_len < buf->len && at->pos != at->buf_max_len) { + if (*str != stop_byte) { + at->buf[at->pos++] = *str; + cmd_len++; + str++; + pos = at->pos; + } else { + cmd_len++; + at->buf[at->pos] = '\0'; + at->pos = 0U; + at->state = state; + break; + } + } + net_buf_pull(buf, cmd_len); + + if (pos == at->buf_max_len) { + return -ENOBUFS; + } + + return 0; +} + +static void reset_buffer(struct at_client *at) +{ + (void)memset(at->buf, 0, at->buf_max_len); + at->pos = 0U; +} + +static int at_state_start(struct at_client *at, struct net_buf *buf) +{ + int err; + + err = at_check_byte(buf, '\r'); + if (err < 0) { + return err; + } + at->state = AT_STATE_START_CR; + + return 0; +} + +static int at_state_start_cr(struct at_client *at, struct net_buf *buf) +{ + int err; + + err = at_check_byte(buf, '\n'); + if (err < 0) { + return err; + } + at->state = AT_STATE_START_LF; + + return 0; +} + +static int at_state_start_lf(struct at_client *at, struct net_buf *buf) +{ + reset_buffer(at); + if (at_check_byte(buf, '+') == 0) { + at->state = AT_STATE_GET_CMD_STRING; + return 0; + } else if (isalpha(*buf->data)) { + at->state = AT_STATE_GET_RESULT_STRING; + return 0; + } + + return -ENODATA; +} + +static int at_state_get_cmd_string(struct at_client *at, struct net_buf *buf) +{ + return get_response_string(at, buf, ':', AT_STATE_PROCESS_CMD); +} + +static bool is_cmer(struct at_client *at) +{ + if (strncmp(at->buf, "CME ERROR", 9) == 0) { + return true; + } + + return false; +} + +static int at_state_process_cmd(struct at_client *at, struct net_buf *buf) +{ + if (is_cmer(at)) { + at->state = AT_STATE_PROCESS_AG_NW_ERR; + return 0; + } + + if (at->resp) { + at->resp(at, buf); + at->resp = NULL; + return 0; + } + at->state = AT_STATE_UNSOLICITED_CMD; + return 0; +} + +static int at_state_get_result_string(struct at_client *at, struct net_buf *buf) +{ + return get_response_string(at, buf, '\r', AT_STATE_PROCESS_RESULT); +} + +static bool is_ring(struct at_client *at) +{ + if (strncmp(at->buf, "RING", 4) == 0) { + return true; + } + + return false; +} + +static int at_state_process_result(struct at_client *at, struct net_buf *buf) +{ + enum at_cme cme_err; + enum at_result result; + + if (is_ring(at)) { + at->state = AT_STATE_UNSOLICITED_CMD; + return 0; + } + + if (at_parse_result(at->buf, buf, &result) == 0) { + if (at->finish) { + /* cme_err is 0 - Is invalid until result is + * AT_RESULT_CME_ERROR + */ + cme_err = 0; + at->finish(at, result, cme_err); + } + } + + /* Reset the state to process unsolicited response */ + at->cmd_state = AT_CMD_START; + at->state = AT_STATE_START; + + return 0; +} + +int cme_handle(struct at_client *at) +{ + enum at_cme cme_err; + uint32_t val; + + if (!at_get_number(at, &val) && val <= CME_ERROR_NETWORK_NOT_ALLOWED) { + cme_err = val; + } else { + cme_err = CME_ERROR_UNKNOWN; + } + + if (at->finish) { + at->finish(at, AT_RESULT_CME_ERROR, cme_err); + } + + return 0; +} + +static int at_state_process_ag_nw_err(struct at_client *at, struct net_buf *buf) +{ + at->cmd_state = AT_CMD_GET_VALUE; + return at_parse_cmd_input(at, buf, NULL, cme_handle, + AT_CMD_TYPE_NORMAL); +} + +static int at_state_unsolicited_cmd(struct at_client *at, struct net_buf *buf) +{ + if (at->unsolicited) { + return at->unsolicited(at, buf); + } + + return -ENODATA; +} + +/* The order of handler function should match the enum at_state */ +static handle_parse_input_t parser_cb[] = { + at_state_start, /* AT_STATE_START */ + at_state_start_cr, /* AT_STATE_START_CR */ + at_state_start_lf, /* AT_STATE_START_LF */ + at_state_get_cmd_string, /* AT_STATE_GET_CMD_STRING */ + at_state_process_cmd, /* AT_STATE_PROCESS_CMD */ + at_state_get_result_string, /* AT_STATE_GET_RESULT_STRING */ + at_state_process_result, /* AT_STATE_PROCESS_RESULT */ + at_state_process_ag_nw_err, /* AT_STATE_PROCESS_AG_NW_ERR */ + at_state_unsolicited_cmd /* AT_STATE_UNSOLICITED_CMD */ +}; + +int at_parse_input(struct at_client *at, struct net_buf *buf) +{ + int ret; + + while (buf->len) { + if (at->state < AT_STATE_START || at->state >= AT_STATE_END) { + return -EINVAL; + } + ret = parser_cb[at->state](at, buf); + if (ret < 0) { + /* Reset the state in case of error */ + at->cmd_state = AT_CMD_START; + at->state = AT_STATE_START; + return ret; + } + } + + return 0; +} + +static int at_cmd_start(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type) +{ + if (!str_has_prefix(at->buf, prefix)) { + if (type == AT_CMD_TYPE_NORMAL) { + at->state = AT_STATE_UNSOLICITED_CMD; + } + return -ENODATA; + } + + if (type == AT_CMD_TYPE_OTHER) { + /* Skip for Other type such as ..RING.. which does not have + * values to get processed. + */ + at->cmd_state = AT_CMD_PROCESS_VALUE; + } else { + at->cmd_state = AT_CMD_GET_VALUE; + } + + return 0; +} + +static int at_cmd_get_value(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type) +{ + /* Reset buffer before getting the values */ + reset_buffer(at); + return get_cmd_value(at, buf, '\r', AT_CMD_PROCESS_VALUE); +} + +static int at_cmd_process_value(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type) +{ + int ret; + + ret = func(at); + at->cmd_state = AT_CMD_STATE_END_LF; + + return ret; +} + +static int at_cmd_state_end_lf(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type) +{ + int err; + + err = at_check_byte(buf, '\n'); + if (err < 0) { + return err; + } + + at->cmd_state = AT_CMD_START; + at->state = AT_STATE_START; + return 0; +} + +/* The order of handler function should match the enum at_cmd_state */ +static handle_cmd_input_t cmd_parser_cb[] = { + at_cmd_start, /* AT_CMD_START */ + at_cmd_get_value, /* AT_CMD_GET_VALUE */ + at_cmd_process_value, /* AT_CMD_PROCESS_VALUE */ + at_cmd_state_end_lf /* AT_CMD_STATE_END_LF */ +}; + +int at_parse_cmd_input(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type) +{ + int ret; + + while (buf->len) { + if (at->cmd_state < AT_CMD_START || + at->cmd_state >= AT_CMD_STATE_END) { + return -EINVAL; + } + ret = cmd_parser_cb[at->cmd_state](at, buf, prefix, func, type); + if (ret < 0) { + return ret; + } + /* Check for main state, the end of cmd parsing and return. */ + if (at->state == AT_STATE_START) { + return 0; + } + } + + return 0; +} + +int at_has_next_list(struct at_client *at) +{ + return at->buf[at->pos] != '\0'; +} + +int at_open_list(struct at_client *at) +{ + skip_space(at); + + /* The list shall start with '(' open parenthesis */ + if (at->buf[at->pos] != '(') { + return -ENODATA; + } + at->pos++; + + return 0; +} + +int at_close_list(struct at_client *at) +{ + skip_space(at); + + if (at->buf[at->pos] != ')') { + return -ENODATA; + } + at->pos++; + + next_list(at); + + return 0; +} + +int at_list_get_string(struct at_client *at, char *name, uint8_t len) +{ + int i = 0; + + skip_space(at); + + if (at->buf[at->pos] != '"') { + return -ENODATA; + } + at->pos++; + + while (at->buf[at->pos] != '\0' && at->buf[at->pos] != '"') { + if (i == len) { + return -ENODATA; + } + name[i++] = at->buf[at->pos++]; + } + + if (i == len) { + return -ENODATA; + } + + name[i] = '\0'; + + if (at->buf[at->pos] != '"') { + return -ENODATA; + } + at->pos++; + + skip_space(at); + next_list(at); + + return 0; +} + +int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max) +{ + uint32_t low, high; + int ret; + + ret = at_get_number(at, &low); + if (ret < 0) { + return ret; + } + + if (at->buf[at->pos] == '-') { + at->pos++; + goto out; + } + + if (!isdigit((unsigned char)at->buf[at->pos])) { + return -ENODATA; + } +out: + ret = at_get_number(at, &high); + if (ret < 0) { + return ret; + } + + *min = low; + *max = high; + + next_list(at); + + return 0; +} + +void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited) +{ + at->unsolicited = unsolicited; +} + +void at_register(struct at_client *at, at_resp_cb_t resp, at_finish_cb_t finish) +{ + at->resp = resp; + at->finish = finish; + at->state = AT_STATE_START; +} diff --git a/components/ble/ble_stack/host/at.h b/components/ble/ble_stack/host/at.h new file mode 100644 index 00000000..ae2c7f95 --- /dev/null +++ b/components/ble/ble_stack/host/at.h @@ -0,0 +1,118 @@ +/** @file at.h + * @brief Internal APIs for AT command handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enum at_result { + AT_RESULT_OK, + AT_RESULT_ERROR, + AT_RESULT_CME_ERROR +}; + +enum at_cme { + CME_ERROR_AG_FAILURE = 0, + CME_ERROR_NO_CONNECTION_TO_PHONE = 1, + CME_ERROR_OPERATION_NOT_ALLOWED = 3, + CME_ERROR_OPERATION_NOT_SUPPORTED = 4, + CME_ERROR_PH_SIM_PIN_REQUIRED = 5, + CME_ERROR_SIM_NOT_INSERTED = 10, + CME_ERROR_SIM_PIN_REQUIRED = 11, + CME_ERROR_SIM_PUK_REQUIRED = 12, + CME_ERROR_SIM_FAILURE = 13, + CME_ERROR_SIM_BUSY = 14, + CME_ERROR_INCORRECT_PASSWORD = 16, + CME_ERROR_SIM_PIN2_REQUIRED = 17, + CME_ERROR_SIM_PUK2_REQUIRED = 18, + CME_ERROR_MEMORY_FULL = 20, + CME_ERROR_INVALID_INDEX = 21, + CME_ERROR_MEMORY_FAILURE = 23, + CME_ERROR_TEXT_STRING_TOO_LONG = 24, + CME_ERROR_INVALID_CHARS_IN_TEXT_STRING = 25, + CME_ERROR_DIAL_STRING_TO_LONG = 26, + CME_ERROR_INVALID_CHARS_IN_DIAL_STRING = 27, + CME_ERROR_NO_NETWORK_SERVICE = 30, + CME_ERROR_NETWORK_TIMEOUT = 31, + CME_ERROR_NETWORK_NOT_ALLOWED = 32, + CME_ERROR_UNKNOWN = 33, +}; + +enum at_state { + AT_STATE_START, + AT_STATE_START_CR, + AT_STATE_START_LF, + AT_STATE_GET_CMD_STRING, + AT_STATE_PROCESS_CMD, + AT_STATE_GET_RESULT_STRING, + AT_STATE_PROCESS_RESULT, + AT_STATE_PROCESS_AG_NW_ERR, + AT_STATE_UNSOLICITED_CMD, + AT_STATE_END +}; + +enum at_cmd_state { + AT_CMD_START, + AT_CMD_GET_VALUE, + AT_CMD_PROCESS_VALUE, + AT_CMD_STATE_END_LF, + AT_CMD_STATE_END +}; + +enum at_cmd_type { + AT_CMD_TYPE_NORMAL, + AT_CMD_TYPE_UNSOLICITED, + AT_CMD_TYPE_OTHER +}; + +struct at_client; + +/* Callback at_resp_cb_t used to parse response value received for the + * particular AT command. Eg: +CIND= + */ +typedef int (*at_resp_cb_t)(struct at_client *at, struct net_buf *buf); + +/* Callback at_finish_cb used to monitor the success or failure of the AT + * command received from server. + * Argument 'cme_err' is valid only when argument 'result' is equal to + * AT_RESULT_CME_ERROR + */ +typedef int (*at_finish_cb_t)(struct at_client *at, enum at_result result, + enum at_cme cme_err); +typedef int (*parse_val_t)(struct at_client *at); +typedef int (*handle_parse_input_t)(struct at_client *at, struct net_buf *buf); +typedef int (*handle_cmd_input_t)(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type); + +struct at_client { + char *buf; + uint8_t pos; + uint8_t buf_max_len; + uint8_t state; + uint8_t cmd_state; + at_resp_cb_t resp; + at_resp_cb_t unsolicited; + at_finish_cb_t finish; +}; + +/* Register the callback functions */ +void at_register(struct at_client *at, at_resp_cb_t resp, + at_finish_cb_t finish); +void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited); +int at_get_number(struct at_client *at, uint32_t *val); +/* This parsing will only works for non-fragmented net_buf */ +int at_parse_input(struct at_client *at, struct net_buf *buf); +/* This command parsing will only works for non-fragmented net_buf */ +int at_parse_cmd_input(struct at_client *at, struct net_buf *buf, + const char *prefix, parse_val_t func, + enum at_cmd_type type); +int at_check_byte(struct net_buf *buf, char check_byte); +int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max); +int at_list_get_string(struct at_client *at, char *name, uint8_t len); +int at_close_list(struct at_client *at); +int at_open_list(struct at_client *at); +int at_has_next_list(struct at_client *at); diff --git a/components/ble/ble_stack/host/att.c b/components/ble/ble_stack/host/att.c new file mode 100644 index 00000000..2ae46a88 --- /dev/null +++ b/components/ble/ble_stack/host/att.c @@ -0,0 +1,2448 @@ +/* att.c - Attribute protocol handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_ATT) +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "smp.h" +#include "att_internal.h" +#include "gatt_internal.h" + +#define ATT_CHAN(_ch) CONTAINER_OF(_ch, struct bt_att, chan.chan) +#define ATT_REQ(_node) CONTAINER_OF(_node, struct bt_att_req, node) + +#define ATT_CMD_MASK 0x40 + +#define ATT_TIMEOUT K_SECONDS(30) + +typedef enum __packed { + ATT_COMMAND, + ATT_REQUEST, + ATT_RESPONSE, + ATT_NOTIFICATION, + ATT_CONFIRMATION, + ATT_INDICATION, + ATT_UNKNOWN, +} att_type_t; + +static att_type_t att_op_get_type(u8_t op); + +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 +struct bt_attr_data { + u16_t handle; + u16_t offset; +}; + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +/* Pool for incoming ATT packets */ +NET_BUF_POOL_DEFINE(prep_pool, CONFIG_BT_ATT_PREPARE_COUNT, BT_ATT_MTU, + sizeof(struct bt_attr_data), NULL); +#else +struct net_buf_pool prep_pool; +#endif +#endif /* CONFIG_BT_ATT_PREPARE_COUNT */ + +enum { + ATT_PENDING_RSP, + ATT_PENDING_CFM, + ATT_DISCONNECTED, + + /* Total number of flags - must be at the end of the enum */ + ATT_NUM_FLAGS, +}; + +/* ATT channel specific context */ +struct bt_att { + /* The channel this context is associated with */ + struct bt_l2cap_le_chan chan; + ATOMIC_DEFINE(flags, ATT_NUM_FLAGS); + struct bt_att_req *req; + sys_slist_t reqs; + struct k_delayed_work timeout_work; + struct k_sem tx_sem; + struct k_fifo tx_queue; +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 + struct k_fifo prep_queue; +#endif +}; + +#if defined(CONFIG_BT_STACK_PTS) +extern volatile u8_t event_flag; +#endif + +static struct bt_att bt_req_pool[CONFIG_BT_MAX_CONN]; +static struct bt_att_req cancel; + + +#if defined(CONFIG_BLE_AT_CMD) +static u16_t mtu_size = BT_ATT_MTU; +void set_mtu_size(u16_t size) +{ + mtu_size = size; +} +#endif + + +static void att_req_destroy(struct bt_att_req *req) +{ + BT_DBG("req %p", req); + + if (req->buf) { + net_buf_unref(req->buf); + } + + if (req->destroy) { + req->destroy(req); + } + + (void)memset(req, 0, sizeof(*req)); +} + +static struct bt_att *att_get(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan; + + chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_ATT); + __ASSERT(chan, "No ATT channel found"); + + return CONTAINER_OF(chan, struct bt_att, chan); +} + +static bt_conn_tx_cb_t att_cb(struct net_buf *buf); + +static int att_send(struct bt_conn *conn, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data) +{ + struct bt_att_hdr *hdr; + + hdr = (void *)buf->data; + + BT_DBG("code 0x%02x", hdr->code); + + #if defined(CONFIG_BT_SMP) && defined(CONFIG_BT_SIGNING) + if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) { + int err; + + err = bt_smp_sign(conn, buf); + if (err) { + BT_ERR("Error signing data"); + net_buf_unref(buf); + return err; + } + } + #endif + return bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, buf, + cb ? cb : att_cb(buf), + user_data); +} + +void att_pdu_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_att *att = att_get(conn); + struct net_buf *buf; + + BT_DBG("conn %p att %p", conn, att); + + while ((buf = net_buf_get(&att->tx_queue, K_NO_WAIT))) { + /* Check if the queued buf is a request */ + if (att->req && att->req->buf == buf) { + /* Save request state so it can be resent */ + net_buf_simple_save(&att->req->buf->b, + &att->req->state); + } + + if (!att_send(conn, buf, NULL, NULL)) { + return; + } + } + + k_sem_give(&att->tx_sem); +} + +void att_cfm_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_att *att = att_get(conn); + + BT_DBG("conn %p att %p", conn, att); + + if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) { + atomic_clear_bit(att->flags, ATT_PENDING_CFM); + } + + att_pdu_sent(conn, user_data); +} + +void att_rsp_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_att *att = att_get(conn); + + BT_DBG("conn %p att %p", conn, att); + + if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) { + atomic_clear_bit(att->flags, ATT_PENDING_RSP); + } + + att_pdu_sent(conn, user_data); +} + +void att_req_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_att *att = att_get(conn); + + BT_DBG("conn %p att %p att->req %p", conn, att, att->req); + + /* Start timeout work */ + if (att->req) { + k_delayed_work_submit(&att->timeout_work, ATT_TIMEOUT); + } + + att_pdu_sent(conn, user_data); +} + +static bt_conn_tx_cb_t att_cb(struct net_buf *buf) +{ + switch (att_op_get_type(buf->data[0])) { + case ATT_RESPONSE: + return att_rsp_sent; + case ATT_CONFIRMATION: + return att_cfm_sent; + case ATT_REQUEST: + case ATT_INDICATION: + return att_req_sent; + default: + return att_pdu_sent; + } +} + +static void send_err_rsp(struct bt_conn *conn, u8_t req, u16_t handle, + u8_t err) +{ + struct bt_att_error_rsp *rsp; + struct net_buf *buf; + + /* Ignore opcode 0x00 */ + if (!req) { + return; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_ERROR_RSP, sizeof(*rsp)); + if (!buf) { + return; + } + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->request = req; + rsp->handle = sys_cpu_to_le16(handle); + rsp->error = err; + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, buf, att_rsp_sent, NULL); +} + +static u8_t att_mtu_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_exchange_mtu_req *req; + struct bt_att_exchange_mtu_rsp *rsp; + struct net_buf *pdu; + u16_t mtu_client, mtu_server; + + req = (void *)buf->data; + + mtu_client = sys_le16_to_cpu(req->mtu); + + BT_DBG("Client MTU %u", mtu_client); + + /* Check if MTU is valid */ + if (mtu_client < BT_ATT_DEFAULT_LE_MTU) { + return BT_ATT_ERR_INVALID_PDU; + } + + pdu = bt_att_create_pdu(conn, BT_ATT_OP_MTU_RSP, sizeof(*rsp)); + if (!pdu) { + return BT_ATT_ERR_UNLIKELY; + } + + mtu_server = BT_ATT_MTU; + + BT_DBG("Server MTU %u", mtu_server); + + rsp = net_buf_add(pdu, sizeof(*rsp)); + rsp->mtu = sys_cpu_to_le16(mtu_server); + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, pdu, att_rsp_sent, NULL); + + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484: + * + * A device's Exchange MTU Request shall contain the same MTU as the + * device's Exchange MTU Response (i.e. the MTU shall be symmetric). + */ + att->chan.rx.mtu = MIN(mtu_client, mtu_server); + att->chan.tx.mtu = att->chan.rx.mtu; + + BT_DBG("Negotiated MTU %u", att->chan.rx.mtu); + + #if defined(BFLB_BLE_MTU_CHANGE_CB) + if(att->chan.chan.ops->mtu_changed) + att->chan.chan.ops->mtu_changed(&(att->chan.chan), att->chan.rx.mtu); + #endif + + return 0; +} + +static inline bool att_is_connected(struct bt_att *att) +{ + return (att->chan.chan.conn->state != BT_CONN_CONNECTED || + !atomic_test_bit(att->flags, ATT_DISCONNECTED)); +} + +static int att_send_req(struct bt_att *att, struct bt_att_req *req) +{ + int err; + + __ASSERT_NO_MSG(req); + __ASSERT_NO_MSG(req->func); + __ASSERT_NO_MSG(!att->req); + + BT_DBG("req %p", req); + + att->req = req; + + if (k_sem_take(&att->tx_sem, K_NO_WAIT) < 0) { + k_fifo_put(&att->tx_queue, req->buf); + return 0; + } + + /* Save request state so it can be resent */ + net_buf_simple_save(&req->buf->b, &req->state); + + /* Keep a reference for resending in case of an error */ + err = bt_l2cap_send_cb(att->chan.chan.conn, BT_L2CAP_CID_ATT, + net_buf_ref(req->buf), att_cb(req->buf), NULL); + if (err) { + net_buf_unref(req->buf); + req->buf = NULL; + return err; + } + + return 0; +} + +static void att_process(struct bt_att *att) +{ + sys_snode_t *node; + + BT_DBG(""); + + /* Pull next request from the list */ + node = sys_slist_get(&att->reqs); + if (!node) { + return; + } + + att_send_req(att, ATT_REQ(node)); +} + +static u8_t att_handle_rsp(struct bt_att *att, void *pdu, u16_t len, u8_t err) +{ + bt_att_func_t func; + + BT_DBG("err 0x%02x len %u: %s", err, len, bt_hex(pdu, len)); + + /* Cancel timeout if ongoing */ + k_delayed_work_cancel(&att->timeout_work); + + if (!att->req) { + BT_WARN("No pending ATT request"); + goto process; + } + + /* Check if request has been cancelled */ + if (att->req == &cancel) { + att->req = NULL; + goto process; + } + + /* Release original buffer */ + if (att->req->buf) { + net_buf_unref(att->req->buf); + att->req->buf = NULL; + } + + /* Reset func so it can be reused by the callback */ + func = att->req->func; + att->req->func = NULL; + + func(att->chan.chan.conn, err, pdu, len, att->req); + + /* Don't destroy if callback had reused the request */ + if (!att->req->func) { + att_req_destroy(att->req); + } + + att->req = NULL; + +process: + /* Process pending requests */ + att_process(att); + + return 0; +} + +#if defined(CONFIG_BT_GATT_CLIENT) +static u8_t att_mtu_rsp(struct bt_att *att, struct net_buf *buf) +{ + struct bt_att_exchange_mtu_rsp *rsp; + u16_t mtu; + + if (!att) { + return 0; + } + + rsp = (void *)buf->data; + + mtu = sys_le16_to_cpu(rsp->mtu); + + BT_DBG("Server MTU %u", mtu); + + /* Check if MTU is valid */ + if (mtu < BT_ATT_DEFAULT_LE_MTU) { + return att_handle_rsp(att, NULL, 0, BT_ATT_ERR_INVALID_PDU); + } + + att->chan.rx.mtu = MIN(mtu, BT_ATT_MTU); + + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 484: + * + * A device's Exchange MTU Request shall contain the same MTU as the + * device's Exchange MTU Response (i.e. the MTU shall be symmetric). + */ + att->chan.tx.mtu = att->chan.rx.mtu; + + BT_DBG("Negotiated MTU %u", att->chan.rx.mtu); + + return att_handle_rsp(att, rsp, buf->len, 0); +} +#endif /* CONFIG_BT_GATT_CLIENT */ + +static bool range_is_valid(u16_t start, u16_t end, u16_t *err) +{ + /* Handle 0 is invalid */ + if (!start || !end) { + if (err) { + *err = 0U; + } + return false; + } + + /* Check if range is valid */ + if (start > end) { + if (err) { + *err = start; + } + return false; + } + + return true; +} + +struct find_info_data { + struct bt_att *att; + struct net_buf *buf; + struct bt_att_find_info_rsp *rsp; + union { + struct bt_att_info_16 *info16; + struct bt_att_info_128 *info128; + }; +}; + +static u8_t find_info_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct find_info_data *data = user_data; + struct bt_att *att = data->att; + + BT_DBG("handle 0x%04x", attr->handle); + + /* Initialize rsp at first entry */ + if (!data->rsp) { + data->rsp = net_buf_add(data->buf, sizeof(*data->rsp)); + data->rsp->format = (attr->uuid->type == BT_UUID_TYPE_16) ? + BT_ATT_INFO_16 : BT_ATT_INFO_128; + } + + switch (data->rsp->format) { + case BT_ATT_INFO_16: + if (attr->uuid->type != BT_UUID_TYPE_16) { + return BT_GATT_ITER_STOP; + } + + /* Fast forward to next item position */ + data->info16 = net_buf_add(data->buf, sizeof(*data->info16)); + data->info16->handle = sys_cpu_to_le16(attr->handle); + data->info16->uuid = sys_cpu_to_le16(BT_UUID_16(attr->uuid)->val); + + if (att->chan.tx.mtu - data->buf->len > + sizeof(*data->info16)) { + return BT_GATT_ITER_CONTINUE; + } + + break; + case BT_ATT_INFO_128: + if (attr->uuid->type != BT_UUID_TYPE_128) { + return BT_GATT_ITER_STOP; + } + + /* Fast forward to next item position */ + data->info128 = net_buf_add(data->buf, sizeof(*data->info128)); + data->info128->handle = sys_cpu_to_le16(attr->handle); + memcpy(data->info128->uuid, BT_UUID_128(attr->uuid)->val, + sizeof(data->info128->uuid)); + + if (att->chan.tx.mtu - data->buf->len > + sizeof(*data->info128)) { + return BT_GATT_ITER_CONTINUE; + } + } + + return BT_GATT_ITER_STOP; +} + +static u8_t att_find_info_rsp(struct bt_att *att, u16_t start_handle, + u16_t end_handle) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct find_info_data data; + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_RSP, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + bt_gatt_foreach_attr(start_handle, end_handle, find_info_cb, &data); + + if (!data.rsp) { + net_buf_unref(data.buf); + /* Respond since handle is set */ + send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} + +static u8_t att_find_info_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_find_info_req *req; + u16_t start_handle, end_handle, err_handle; + + req = (void *)buf->data; + + start_handle = sys_le16_to_cpu(req->start_handle); + end_handle = sys_le16_to_cpu(req->end_handle); + + BT_DBG("start_handle 0x%04x end_handle 0x%04x", start_handle, + end_handle); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) { + send_err_rsp(conn, BT_ATT_OP_FIND_INFO_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + return att_find_info_rsp(att, start_handle, end_handle); +} + +struct find_type_data { + struct bt_att *att; + struct net_buf *buf; + struct bt_att_handle_group *group; + const void *value; + u8_t value_len; + u8_t err; +}; + +static u8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct find_type_data *data = user_data; + struct bt_att *att = data->att; + struct bt_conn *conn = att->chan.chan.conn; + int read; + u8_t uuid[16]; + + /* Skip secondary services */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) { + goto skip; + } + + /* Update group end_handle if not a primary service */ + if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY)) { + if (data->group && + attr->handle > sys_le16_to_cpu(data->group->end_handle)) { + data->group->end_handle = sys_cpu_to_le16(attr->handle); + } + return BT_GATT_ITER_CONTINUE; + } + + BT_DBG("handle 0x%04x", attr->handle); + + /* stop if there is no space left */ + if (att->chan.tx.mtu - data->buf->len < sizeof(*data->group)) { + return BT_GATT_ITER_STOP; + } + + /* Read attribute value and store in the buffer */ + read = attr->read(conn, attr, uuid, sizeof(uuid), 0); + if (read < 0) { + /* + * Since we don't know if it is the service with requested UUID, + * we cannot respond with an error to this request. + */ + goto skip; + } + + /* Check if data matches */ + if (read != data->value_len) { + /* Use bt_uuid_cmp() to compare UUIDs of different form. */ + struct bt_uuid_128 ref_uuid; + struct bt_uuid_128 recvd_uuid; + + if (!bt_uuid_create(&recvd_uuid.uuid, data->value, data->value_len)) { + BT_WARN("Unable to create UUID: size %u", data->value_len); + goto skip; + } + if (!bt_uuid_create(&ref_uuid.uuid, uuid, read)) { + BT_WARN("Unable to create UUID: size %d", read); + goto skip; + } + if (bt_uuid_cmp(&recvd_uuid.uuid, &ref_uuid.uuid)) { + goto skip; + } + } else if (memcmp(data->value, uuid, read)) { + goto skip; + } + + /* If service has been found, error should be cleared */ + data->err = 0x00; + + /* Fast forward to next item position */ + data->group = net_buf_add(data->buf, sizeof(*data->group)); + data->group->start_handle = sys_cpu_to_le16(attr->handle); + data->group->end_handle = sys_cpu_to_le16(attr->handle); + + /* continue to find the end_handle */ + return BT_GATT_ITER_CONTINUE; + +skip: + data->group = NULL; + return BT_GATT_ITER_CONTINUE; +} + +static u8_t att_find_type_rsp(struct bt_att *att, u16_t start_handle, + u16_t end_handle, const void *value, + u8_t value_len) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct find_type_data data; + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_RSP, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + data.group = NULL; + data.value = value; + data.value_len = value_len; + + /* Pre-set error in case no service will be found */ + data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND; + + bt_gatt_foreach_attr(start_handle, end_handle, find_type_cb, &data); + + /* If error has not been cleared, no service has been found */ + if (data.err) { + net_buf_unref(data.buf); + /* Respond since handle is set */ + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle, + data.err); + + #if defined(CONFIG_BT_STACK_PTS) + /*PTS sends a request to the iut discover all primary services it contains */ + if(event_flag == att_find_by_type_value_ind){ + BT_PTS("rsp err : [%d] start_handle = [0x%04x]\r\n",data.err,start_handle); + } + #endif + return 0; + } + + #if defined(CONFIG_BT_STACK_PTS) + /*when PTS sends a request to the iut discover all primary services it contains, set event flag + * to @att_find_by_type_value_ind make it easy for the user to check whether the messages is correct in the console. + */ + if(event_flag == att_find_by_type_value_ind){ + u8_t i = 0; + u8_t *req_val = (u8_t *)data.value; + u8_t src[20]; + + (void)memcpy(src, req_val, data.value_len); + + BT_PTS("uuid = ["); + for(i=0;idata[1] | data.buf->data[2] << 8, + data.buf->data[3] | data.buf->data[4] << 8); + + } + #endif + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} + +static u8_t att_find_type_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_find_type_req *req; + u16_t start_handle, end_handle, err_handle, type; + u8_t *value; + + req = net_buf_pull_mem(buf, sizeof(*req)); + + start_handle = sys_le16_to_cpu(req->start_handle); + end_handle = sys_le16_to_cpu(req->end_handle); + type = sys_le16_to_cpu(req->type); + value = buf->data; + + BT_DBG("start_handle 0x%04x end_handle 0x%04x type %u", start_handle, + end_handle, type); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) { + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + /* The Attribute Protocol Find By Type Value Request shall be used with + * the Attribute Type parameter set to the UUID for "Primary Service" + * and the Attribute Value set to the 16-bit Bluetooth UUID or 128-bit + * UUID for the specific primary service. + */ + if (bt_uuid_cmp(BT_UUID_DECLARE_16(type), BT_UUID_GATT_PRIMARY)) { + send_err_rsp(conn, BT_ATT_OP_FIND_TYPE_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + return att_find_type_rsp(att, start_handle, end_handle, value, + buf->len); +} + +static u8_t err_to_att(int err) +{ + BT_DBG("%d", err); + + if (err < 0 && err >= -0xff) { + return -err; + } + + return BT_ATT_ERR_UNLIKELY; +} + +struct read_type_data { + struct bt_att *att; + struct bt_uuid *uuid; + struct net_buf *buf; + struct bt_att_read_type_rsp *rsp; + struct bt_att_data *item; + u8_t err; +}; + +static u8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct read_type_data *data = user_data; + struct bt_att *att = data->att; + struct bt_conn *conn = att->chan.chan.conn; + int read; + + /* Skip if doesn't match */ + if (bt_uuid_cmp(attr->uuid, data->uuid)) { + return BT_GATT_ITER_CONTINUE; + } + + BT_DBG("handle 0x%04x", attr->handle); + + /* + * If an attribute in the set of requested attributes would cause an + * Error Response then this attribute cannot be included in a + * Read By Type Response and the attributes before this attribute + * shall be returned + * + * If the first attribute in the set of requested attributes would + * cause an Error Response then no other attributes in the requested + * attributes can be considered. + */ + data->err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_READ_MASK); + if (data->err) { + if (data->rsp->len) { + data->err = 0x00; + } + return BT_GATT_ITER_STOP; + } + + /* + * If any attribute is founded in handle range it means that error + * should be changed from pre-set: attr not found error to no error. + */ + data->err = 0x00; + + /* Fast forward to next item position */ + data->item = net_buf_add(data->buf, sizeof(*data->item)); + data->item->handle = sys_cpu_to_le16(attr->handle); + + /* Read attribute value and store in the buffer */ + read = attr->read(conn, attr, data->buf->data + data->buf->len, + att->chan.tx.mtu - data->buf->len, 0); + if (read < 0) { + data->err = err_to_att(read); + return BT_GATT_ITER_STOP; + } + + if (!data->rsp->len) { + /* Set len to be the first item found */ + data->rsp->len = read + sizeof(*data->item); + } else if (data->rsp->len != read + sizeof(*data->item)) { + /* All items should have the same size */ + data->buf->len -= sizeof(*data->item); + return BT_GATT_ITER_STOP; + } + + net_buf_add(data->buf, read); + + /* return true only if there are still space for more items */ + return att->chan.tx.mtu - data->buf->len > data->rsp->len ? + BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; +} + +static u8_t att_read_type_rsp(struct bt_att *att, struct bt_uuid *uuid, + u16_t start_handle, u16_t end_handle) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct read_type_data data; + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_RSP, + sizeof(*data.rsp)); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + data.uuid = uuid; + data.rsp = net_buf_add(data.buf, sizeof(*data.rsp)); + data.rsp->len = 0U; + + /* Pre-set error if no attr will be found in handle */ + data.err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND; + + bt_gatt_foreach_attr(start_handle, end_handle, read_type_cb, &data); + + if (data.err) { + net_buf_unref(data.buf); + /* Response here since handle is set */ + send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, start_handle, + data.err); + return 0; + } + + #if defined(CONFIG_BT_STACK_PTS) + if(event_flag == att_read_by_type_ind) + BT_PTS("handle : [0x%04x]\r\n",data.rsp->data->handle); + #endif + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} + +static u8_t att_read_type_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_read_type_req *req; + u16_t start_handle, end_handle, err_handle; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + u8_t uuid_len = buf->len - sizeof(*req); + + /* Type can only be UUID16 or UUID128 */ + if (uuid_len != 2 && uuid_len != 16) { + return BT_ATT_ERR_INVALID_PDU; + } + + req = net_buf_pull_mem(buf, sizeof(*req)); + + start_handle = sys_le16_to_cpu(req->start_handle); + end_handle = sys_le16_to_cpu(req->end_handle); + if (!bt_uuid_create(&u.uuid, req->uuid, uuid_len)) { + return BT_ATT_ERR_UNLIKELY; + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x type %s", + start_handle, end_handle, bt_uuid_str(&u.uuid)); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) { + send_err_rsp(conn, BT_ATT_OP_READ_TYPE_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + return att_read_type_rsp(att, &u.uuid, start_handle, end_handle); +} + +struct read_data { + struct bt_att *att; + u16_t offset; + struct net_buf *buf; + struct bt_att_read_rsp *rsp; + u8_t err; +}; + +static u8_t read_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct read_data *data = user_data; + struct bt_att *att = data->att; + struct bt_conn *conn = att->chan.chan.conn; + int read; + + BT_DBG("handle 0x%04x", attr->handle); + + data->rsp = net_buf_add(data->buf, sizeof(*data->rsp)); + + /* + * If any attribute is founded in handle range it means that error + * should be changed from pre-set: invalid handle error to no error. + */ + data->err = 0x00; + + /* Check attribute permissions */ + data->err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_READ_MASK); + if (data->err) { + return BT_GATT_ITER_STOP; + } + + /* Read attribute value and store in the buffer */ + read = attr->read(conn, attr, data->buf->data + data->buf->len, + att->chan.tx.mtu - data->buf->len, data->offset); + if (read < 0) { + data->err = err_to_att(read); + return BT_GATT_ITER_STOP; + } + + net_buf_add(data->buf, read); + + return BT_GATT_ITER_CONTINUE; +} + +static u8_t att_read_rsp(struct bt_att *att, u8_t op, u8_t rsp, u16_t handle, + u16_t offset) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct read_data data; + + if (!bt_gatt_change_aware(conn, true)) { + return BT_ATT_ERR_DB_OUT_OF_SYNC; + } + + if (!handle) { + return BT_ATT_ERR_INVALID_HANDLE; + } + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, rsp, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + data.offset = offset; + + /* Pre-set error if no attr will be found in handle */ + data.err = BT_ATT_ERR_INVALID_HANDLE; + + bt_gatt_foreach_attr(handle, handle, read_cb, &data); + + /* In case of error discard data and respond with an error */ + if (data.err) { + net_buf_unref(data.buf); + /* Respond here since handle is set */ + send_err_rsp(conn, op, handle, data.err); + return 0; + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} + +static u8_t att_read_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_att_read_req *req; + u16_t handle; + + req = (void *)buf->data; + + handle = sys_le16_to_cpu(req->handle); + + BT_DBG("handle 0x%04x", handle); + + return att_read_rsp(att, BT_ATT_OP_READ_REQ, BT_ATT_OP_READ_RSP, + handle, 0); +} + +static u8_t att_read_blob_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_att_read_blob_req *req; + u16_t handle, offset; + + req = (void *)buf->data; + + handle = sys_le16_to_cpu(req->handle); + offset = sys_le16_to_cpu(req->offset); + + BT_DBG("handle 0x%04x offset %u", handle, offset); + + return att_read_rsp(att, BT_ATT_OP_READ_BLOB_REQ, + BT_ATT_OP_READ_BLOB_RSP, handle, offset); +} + +#if defined(CONFIG_BT_GATT_READ_MULTIPLE) +static u8_t att_read_mult_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct read_data data; + u16_t handle; + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_RSP, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + + while (buf->len >= sizeof(u16_t)) { + handle = net_buf_pull_le16(buf); + + BT_DBG("handle 0x%04x ", handle); + + /* An Error Response shall be sent by the server in response to + * the Read Multiple Request [....] if a read operation is not + * permitted on any of the Characteristic Values. + * + * If handle is not valid then return invalid handle error. + * If handle is found error will be cleared by read_cb. + */ + data.err = BT_ATT_ERR_INVALID_HANDLE; + + bt_gatt_foreach_attr(handle, handle, read_cb, &data); + + /* Stop reading in case of error */ + if (data.err) { + net_buf_unref(data.buf); + /* Respond here since handle is set */ + send_err_rsp(conn, BT_ATT_OP_READ_MULT_REQ, handle, + data.err); + return 0; + } + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} +#endif /* CONFIG_BT_GATT_READ_MULTIPLE */ + +struct read_group_data { + struct bt_att *att; + struct bt_uuid *uuid; + struct net_buf *buf; + struct bt_att_read_group_rsp *rsp; + struct bt_att_group_data *group; +}; + +static u8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct read_group_data *data = user_data; + struct bt_att *att = data->att; + struct bt_conn *conn = att->chan.chan.conn; + int read; + + /* Update group end_handle if attribute is not a service */ + if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) && + bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) { + if (data->group && + attr->handle > sys_le16_to_cpu(data->group->end_handle)) { + data->group->end_handle = sys_cpu_to_le16(attr->handle); + + } + return BT_GATT_ITER_CONTINUE; + } + + /* If Group Type don't match skip */ + if (bt_uuid_cmp(attr->uuid, data->uuid)) { + data->group = NULL; + return BT_GATT_ITER_CONTINUE; + } + + BT_DBG("handle 0x%04x", attr->handle); + + /* Stop if there is no space left */ + if (data->rsp->len && + att->chan.tx.mtu - data->buf->len < data->rsp->len) { + return BT_GATT_ITER_STOP; + } + + /* Fast forward to next group position */ + data->group = net_buf_add(data->buf, sizeof(*data->group)); + + /* Initialize group handle range */ + data->group->start_handle = sys_cpu_to_le16(attr->handle); + data->group->end_handle = sys_cpu_to_le16(attr->handle); + + + /* Read attribute value and store in the buffer */ + read = attr->read(conn, attr, data->buf->data + data->buf->len, + att->chan.tx.mtu - data->buf->len, 0); + if (read < 0) { + /* TODO: Handle read errors */ + return BT_GATT_ITER_STOP; + } + + if (!data->rsp->len) { + /* Set len to be the first group found */ + data->rsp->len = read + sizeof(*data->group); + } else if (data->rsp->len != read + sizeof(*data->group)) { + /* All groups entries should have the same size */ + data->buf->len -= sizeof(*data->group); + return false; + } + + net_buf_add(data->buf, read); + + /* Continue to find the end handle */ + return BT_GATT_ITER_CONTINUE; +} + +static u8_t att_read_group_rsp(struct bt_att *att, struct bt_uuid *uuid, + u16_t start_handle, u16_t end_handle) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct read_group_data data; + + (void)memset(&data, 0, sizeof(data)); + + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_RSP, + sizeof(*data.rsp)); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + data.att = att; + data.uuid = uuid; + data.rsp = net_buf_add(data.buf, sizeof(*data.rsp)); + data.rsp->len = 0U; + data.group = NULL; + + bt_gatt_foreach_attr(start_handle, end_handle, read_group_cb, &data); + + + if (!data.rsp->len) { + net_buf_unref(data.buf); + /* Respond here since handle is set */ + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, + BT_ATT_ERR_ATTRIBUTE_NOT_FOUND); + return 0; + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} + +static u8_t att_read_group_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_read_group_req *req; + u16_t start_handle, end_handle, err_handle; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + u8_t uuid_len = buf->len - sizeof(*req); + + /* Type can only be UUID16 or UUID128 */ + if (uuid_len != 2 && uuid_len != 16) { + return BT_ATT_ERR_INVALID_PDU; + } + + req = net_buf_pull_mem(buf, sizeof(*req)); + + start_handle = sys_le16_to_cpu(req->start_handle); + end_handle = sys_le16_to_cpu(req->end_handle); + + if (!bt_uuid_create(&u.uuid, req->uuid, uuid_len)) { + return BT_ATT_ERR_UNLIKELY; + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x type %s", + start_handle, end_handle, bt_uuid_str(&u.uuid)); + + if (!range_is_valid(start_handle, end_handle, &err_handle)) { + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, err_handle, + BT_ATT_ERR_INVALID_HANDLE); + return 0; + } + + /* Core v4.2, Vol 3, sec 2.5.3 Attribute Grouping: + * Not all of the grouping attributes can be used in the ATT + * Read By Group Type Request. The "Primary Service" and "Secondary + * Service" grouping types may be used in the Read By Group Type + * Request. The "Characteristic" grouping type shall not be used in + * the ATT Read By Group Type Request. + */ + if (bt_uuid_cmp(&u.uuid, BT_UUID_GATT_PRIMARY) && + bt_uuid_cmp(&u.uuid, BT_UUID_GATT_SECONDARY)) { + send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, + BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE); + return 0; + } + + return att_read_group_rsp(att, &u.uuid, start_handle, end_handle); +} + +struct write_data { + struct bt_conn *conn; + struct net_buf *buf; + u8_t req; + const void *value; + u16_t len; + u16_t offset; + u8_t err; +}; + +static u8_t write_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct write_data *data = user_data; + int write; + u8_t flags = 0U; + + BT_DBG("handle 0x%04x offset %u", attr->handle, data->offset); + + /* Check attribute permissions */ + data->err = bt_gatt_check_perm(data->conn, attr, + BT_GATT_PERM_WRITE_MASK); + if (data->err) { + return BT_GATT_ITER_STOP; + } + + /* Set command flag if not a request */ + if (!data->req) { + flags |= BT_GATT_WRITE_FLAG_CMD; + } + + /* Write attribute value */ + write = attr->write(data->conn, attr, data->value, data->len, + data->offset, flags); + if (write < 0 || write != data->len) { + data->err = err_to_att(write); + return BT_GATT_ITER_STOP; + } + + data->err = 0U; + + return BT_GATT_ITER_CONTINUE; +} + +static u8_t att_write_rsp(struct bt_conn *conn, u8_t req, u8_t rsp, + u16_t handle, u16_t offset, const void *value, + u16_t len) +{ + struct write_data data; + + if (!bt_gatt_change_aware(conn, req ? true : false)) { + return BT_ATT_ERR_DB_OUT_OF_SYNC; + } + + if (!handle) { + return BT_ATT_ERR_INVALID_HANDLE; + } + + (void)memset(&data, 0, sizeof(data)); + + /* Only allocate buf if required to respond */ + if (rsp) { + data.buf = bt_att_create_pdu(conn, rsp, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + } + + data.conn = conn; + data.req = req; + data.offset = offset; + data.value = value; + data.len = len; + data.err = BT_ATT_ERR_INVALID_HANDLE; + + bt_gatt_foreach_attr(handle, handle, write_cb, &data); + + if (data.err) { + /* In case of error discard data and respond with an error */ + if (rsp) { + net_buf_unref(data.buf); + /* Respond here since handle is set */ + send_err_rsp(conn, req, handle, data.err); + } + return req == BT_ATT_OP_EXEC_WRITE_REQ ? data.err : 0; + } + + if (data.buf) { + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, + att_rsp_sent, NULL); + } + + return 0; +} + +static u8_t att_write_req(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + u16_t handle; + + handle = net_buf_pull_le16(buf); + + BT_DBG("handle 0x%04x", handle); + + return att_write_rsp(conn, BT_ATT_OP_WRITE_REQ, BT_ATT_OP_WRITE_RSP, + handle, 0, buf->data, buf->len); +} + +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 +struct prep_data { + struct bt_conn *conn; + struct net_buf *buf; + const void *value; + u16_t len; + u16_t offset; + u8_t err; +}; + +static u8_t prep_write_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct prep_data *data = user_data; + struct bt_attr_data *attr_data; + int write; + + BT_DBG("handle 0x%04x offset %u", attr->handle, data->offset); + + /* Check attribute permissions */ + data->err = bt_gatt_check_perm(data->conn, attr, + BT_GATT_PERM_WRITE_MASK); + if (data->err) { + return BT_GATT_ITER_STOP; + } + + /* Check if attribute requires handler to accept the data */ + if (!(attr->perm & BT_GATT_PERM_PREPARE_WRITE)) { + goto append; + } + + /* Write attribute value to check if device is authorized */ + write = attr->write(data->conn, attr, data->value, data->len, + data->offset, BT_GATT_WRITE_FLAG_PREPARE); + if (write != 0) { + data->err = err_to_att(write); + return BT_GATT_ITER_STOP; + } + +append: + /* Copy data into the outstanding queue */ + data->buf = net_buf_alloc(&prep_pool, K_NO_WAIT); + if (!data->buf) { + data->err = BT_ATT_ERR_PREPARE_QUEUE_FULL; + return BT_GATT_ITER_STOP; + } + + attr_data = net_buf_user_data(data->buf); + attr_data->handle = attr->handle; + attr_data->offset = data->offset; + + net_buf_add_mem(data->buf, data->value, data->len); + + data->err = 0U; + + return BT_GATT_ITER_CONTINUE; +} + +static u8_t att_prep_write_rsp(struct bt_att *att, u16_t handle, u16_t offset, + const void *value, u16_t len) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct prep_data data; + struct bt_att_prepare_write_rsp *rsp; + + if (!bt_gatt_change_aware(conn, true)) { + return BT_ATT_ERR_DB_OUT_OF_SYNC; + } + + if (!handle) { + return BT_ATT_ERR_INVALID_HANDLE; + } + + (void)memset(&data, 0, sizeof(data)); + + data.conn = conn; + data.offset = offset; + data.value = value; + data.len = len; + data.err = BT_ATT_ERR_INVALID_HANDLE; + + bt_gatt_foreach_attr(handle, handle, prep_write_cb, &data); + + if (data.err) { + /* Respond here since handle is set */ + send_err_rsp(conn, BT_ATT_OP_PREPARE_WRITE_REQ, handle, + data.err); + return 0; + } + + BT_DBG("buf %p handle 0x%04x offset %u", data.buf, handle, offset); + + /* Store buffer in the outstanding queue */ + net_buf_put(&att->prep_queue, data.buf); + + /* Generate response */ + data.buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_RSP, 0); + if (!data.buf) { + return BT_ATT_ERR_UNLIKELY; + } + + rsp = net_buf_add(data.buf, sizeof(*rsp)); + rsp->handle = sys_cpu_to_le16(handle); + rsp->offset = sys_cpu_to_le16(offset); + net_buf_add(data.buf, len); + memcpy(rsp->value, value, len); + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, data.buf, att_rsp_sent, + NULL); + + return 0; +} +#endif /* CONFIG_BT_ATT_PREPARE_COUNT */ + +static u8_t att_prepare_write_req(struct bt_att *att, struct net_buf *buf) +{ +#if CONFIG_BT_ATT_PREPARE_COUNT == 0 + return BT_ATT_ERR_NOT_SUPPORTED; +#else + struct bt_att_prepare_write_req *req; + u16_t handle, offset; + + req = net_buf_pull_mem(buf, sizeof(*req)); + + handle = sys_le16_to_cpu(req->handle); + offset = sys_le16_to_cpu(req->offset); + + BT_DBG("handle 0x%04x offset %u", handle, offset); + + return att_prep_write_rsp(att, handle, offset, buf->data, buf->len); +#endif /* CONFIG_BT_ATT_PREPARE_COUNT */ +} + +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 +static u8_t att_exec_write_rsp(struct bt_att *att, u8_t flags) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct net_buf *buf; + u8_t err = 0U; + + while ((buf = net_buf_get(&att->prep_queue, K_NO_WAIT))) { + struct bt_attr_data *data = net_buf_user_data(buf); + + BT_DBG("buf %p handle 0x%04x offset %u", buf, data->handle, + data->offset); + + /* Just discard the data if an error was set */ + if (!err && flags == BT_ATT_FLAG_EXEC) { + err = att_write_rsp(conn, BT_ATT_OP_EXEC_WRITE_REQ, 0, + data->handle, data->offset, + buf->data, buf->len); + if (err) { + /* Respond here since handle is set */ + send_err_rsp(conn, BT_ATT_OP_EXEC_WRITE_REQ, + data->handle, err); + } + } + + net_buf_unref(buf); + } + + if (err) { + return 0; + } + + /* Generate response */ + buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_RSP, 0); + if (!buf) { + return BT_ATT_ERR_UNLIKELY; + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, buf, att_rsp_sent, NULL); + + return 0; +} +#endif /* CONFIG_BT_ATT_PREPARE_COUNT */ + + +static u8_t att_exec_write_req(struct bt_att *att, struct net_buf *buf) +{ +#if CONFIG_BT_ATT_PREPARE_COUNT == 0 + return BT_ATT_ERR_NOT_SUPPORTED; +#else + struct bt_att_exec_write_req *req; + + req = (void *)buf->data; + + BT_DBG("flags 0x%02x", req->flags); + + return att_exec_write_rsp(att, req->flags); +#endif /* CONFIG_BT_ATT_PREPARE_COUNT */ +} + +static u8_t att_write_cmd(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + u16_t handle; + + handle = net_buf_pull_le16(buf); + + BT_DBG("handle 0x%04x", handle); + + return att_write_rsp(conn, 0, 0, handle, 0, buf->data, buf->len); +} + +#if defined(CONFIG_BT_SIGNING) +static u8_t att_signed_write_cmd(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + struct bt_att_signed_write_cmd *req; + u16_t handle; + int err; + + req = (void *)buf->data; + + handle = sys_le16_to_cpu(req->handle); + + BT_DBG("handle 0x%04x", handle); + + /* Verifying data requires full buffer including attribute header */ + net_buf_push(buf, sizeof(struct bt_att_hdr)); + err = bt_smp_sign_verify(conn, buf); + if (err) { + BT_ERR("Error verifying data"); + /* No response for this command */ + return 0; + } + + net_buf_pull(buf, sizeof(struct bt_att_hdr)); + net_buf_pull(buf, sizeof(*req)); + + return att_write_rsp(conn, 0, 0, handle, 0, buf->data, + buf->len - sizeof(struct bt_att_signature)); +} +#endif /* CONFIG_BT_SIGNING */ + +#if defined(CONFIG_BT_GATT_CLIENT) +#if defined(CONFIG_BT_SMP) +static int att_change_security(struct bt_conn *conn, u8_t err) +{ + bt_security_t sec; + + switch (err) { + case BT_ATT_ERR_INSUFFICIENT_ENCRYPTION: + if (conn->sec_level >= BT_SECURITY_L2) + return -EALREADY; + sec = BT_SECURITY_L2; + break; + case BT_ATT_ERR_AUTHENTICATION: + if (conn->sec_level < BT_SECURITY_L2) { + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C] + * page 375: + * + * If an LTK is not available, the service request + * shall be rejected with the error code 'Insufficient + * Authentication'. + * Note: When the link is not encrypted, the error code + * "Insufficient Authentication" does not indicate that + * MITM protection is required. + */ + sec = BT_SECURITY_L2; + } else if (conn->sec_level < BT_SECURITY_L3) { + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C] + * page 375: + * + * If an authenticated pairing is required but only an + * unauthenticated pairing has occurred and the link is + * currently encrypted, the service request shall be + * rejected with the error code 'Insufficient + * Authentication'. + * Note: When unauthenticated pairing has occurred and + * the link is currently encrypted, the error code + * 'Insufficient Authentication' indicates that MITM + * protection is required. + */ + sec = BT_SECURITY_L3; + } else if (conn->sec_level < BT_SECURITY_L4) { + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part C] + * page 375: + * + * If LE Secure Connections authenticated pairing is + * required but LE legacy pairing has occurred and the + * link is currently encrypted, the service request + * shall be rejected with the error code ''Insufficient + * Authentication'. + */ + sec = BT_SECURITY_L4; + } else { + return -EALREADY; + } + break; + default: + return -EINVAL; + } + + return bt_conn_set_security(conn, sec); +} +#endif /* CONFIG_BT_SMP */ + +static u8_t att_error_rsp(struct bt_att *att, struct net_buf *buf) +{ + struct bt_att_error_rsp *rsp; + u8_t err; + + rsp = (void *)buf->data; + + BT_DBG("request 0x%02x handle 0x%04x error 0x%02x", rsp->request, + sys_le16_to_cpu(rsp->handle), rsp->error); + + /* Don't retry if there is no req pending or it has been cancelled */ + if (!att->req || att->req == &cancel) { + err = BT_ATT_ERR_UNLIKELY; + goto done; + } + + if (att->req->buf) { + /* Restore state to be resent */ + net_buf_simple_restore(&att->req->buf->b, &att->req->state); + } + + err = rsp->error; +#if defined(CONFIG_BT_SMP) + if (att->req->retrying) { + goto done; + } + + /* Check if security needs to be changed */ + if (!att_change_security(att->chan.chan.conn, err)) { + att->req->retrying = true; + /* Wait security_changed: TODO: Handle fail case */ + return 0; + } +#endif /* CONFIG_BT_SMP */ + +done: + return att_handle_rsp(att, NULL, 0, err); +} + +static u8_t att_handle_find_info_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_find_type_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_read_type_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_read_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_read_blob_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +#if defined(CONFIG_BT_GATT_READ_MULTIPLE) +static u8_t att_handle_read_mult_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} +#endif /* CONFIG_BT_GATT_READ_MULTIPLE */ + +static u8_t att_handle_read_group_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_write_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_prepare_write_rsp(struct bt_att *att, + struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_handle_exec_write_rsp(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static u8_t att_notify(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + u16_t handle; + + handle = net_buf_pull_le16(buf); + BT_DBG("handle 0x%04x", handle); + + bt_gatt_notification(conn, handle, buf->data, buf->len); + return 0; +} + +static u8_t att_indicate(struct bt_att *att, struct net_buf *buf) +{ + struct bt_conn *conn = att->chan.chan.conn; + u16_t handle; + + handle = net_buf_pull_le16(buf); + + BT_DBG("handle 0x%04x", handle); + + bt_gatt_notification(conn, handle, buf->data, buf->len); + + buf = bt_att_create_pdu(conn, BT_ATT_OP_CONFIRM, 0); + if (!buf) { + return 0; + } + + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, buf, att_cfm_sent, NULL); + + return 0; +} +#endif /* CONFIG_BT_GATT_CLIENT */ + +static u8_t att_confirm(struct bt_att *att, struct net_buf *buf) +{ + BT_DBG(""); + + return att_handle_rsp(att, buf->data, buf->len, 0); +} + +static const struct att_handler { + u8_t op; + u8_t expect_len; + att_type_t type; + u8_t (*func)(struct bt_att *att, struct net_buf *buf); +} handlers[] = { + { BT_ATT_OP_MTU_REQ, + sizeof(struct bt_att_exchange_mtu_req), + ATT_REQUEST, + att_mtu_req }, + { BT_ATT_OP_FIND_INFO_REQ, + sizeof(struct bt_att_find_info_req), + ATT_REQUEST, + att_find_info_req }, + { BT_ATT_OP_FIND_TYPE_REQ, + sizeof(struct bt_att_find_type_req), + ATT_REQUEST, + att_find_type_req }, + { BT_ATT_OP_READ_TYPE_REQ, + sizeof(struct bt_att_read_type_req), + ATT_REQUEST, + att_read_type_req }, + { BT_ATT_OP_READ_REQ, + sizeof(struct bt_att_read_req), + ATT_REQUEST, + att_read_req }, + { BT_ATT_OP_READ_BLOB_REQ, + sizeof(struct bt_att_read_blob_req), + ATT_REQUEST, + att_read_blob_req }, +#if defined(CONFIG_BT_GATT_READ_MULTIPLE) + { BT_ATT_OP_READ_MULT_REQ, + BT_ATT_READ_MULT_MIN_LEN_REQ, + ATT_REQUEST, + att_read_mult_req }, +#endif /* CONFIG_BT_GATT_READ_MULTIPLE */ + { BT_ATT_OP_READ_GROUP_REQ, + sizeof(struct bt_att_read_group_req), + ATT_REQUEST, + att_read_group_req }, + { BT_ATT_OP_WRITE_REQ, + sizeof(struct bt_att_write_req), + ATT_REQUEST, + att_write_req }, + { BT_ATT_OP_PREPARE_WRITE_REQ, + sizeof(struct bt_att_prepare_write_req), + ATT_REQUEST, + att_prepare_write_req }, + { BT_ATT_OP_EXEC_WRITE_REQ, + sizeof(struct bt_att_exec_write_req), + ATT_REQUEST, + att_exec_write_req }, + { BT_ATT_OP_CONFIRM, + 0, + ATT_CONFIRMATION, + att_confirm }, + { BT_ATT_OP_WRITE_CMD, + sizeof(struct bt_att_write_cmd), + ATT_COMMAND, + att_write_cmd }, +#if defined(CONFIG_BT_SIGNING) + { BT_ATT_OP_SIGNED_WRITE_CMD, + (sizeof(struct bt_att_write_cmd) + + sizeof(struct bt_att_signature)), + ATT_COMMAND, + att_signed_write_cmd }, +#endif /* CONFIG_BT_SIGNING */ +#if defined(CONFIG_BT_GATT_CLIENT) + { BT_ATT_OP_ERROR_RSP, + sizeof(struct bt_att_error_rsp), + ATT_RESPONSE, + att_error_rsp }, + { BT_ATT_OP_MTU_RSP, + sizeof(struct bt_att_exchange_mtu_rsp), + ATT_RESPONSE, + att_mtu_rsp }, + { BT_ATT_OP_FIND_INFO_RSP, + sizeof(struct bt_att_find_info_rsp), + ATT_RESPONSE, + att_handle_find_info_rsp }, + { BT_ATT_OP_FIND_TYPE_RSP, + sizeof(struct bt_att_find_type_rsp), + ATT_RESPONSE, + att_handle_find_type_rsp }, + { BT_ATT_OP_READ_TYPE_RSP, + sizeof(struct bt_att_read_type_rsp), + ATT_RESPONSE, + att_handle_read_type_rsp }, + { BT_ATT_OP_READ_RSP, + sizeof(struct bt_att_read_rsp), + ATT_RESPONSE, + att_handle_read_rsp }, + { BT_ATT_OP_READ_BLOB_RSP, + sizeof(struct bt_att_read_blob_rsp), + ATT_RESPONSE, + att_handle_read_blob_rsp }, +#if defined(CONFIG_BT_GATT_READ_MULTIPLE) + { BT_ATT_OP_READ_MULT_RSP, + sizeof(struct bt_att_read_mult_rsp), + ATT_RESPONSE, + att_handle_read_mult_rsp }, +#endif /* CONFIG_BT_GATT_READ_MULTIPLE */ + { BT_ATT_OP_READ_GROUP_RSP, + sizeof(struct bt_att_read_group_rsp), + ATT_RESPONSE, + att_handle_read_group_rsp }, + { BT_ATT_OP_WRITE_RSP, + 0, + ATT_RESPONSE, + att_handle_write_rsp }, + { BT_ATT_OP_PREPARE_WRITE_RSP, + sizeof(struct bt_att_prepare_write_rsp), + ATT_RESPONSE, + att_handle_prepare_write_rsp }, + { BT_ATT_OP_EXEC_WRITE_RSP, + 0, + ATT_RESPONSE, + att_handle_exec_write_rsp }, + { BT_ATT_OP_NOTIFY, + sizeof(struct bt_att_notify), + ATT_NOTIFICATION, + att_notify }, + { BT_ATT_OP_INDICATE, + sizeof(struct bt_att_indicate), + ATT_INDICATION, + att_indicate }, +#endif /* CONFIG_BT_GATT_CLIENT */ +}; + +static att_type_t att_op_get_type(u8_t op) +{ + switch (op) { + case BT_ATT_OP_MTU_REQ: + case BT_ATT_OP_FIND_INFO_REQ: + case BT_ATT_OP_FIND_TYPE_REQ: + case BT_ATT_OP_READ_TYPE_REQ: + case BT_ATT_OP_READ_REQ: + case BT_ATT_OP_READ_BLOB_REQ: + case BT_ATT_OP_READ_MULT_REQ: + case BT_ATT_OP_READ_GROUP_REQ: + case BT_ATT_OP_WRITE_REQ: + case BT_ATT_OP_PREPARE_WRITE_REQ: + case BT_ATT_OP_EXEC_WRITE_REQ: + return ATT_REQUEST; + case BT_ATT_OP_CONFIRM: + return ATT_CONFIRMATION; + case BT_ATT_OP_WRITE_CMD: + case BT_ATT_OP_SIGNED_WRITE_CMD: + return ATT_COMMAND; + case BT_ATT_OP_ERROR_RSP: + case BT_ATT_OP_MTU_RSP: + case BT_ATT_OP_FIND_INFO_RSP: + case BT_ATT_OP_FIND_TYPE_RSP: + case BT_ATT_OP_READ_TYPE_RSP: + case BT_ATT_OP_READ_RSP: + case BT_ATT_OP_READ_BLOB_RSP: + case BT_ATT_OP_READ_MULT_RSP: + case BT_ATT_OP_READ_GROUP_RSP: + case BT_ATT_OP_WRITE_RSP: + case BT_ATT_OP_PREPARE_WRITE_RSP: + case BT_ATT_OP_EXEC_WRITE_RSP: + return ATT_RESPONSE; + case BT_ATT_OP_NOTIFY: + return ATT_NOTIFICATION; + case BT_ATT_OP_INDICATE: + return ATT_INDICATION; + } + + if (op & ATT_CMD_MASK) { + return ATT_COMMAND; + } + + return ATT_UNKNOWN; +} + +static int bt_att_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_att *att = ATT_CHAN(chan); + struct bt_att_hdr *hdr; + const struct att_handler *handler; + u8_t err; + size_t i; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small ATT PDU received"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_DBG("Received ATT code 0x%02x len %u", hdr->code, buf->len); + + for (i = 0, handler = NULL; i < ARRAY_SIZE(handlers); i++) { + if (hdr->code == handlers[i].op) { + handler = &handlers[i]; + break; + } + } + + if (!handler) { + BT_WARN("Unhandled ATT code 0x%02x", hdr->code); + if (att_op_get_type(hdr->code) != ATT_COMMAND) { + send_err_rsp(chan->conn, hdr->code, 0, + BT_ATT_ERR_NOT_SUPPORTED); + } + return 0; + } + + if (IS_ENABLED(CONFIG_BT_ATT_ENFORCE_FLOW)) { + if (handler->type == ATT_REQUEST && + atomic_test_and_set_bit(att->flags, ATT_PENDING_RSP)) { + BT_WARN("Ignoring unexpected request"); + return 0; + } else if (handler->type == ATT_INDICATION && + atomic_test_and_set_bit(att->flags, + ATT_PENDING_CFM)) { + BT_WARN("Ignoring unexpected indication"); + return 0; + } + } + + if (buf->len < handler->expect_len) { + BT_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code); + err = BT_ATT_ERR_INVALID_PDU; + } else { + err = handler->func(att, buf); + } + + if (handler->type == ATT_REQUEST && err) { + BT_DBG("ATT error 0x%02x", err); + send_err_rsp(chan->conn, hdr->code, 0, err); + } + + return 0; +} + +static struct bt_att *att_chan_get(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan; + struct bt_att *att; + + if (conn->state != BT_CONN_CONNECTED) { + BT_WARN("Not connected"); + return NULL; + } + + chan = bt_l2cap_le_lookup_rx_cid(conn, BT_L2CAP_CID_ATT); + if (!chan) { + BT_ERR("Unable to find ATT channel"); + return NULL; + } + + att = ATT_CHAN(chan); + if (atomic_test_bit(att->flags, ATT_DISCONNECTED)) { + BT_WARN("ATT context flagged as disconnected"); + return NULL; + } + + return att; +} + +struct net_buf *bt_att_create_pdu(struct bt_conn *conn, u8_t op, size_t len) +{ + struct bt_att_hdr *hdr; + struct net_buf *buf; + struct bt_att *att; + + att = att_chan_get(conn); + if (!att) { + return NULL; + } + + if (len + sizeof(op) > att->chan.tx.mtu) { + BT_WARN("ATT MTU exceeded, max %u, wanted %zu", + att->chan.tx.mtu, len + sizeof(op)); + return NULL; + } + + switch (att_op_get_type(op)) { + case ATT_RESPONSE: + case ATT_CONFIRMATION: + /* Use a timeout only when responding/confirming */ + buf = bt_l2cap_create_pdu_timeout(NULL, 0, ATT_TIMEOUT); + break; + default: + buf = bt_l2cap_create_pdu(NULL, 0); + } + + if (!buf) { + BT_ERR("Unable to allocate buffer for op 0x%02x", op); + return NULL; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = op; + + return buf; +} + +static void att_reset(struct bt_att *att) +{ + struct bt_att_req *req, *tmp; + int i; + struct net_buf *buf; + +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 + /* Discard queued buffers */ + while ((buf = k_fifo_get(&att->prep_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } +#endif /* CONFIG_BT_ATT_PREPARE_COUNT > 0 */ + + while ((buf = k_fifo_get(&att->tx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + atomic_set_bit(att->flags, ATT_DISCONNECTED); + + /* Ensure that any waiters are woken up */ + for (i = 0; i < CONFIG_BT_ATT_TX_MAX; i++) { + k_sem_give(&att->tx_sem); + } + + /* Notify pending requests */ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&att->reqs, req, tmp, node) { + if (req->func) { + req->func(NULL, BT_ATT_ERR_UNLIKELY, NULL, 0, req); + } + + att_req_destroy(req); + } + + /* Reset list */ + sys_slist_init(&att->reqs); + + if (!att->req) { + return; + } + + /* Notify outstanding request */ + att_handle_rsp(att, NULL, 0, BT_ATT_ERR_UNLIKELY); +} + +static void att_timeout(struct k_work *work) +{ + struct bt_att *att = CONTAINER_OF(work, struct bt_att, timeout_work); + struct bt_l2cap_le_chan *ch = &att->chan; + + BT_ERR("ATT Timeout"); + + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part F] page 480: + * + * A transaction not completed within 30 seconds shall time out. Such a + * transaction shall be considered to have failed and the local higher + * layers shall be informed of this failure. No more attribute protocol + * requests, commands, indications or notifications shall be sent to the + * target device on this ATT Bearer. + */ + att_reset(att); + + /* Consider the channel disconnected */ + bt_gatt_disconnected(ch->chan.conn); + ch->chan.conn = NULL; +} + +static void bt_att_connected(struct bt_l2cap_chan *chan) +{ + struct bt_att *att = ATT_CHAN(chan); + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + + BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid); + + k_fifo_init(&att->tx_queue, 20); +#if CONFIG_BT_ATT_PREPARE_COUNT > 0 + k_fifo_init(&att->prep_queue, 20); +#endif + + ch->tx.mtu = BT_ATT_DEFAULT_LE_MTU; + ch->rx.mtu = BT_ATT_DEFAULT_LE_MTU; + + k_delayed_work_init(&att->timeout_work, att_timeout); + sys_slist_init(&att->reqs); +} + +static void bt_att_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_att *att = ATT_CHAN(chan); + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + + BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid); + + att_reset(att); + + bt_gatt_disconnected(ch->chan.conn); + + #ifdef BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS + if(att->timeout_work.timer.timer.hdl) + k_delayed_work_del_timer(&att->timeout_work); + + if(att->tx_queue._queue.hdl){ + k_queue_free(&att->tx_queue._queue); + att->tx_queue._queue.hdl = NULL; + } + + if(att->tx_sem.sem.hdl) + k_sem_delete(&att->tx_sem); + #endif +} + +#if defined(CONFIG_BT_SMP) +static void bt_att_encrypt_change(struct bt_l2cap_chan *chan, + u8_t hci_status) +{ + struct bt_att *att = ATT_CHAN(chan); + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + struct bt_conn *conn = ch->chan.conn; + + BT_DBG("chan %p conn %p handle %u sec_level 0x%02x status 0x%02x", ch, + conn, conn->handle, conn->sec_level, hci_status); + + /* + * If status (HCI status of security procedure) is non-zero, notify + * outstanding request about security failure. + */ + if (hci_status) { + att_handle_rsp(att, NULL, 0, BT_ATT_ERR_AUTHENTICATION); + return; + } + + bt_gatt_encrypt_change(conn); + + if (conn->sec_level == BT_SECURITY_L1) { + return; + } + + if (!att->req || !att->req->retrying) { + return; + } + + k_sem_take(&att->tx_sem, K_FOREVER); + if (!att_is_connected(att)) { + BT_WARN("Disconnected"); + k_sem_give(&att->tx_sem); + return; + } + + BT_DBG("Retrying"); + + /* Resend buffer */ + (void)bt_l2cap_send_cb(conn, BT_L2CAP_CID_ATT, att->req->buf, + att_cb(att->req->buf), NULL); + att->req->buf = NULL; +} +#endif /* CONFIG_BT_SMP */ + +#if defined(BFLB_BLE_MTU_CHANGE_CB) +void bt_att_mtu_changed(struct bt_l2cap_chan *chan, u16_t mtu) +{ + bt_gatt_mtu_changed(chan->conn, mtu); +} +#endif + +static int bt_att_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + int i; + static struct bt_l2cap_chan_ops ops = { + .connected = bt_att_connected, + .disconnected = bt_att_disconnected, + .recv = bt_att_recv, +#if defined(CONFIG_BT_SMP) + .encrypt_change = bt_att_encrypt_change, +#endif /* CONFIG_BT_SMP */ +#if defined(BFLB_BLE_MTU_CHANGE_CB) + .mtu_changed = bt_att_mtu_changed, +#endif + }; + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_req_pool); i++) { + struct bt_att *att = &bt_req_pool[i]; + + if (att->chan.chan.conn) { + continue; + } + + (void)memset(att, 0, sizeof(*att)); + att->chan.chan.ops = &ops; + k_sem_init(&att->tx_sem, CONFIG_BT_ATT_TX_MAX, + CONFIG_BT_ATT_TX_MAX); + + *chan = &att->chan.chan; + + return 0; + } + + BT_ERR("No available ATT context for conn %p", conn); + + return -ENOMEM; +} + +BT_L2CAP_CHANNEL_DEFINE(att_fixed_chan, BT_L2CAP_CID_ATT, bt_att_accept); + +void bt_att_init(void) +{ + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + static struct bt_l2cap_fixed_chan chan = { + .cid = BT_L2CAP_CID_ATT, + .accept = bt_att_accept, + }; + + bt_l2cap_le_fixed_chan_register(&chan); + #endif + + #if CONFIG_BT_ATT_PREPARE_COUNT > 0 + #if defined(BFLB_DYNAMIC_ALLOC_MEM) + k_lifo_init(&prep_pool.free, CONFIG_BT_ATT_PREPARE_COUNT); + net_buf_init(&prep_pool, CONFIG_BT_ATT_PREPARE_COUNT, BT_ATT_MTU, NULL); + #endif + #endif + + bt_gatt_init(); +} + +u16_t bt_att_get_mtu(struct bt_conn *conn) +{ + struct bt_att *att; + + att = att_chan_get(conn); + if (!att) { + return 0; + } + + /* tx and rx MTU shall be symmetric */ + return att->chan.tx.mtu; +} + +int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_conn_tx_cb_t cb, + void *user_data) +{ + struct bt_att *att; + int err; + + __ASSERT_NO_MSG(conn); + __ASSERT_NO_MSG(buf); + + att = att_chan_get(conn); + if (!att) { + net_buf_unref(buf); + return -ENOTCONN; + } + + /* Don't use tx_sem if caller has set it own callback */ + if (!cb) { + /* Queue buffer to be send later */ + if (k_sem_take(&att->tx_sem, K_NO_WAIT) < 0) { + k_fifo_put(&att->tx_queue, buf); + return 0; + } + } + + err = att_send(conn, buf, cb, user_data); + if (err) { + if (!cb) { + k_sem_give(&att->tx_sem); + } + return err; + } + + return 0; +} + +int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req) +{ + struct bt_att *att; + + BT_DBG("conn %p req %p", conn, req); + + __ASSERT_NO_MSG(conn); + __ASSERT_NO_MSG(req); + + att = att_chan_get(conn); + if (!att) { + net_buf_unref(req->buf); + req->buf = NULL; + return -ENOTCONN; + } + + /* Check if there is a request outstanding */ + if (att->req) { + /* Queue the request to be send later */ + sys_slist_append(&att->reqs, &req->node); + return 0; + } + + return att_send_req(att, req); +} + +void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req) +{ + struct bt_att *att; + + BT_DBG("req %p", req); + + if (!conn || !req) { + return; + } + + att = att_chan_get(conn); + if (!att) { + return; + } + + /* Check if request is outstanding */ + if (att->req == req) { + att->req = &cancel; + } else { + /* Remove request from the list */ + sys_slist_find_and_remove(&att->reqs, &req->node); + } + + att_req_destroy(req); +} diff --git a/components/ble/ble_stack/host/att_internal.h b/components/ble/ble_stack/host/att_internal.h new file mode 100644 index 00000000..7f8e089c --- /dev/null +++ b/components/ble/ble_stack/host/att_internal.h @@ -0,0 +1,265 @@ +/* att_internal.h - Attribute protocol handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_ATT_DEFAULT_LE_MTU 23 + +#if BT_L2CAP_RX_MTU < CONFIG_BT_L2CAP_TX_MTU +#define BT_ATT_MTU BT_L2CAP_RX_MTU +#else +#define BT_ATT_MTU CONFIG_BT_L2CAP_TX_MTU +#endif + +struct bt_att_hdr { + u8_t code; +} __packed; + +#define BT_ATT_OP_ERROR_RSP 0x01 +struct bt_att_error_rsp { + u8_t request; + u16_t handle; + u8_t error; +} __packed; + +#define BT_ATT_OP_MTU_REQ 0x02 +struct bt_att_exchange_mtu_req { + u16_t mtu; +} __packed; + +#define BT_ATT_OP_MTU_RSP 0x03 +struct bt_att_exchange_mtu_rsp { + u16_t mtu; +} __packed; + +/* Find Information Request */ +#define BT_ATT_OP_FIND_INFO_REQ 0x04 +struct bt_att_find_info_req { + u16_t start_handle; + u16_t end_handle; +} __packed; + +/* Format field values for BT_ATT_OP_FIND_INFO_RSP */ +#define BT_ATT_INFO_16 0x01 +#define BT_ATT_INFO_128 0x02 + +struct bt_att_info_16 { + u16_t handle; + u16_t uuid; +} __packed; + +struct bt_att_info_128 { + u16_t handle; + u8_t uuid[16]; +} __packed; + +/* Find Information Response */ +#define BT_ATT_OP_FIND_INFO_RSP 0x05 +struct bt_att_find_info_rsp { + u8_t format; + u8_t info[0]; +} __packed; + +/* Find By Type Value Request */ +#define BT_ATT_OP_FIND_TYPE_REQ 0x06 +struct bt_att_find_type_req { + u16_t start_handle; + u16_t end_handle; + u16_t type; + u8_t value[0]; +} __packed; + +struct bt_att_handle_group { + u16_t start_handle; + u16_t end_handle; +} __packed; + +/* Find By Type Value Response */ +#define BT_ATT_OP_FIND_TYPE_RSP 0x07 +struct bt_att_find_type_rsp { + struct bt_att_handle_group list[0]; +} __packed; + +/* Read By Type Request */ +#define BT_ATT_OP_READ_TYPE_REQ 0x08 +struct bt_att_read_type_req { + u16_t start_handle; + u16_t end_handle; + u8_t uuid[0]; +} __packed; + +struct bt_att_data { + u16_t handle; + u8_t value[0]; +} __packed; + +/* Read By Type Response */ +#define BT_ATT_OP_READ_TYPE_RSP 0x09 +struct bt_att_read_type_rsp { + u8_t len; + struct bt_att_data data[0]; +} __packed; + +/* Read Request */ +#define BT_ATT_OP_READ_REQ 0x0a +struct bt_att_read_req { + u16_t handle; +} __packed; + +/* Read Response */ +#define BT_ATT_OP_READ_RSP 0x0b +struct bt_att_read_rsp { + u8_t value[0]; +} __packed; + +/* Read Blob Request */ +#define BT_ATT_OP_READ_BLOB_REQ 0x0c +struct bt_att_read_blob_req { + u16_t handle; + u16_t offset; +} __packed; + +/* Read Blob Response */ +#define BT_ATT_OP_READ_BLOB_RSP 0x0d +struct bt_att_read_blob_rsp { + u8_t value[0]; +} __packed; + +/* Read Multiple Request */ +#define BT_ATT_READ_MULT_MIN_LEN_REQ 0x04 + +#define BT_ATT_OP_READ_MULT_REQ 0x0e +struct bt_att_read_mult_req { + u16_t handles[0]; +} __packed; + +/* Read Multiple Respose */ +#define BT_ATT_OP_READ_MULT_RSP 0x0f +struct bt_att_read_mult_rsp { + u8_t value[0]; +} __packed; + +/* Read by Group Type Request */ +#define BT_ATT_OP_READ_GROUP_REQ 0x10 +struct bt_att_read_group_req { + u16_t start_handle; + u16_t end_handle; + u8_t uuid[0]; +} __packed; + +struct bt_att_group_data { + u16_t start_handle; + u16_t end_handle; + u8_t value[0]; +} __packed; + +/* Read by Group Type Response */ +#define BT_ATT_OP_READ_GROUP_RSP 0x11 +struct bt_att_read_group_rsp { + u8_t len; + struct bt_att_group_data data[0]; +} __packed; + +/* Write Request */ +#define BT_ATT_OP_WRITE_REQ 0x12 +struct bt_att_write_req { + u16_t handle; + u8_t value[0]; +} __packed; + +/* Write Response */ +#define BT_ATT_OP_WRITE_RSP 0x13 + +/* Prepare Write Request */ +#define BT_ATT_OP_PREPARE_WRITE_REQ 0x16 +struct bt_att_prepare_write_req { + u16_t handle; + u16_t offset; + u8_t value[0]; +} __packed; + +/* Prepare Write Respond */ +#define BT_ATT_OP_PREPARE_WRITE_RSP 0x17 +struct bt_att_prepare_write_rsp { + u16_t handle; + u16_t offset; + u8_t value[0]; +} __packed; + +/* Execute Write Request */ +#define BT_ATT_FLAG_CANCEL 0x00 +#define BT_ATT_FLAG_EXEC 0x01 + +#define BT_ATT_OP_EXEC_WRITE_REQ 0x18 +struct bt_att_exec_write_req { + u8_t flags; +} __packed; + +/* Execute Write Response */ +#define BT_ATT_OP_EXEC_WRITE_RSP 0x19 + +/* Handle Value Notification */ +#define BT_ATT_OP_NOTIFY 0x1b +struct bt_att_notify { + u16_t handle; + u8_t value[0]; +} __packed; + +/* Handle Value Indication */ +#define BT_ATT_OP_INDICATE 0x1d +struct bt_att_indicate { + u16_t handle; + u8_t value[0]; +} __packed; + +/* Handle Value Confirm */ +#define BT_ATT_OP_CONFIRM 0x1e + +struct bt_att_signature { + u8_t value[12]; +} __packed; + +/* Write Command */ +#define BT_ATT_OP_WRITE_CMD 0x52 +struct bt_att_write_cmd { + u16_t handle; + u8_t value[0]; +} __packed; + +/* Signed Write Command */ +#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2 +struct bt_att_signed_write_cmd { + u16_t handle; + u8_t value[0]; +} __packed; + +void att_pdu_sent(struct bt_conn *conn, void *user_data); + +void att_cfm_sent(struct bt_conn *conn, void *user_data); + +void att_rsp_sent(struct bt_conn *conn, void *user_data); + +void att_req_sent(struct bt_conn *conn, void *user_data); + +void bt_att_init(void); +u16_t bt_att_get_mtu(struct bt_conn *conn); + +#if defined(CONFIG_BLE_AT_CMD) +void set_mtu_size(u16_t size); +#endif + +struct net_buf *bt_att_create_pdu(struct bt_conn *conn, u8_t op, + size_t len); + +/* Send ATT PDU over a connection */ +int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_conn_tx_cb_t cb, + void *user_data); + +/* Send ATT Request over a connection */ +int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req); + +/* Cancel ATT request */ +void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req); diff --git a/components/ble/ble_stack/host/avdtp.c b/components/ble/ble_stack/host/avdtp.c new file mode 100644 index 00000000..6a3c6f40 --- /dev/null +++ b/components/ble/ble_stack/host/avdtp.c @@ -0,0 +1,739 @@ +/* + * Audio Video Distribution Protocol + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_AVDTP) +#define LOG_MODULE_NAME bt_avdtp +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "avdtp_internal.h" +#include "a2dp-codec.h" + +#define AVDTP_MSG_POISTION 0x00 +#define AVDTP_PKT_POSITION 0x02 +#define AVDTP_TID_POSITION 0x04 +#define AVDTP_SIGID_MASK 0x3f + +#define AVDTP_GET_TR_ID(hdr) ((hdr & 0xf0) >> AVDTP_TID_POSITION) +#define AVDTP_GET_MSG_TYPE(hdr) (hdr & 0x03) +#define AVDTP_GET_PKT_TYPE(hdr) ((hdr & 0x0c) >> AVDTP_PKT_POSITION) +#define AVDTP_GET_SIG_ID(s) (s & AVDTP_SIGID_MASK) + +static struct bt_avdtp_event_cb *event_cb; + +static struct bt_avdtp_seid_lsep *lseps; + +extern struct bt_a2dp_codec_sbc_params sbc_info; + +#define AVDTP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_avdtp, br_chan.chan) + +#define AVDTP_KWORK(_work) CONTAINER_OF(_work, struct bt_avdtp_req,\ + timeout_work) + +#define AVDTP_TIMEOUT K_SECONDS(6) + + +static void handle_avdtp_discover_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_get_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_set_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_get_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_reconf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_open_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_start_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_close_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_suspend_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_abort_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_sec_ctrl_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_get_all_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); +static void handle_avdtp_dly_rpt_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type); + +static const struct { + uint8_t sig_id; + void (*func)(struct bt_avdtp *session, struct net_buf *buf, + uint8_t msg_type); +} handler[] = { + {BT_AVDTP_DISCOVER, handle_avdtp_discover_cmd}, + {BT_AVDTP_GET_CAPABILITIES, handle_avdtp_get_cap_cmd}, + {BT_AVDTP_SET_CONFIGURATION, handle_avdtp_set_conf_cmd}, + {BT_AVDTP_GET_CONFIGURATION, handle_avdtp_get_conf_cmd}, + {BT_AVDTP_RECONFIGURE, handle_avdtp_reconf_cmd}, + {BT_AVDTP_OPEN, handle_avdtp_open_cmd}, + {BT_AVDTP_START, handle_avdtp_start_cmd}, + {BT_AVDTP_CLOSE, handle_avdtp_close_cmd}, + {BT_AVDTP_SUSPEND, handle_avdtp_suspend_cmd}, + {BT_AVDTP_ABORT, handle_avdtp_abort_cmd}, + {BT_AVDTP_SECURITY_CONTROL, handle_avdtp_sec_ctrl_cmd}, + {BT_AVDTP_GET_ALL_CAPABILITIES, handle_avdtp_get_all_cap_cmd}, + {BT_AVDTP_DELAYREPORT, handle_avdtp_dly_rpt_cmd}, +}; + +static int avdtp_send(struct bt_avdtp *session, + struct net_buf *buf, struct bt_avdtp_req *req) +{ + int result; + struct bt_avdtp_single_sig_hdr *hdr; + + hdr = (struct bt_avdtp_single_sig_hdr *)buf->data; + + result = bt_l2cap_chan_send(&session->br_chan.chan, buf); + if (result < 0) { + BT_ERR("Error:L2CAP send fail - result = %d", result); + return result; + } + + /*Save the sent request*/ + req->sig = AVDTP_GET_SIG_ID(hdr->signal_id); + req->tid = AVDTP_GET_TR_ID(hdr->hdr); + BT_DBG("sig 0x%02X, tid 0x%02X", req->sig, req->tid); + + session->req = req; + /* Start timeout work */ + k_delayed_work_submit(&session->req->timeout_work, AVDTP_TIMEOUT); + return result; +} + +static struct net_buf *avdtp_create_pdu(uint8_t msg_type, + uint8_t pkt_type, + uint8_t sig_id) +{ + struct net_buf *buf; + static uint8_t tid; + struct bt_avdtp_single_sig_hdr *hdr; + + BT_DBG(""); + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + + hdr->hdr = (msg_type | pkt_type << AVDTP_PKT_POSITION | + tid++ << AVDTP_TID_POSITION); + tid %= 16; /* Loop for 16*/ + hdr->signal_id = sig_id & AVDTP_SIGID_MASK; + + BT_DBG("hdr = 0x%02X, Signal_ID = 0x%02X", hdr->hdr, hdr->signal_id); + return buf; +} + +/* Timeout handler */ +static void avdtp_timeout(struct k_work *work) +{ + BT_DBG("Failed Signal_id = %d", (AVDTP_KWORK(work))->sig); + + /* Gracefully Disconnect the Signalling and streaming L2cap chann*/ + +} + +/** +* @brief avdtp_parsing_capability : parsing avdtp capability content +*/ +static int avdtp_parsing_capability(struct net_buf *buf) +{ + BT_DBG(" "); + while (buf->len) + { + uint8_t svc_cat = net_buf_pull_u8(buf); + uint8_t cat_len = net_buf_pull_u8(buf);; + switch (svc_cat) + { + case BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT: + + break; + + case BT_AVDTP_SERVICE_CAT_REPORTING: + + break; + + case BT_AVDTP_SERVICE_CAT_RECOVERY: + + break; + + case BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION: + + break; + + case BT_AVDTP_SERVICE_CAT_HDR_COMPRESSION: + + break; + + case BT_AVDTP_SERVICE_CAT_MULTIPLEXING: + + break; + + case BT_AVDTP_SERVICE_CAT_MEDIA_CODEC: + + break; + + case BT_AVDTP_SERVICE_CAT_DELAYREPORTING: + + break; + + default: + break; + } + + } + + return 0; +} + +static void handle_avdtp_discover_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_DISCOVER); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + struct bt_avdtp_seid_lsep *disc_sep = lseps; + uint8_t acp_endpoint[2]; + while(disc_sep) + { + acp_endpoint[0] = disc_sep->sep.id << 2 | disc_sep->sep.inuse << 1 |disc_sep->sep.rfa0; + acp_endpoint[1] = disc_sep->sep.media_type << 4 | disc_sep->sep.tsep << 3 |disc_sep->sep.rfa1; + memcpy(rsp_buf->data + rsp_buf->len, acp_endpoint, 2); + rsp_buf->len += 2; + disc_sep = disc_sep->next; + } + +#if 0 + BT_DBG("rsp_buf len: %d \n", rsp_buf->len); + for(int i = 0; i < rsp_buf->len; i++) + { + BT_WARN("0x%02x, ", rsp_buf->data[i]); + } + BT_WARN("\n"); +#endif + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_get_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_GET_CAPABILITIES); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + uint8_t svc_cat_1[2]; + svc_cat_1[0] = BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT; + svc_cat_1[1] = 0; + memcpy(rsp_buf->data + rsp_buf->len, svc_cat_1, 2); + rsp_buf->len += 2; + + uint8_t svc_cat_2[8]; + svc_cat_2[0] = BT_AVDTP_SERVICE_CAT_MEDIA_CODEC; + svc_cat_2[1] = 6; + svc_cat_2[2] = BT_A2DP_AUDIO; + svc_cat_2[3] = BT_A2DP_CODEC_TYPE_SBC; + svc_cat_2[4] = sbc_info.config[0]; + svc_cat_2[5] = sbc_info.config[1]; + svc_cat_2[6] = sbc_info.min_bitpool; + svc_cat_2[7] = sbc_info.max_bitpool; + memcpy(rsp_buf->data + rsp_buf->len, svc_cat_2, 8); + rsp_buf->len += 8; + + uint8_t svc_cat_3[4]; + svc_cat_3[0] = BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION; + svc_cat_3[1] = 2; + svc_cat_3[2] = BT_AVDTP_CONTENT_PROTECTION_LSB_SCMS_T; + svc_cat_3[3] = BT_AVDTP_CONTENT_PROTECTION_MSB; + memcpy(rsp_buf->data + rsp_buf->len, svc_cat_3, 4); + rsp_buf->len += 4; + +#if 0 + BT_DBG("rsp_buf len: %d \n", rsp_buf->len); + for(int i = 0; i < rsp_buf->len; i++) + { + BT_WARN("0x%02x, ", rsp_buf->data[i]); + } + BT_WARN("\n"); +#endif + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_set_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + uint8_t acp_seid = net_buf_pull_u8(buf) >> 2; + uint8_t int_seid = net_buf_pull_u8(buf) >> 2; + + int res_pars = avdtp_parsing_capability(buf); + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SET_CONFIGURATION); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_get_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); +} + +static void handle_avdtp_reconf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); +} + +static void handle_avdtp_open_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_OPEN); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_start_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_START); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_close_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_CLOSE); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_suspend_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SUSPEND); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_abort_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); + if(!session) + { + BT_DBG("Error: Session not valid"); + return; + } + + struct net_buf *rsp_buf; + rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_ABORT); + if (!rsp_buf) { + BT_ERR("Error: No Buff available"); + return; + } + + int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (result < 0) + { + BT_ERR("Error: BT L2CAP send fail - result = %d", result); + return; + } +} + +static void handle_avdtp_sec_ctrl_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); +} + +static void handle_avdtp_get_all_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); +} + +static void handle_avdtp_dly_rpt_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type) +{ + BT_DBG(" "); +} + +/* L2CAP Interface callbacks */ +void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan) +{ + struct bt_avdtp *session; + + if (!chan) { + BT_ERR("Invalid AVDTP chan"); + return; + } + + session = AVDTP_CHAN(chan); + BT_DBG("chan %p session %p", chan, session); + /* Init the timer */ + k_delayed_work_init(&session->req->timeout_work, avdtp_timeout); + +} + +void bt_avdtp_l2cap_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_avdtp *session = AVDTP_CHAN(chan); + + BT_DBG("chan %p session %p", chan, session); + session->br_chan.chan.conn = NULL; + /* Clear the Pending req if set*/ +} + +void bt_avdtp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status) +{ + BT_DBG(""); +} + +int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_avdtp_single_sig_hdr *hdr; + struct bt_avdtp *session = AVDTP_CHAN(chan); + uint8_t i, msgtype, sigid, tid; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Recvd Wrong AVDTP Header"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + msgtype = AVDTP_GET_MSG_TYPE(hdr->hdr); + sigid = AVDTP_GET_SIG_ID(hdr->signal_id); + tid = AVDTP_GET_TR_ID(hdr->hdr); + + BT_DBG("msg_type[0x%02x] sig_id[0x%02x] tid[0x%02x]", + msgtype, sigid, tid); + +#if 0 + BT_DBG("avdtp payload len: %d \n", buf->len); + for(int i = 0; i < buf->len; i++) + { + BT_WARN("0x%02x, ", buf->data[i]); + } + BT_WARN("\n"); +#endif + + /* validate if there is an outstanding resp expected*/ + if (msgtype != BT_AVDTP_CMD) { + if (session->req == NULL) { + BT_DBG("Unexpected peer response"); + return 0; + } + + if (session->req->sig != sigid || + session->req->tid != tid) { + BT_DBG("Peer mismatch resp, expected sig[0x%02x]" + "tid[0x%02x]", session->req->sig, + session->req->tid); + return 0; + } + } + + for (i = 0U; i < ARRAY_SIZE(handler); i++) { + if (sigid == handler[i].sig_id) { + handler[i].func(session, buf, msgtype); + return 0; + } + } + + return 0; +} + +int bt_avdtp_l2cap_media_stream_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ +#if 0 + BT_DBG("avdtp payload len: %d \n", buf->len); + for(int i = 0; i < buf->len; i++) + { + BT_WARN("0x%02x, ", buf->data[i]); + } + BT_WARN("\n"); +#endif + + int res = a2dp_sbc_decode_process(buf->data, buf->len); + if(res) + { + BT_DBG("decode fail, error: %d \n", res); + } + + return 0; +} + +/*A2DP Layer interface */ +int bt_avdtp_connect(struct bt_conn *conn, struct bt_avdtp *session) +{ + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_avdtp_l2cap_connected, + .disconnected = bt_avdtp_l2cap_disconnected, + .encrypt_change = bt_avdtp_l2cap_encrypt_changed, + .recv = bt_avdtp_l2cap_recv + }; + + if (!session) { + return -EINVAL; + } + + session->br_chan.chan.ops = &ops; + session->br_chan.chan.required_sec_level = BT_SECURITY_L2; + + return bt_l2cap_chan_connect(conn, &session->br_chan.chan, + BT_L2CAP_PSM_AVDTP); +} + +int bt_avdtp_disconnect(struct bt_avdtp *session) +{ + if (!session) { + return -EINVAL; + } + + BT_DBG("session %p", session); + + return bt_l2cap_chan_disconnect(&session->br_chan.chan); +} + +int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + struct bt_avdtp *session = NULL; + int result; + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_avdtp_l2cap_connected, + .disconnected = bt_avdtp_l2cap_disconnected, + .recv = bt_avdtp_l2cap_recv, + }; + + static const struct bt_l2cap_chan_ops media_ops = { + .connected = bt_avdtp_l2cap_connected, + .disconnected = bt_avdtp_l2cap_disconnected, + .recv = bt_avdtp_l2cap_media_stream_recv, + }; + + BT_DBG("conn %p", conn); + /* Get the AVDTP session from upper layer */ + result = event_cb->accept(conn, &session); + if (result < 0) { + return result; + } + + if(!session->br_chan.chan.conn) + { + BT_DBG("create l2cap_br signal stream, session %p", session); + session->br_chan.chan.ops = &ops; + session->br_chan.rx.mtu = BT_AVDTP_MAX_MTU; + *chan = &session->br_chan.chan; + } + else + { + BT_DBG("create l2cap_br AV stream, session %p", session); + session->streams->chan.chan.ops = &media_ops; + session->streams->chan.rx.mtu = BT_AVDTP_MAX_MTU; + *chan = &session->streams->chan.chan; + } + + return 0; +} + +/* Application will register its callback */ +int bt_avdtp_register(struct bt_avdtp_event_cb *cb) +{ + BT_DBG(""); + + if (event_cb) { + return -EALREADY; + } + + event_cb = cb; + + return 0; +} + +int bt_avdtp_register_sep(uint8_t media_type, uint8_t role, + struct bt_avdtp_seid_lsep *lsep) +{ + BT_DBG(""); + + static uint8_t bt_avdtp_seid = BT_AVDTP_MIN_SEID; + + if (!lsep) { + return -EIO; + } + + if (bt_avdtp_seid == BT_AVDTP_MAX_SEID) { + return -EIO; + } + + lsep->sep.id = bt_avdtp_seid++; + lsep->sep.inuse = 0U; + lsep->sep.media_type = media_type; + lsep->sep.tsep = role; + + lsep->next = lseps; + lseps = lsep; + + return 0; +} + +/* init function */ +int bt_avdtp_init(void) +{ + int err; + static struct bt_l2cap_server avdtp_l2cap = { + .psm = BT_L2CAP_PSM_AVDTP, + .sec_level = BT_SECURITY_L2, + .accept = bt_avdtp_l2cap_accept, + }; + + BT_DBG(""); + + /* Register AVDTP PSM with L2CAP */ + err = bt_l2cap_br_server_register(&avdtp_l2cap); + if (err < 0) { + BT_ERR("AVDTP L2CAP Registration failed %d", err); + } + + return err; +} + +/* AVDTP Discover Request */ +int bt_avdtp_discover(struct bt_avdtp *session, + struct bt_avdtp_discover_params *param) +{ + struct net_buf *buf; + + BT_DBG(""); + if (!param || !session) { + BT_DBG("Error: Callback/Session not valid"); + return -EINVAL; + } + + buf = avdtp_create_pdu(BT_AVDTP_CMD, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_DISCOVER); + if (!buf) { + BT_ERR("Error: No Buff available"); + return -ENOMEM; + } + + /* Body of the message */ + + return avdtp_send(session, buf, ¶m->req); +} diff --git a/components/ble/ble_stack/host/avdtp_internal.h b/components/ble/ble_stack/host/avdtp_internal.h new file mode 100644 index 00000000..86191af7 --- /dev/null +++ b/components/ble/ble_stack/host/avdtp_internal.h @@ -0,0 +1,175 @@ +/* + * avdtp_internal.h - avdtp handling + + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* @brief A2DP ROLE's */ +#define A2DP_SRC_ROLE 0x00 +#define A2DP_SNK_ROLE 0x01 + +/* @brief AVDTP Role */ +#define BT_AVDTP_INT 0x00 +#define BT_AVDTP_ACP 0x01 + +#define BT_L2CAP_PSM_AVDTP 0x0019 + +/* AVDTP SIGNAL HEADER - Packet Type*/ +#define BT_AVDTP_PACKET_TYPE_SINGLE 0x00 +#define BT_AVDTP_PACKET_TYPE_START 0x01 +#define BT_AVDTP_PACKET_TYPE_CONTINUE 0x02 +#define BT_AVDTP_PACKET_TYPE_END 0x03 + +/* AVDTP SIGNAL HEADER - MESSAGE TYPE */ +#define BT_AVDTP_CMD 0x00 +#define BT_AVDTP_GEN_REJECT 0x01 +#define BT_AVDTP_ACCEPT 0x02 +#define BT_AVDTP_REJECT 0x03 + +/* @brief AVDTP SIGNAL HEADER - Signal Identifier */ +#define BT_AVDTP_DISCOVER 0x01 +#define BT_AVDTP_GET_CAPABILITIES 0x02 +#define BT_AVDTP_SET_CONFIGURATION 0x03 +#define BT_AVDTP_GET_CONFIGURATION 0x04 +#define BT_AVDTP_RECONFIGURE 0x05 +#define BT_AVDTP_OPEN 0x06 +#define BT_AVDTP_START 0x07 +#define BT_AVDTP_CLOSE 0x08 +#define BT_AVDTP_SUSPEND 0x09 +#define BT_AVDTP_ABORT 0x0a +#define BT_AVDTP_SECURITY_CONTROL 0x0b +#define BT_AVDTP_GET_ALL_CAPABILITIES 0x0c +#define BT_AVDTP_DELAYREPORT 0x0d + +/* @brief AVDTP STREAM STATE */ +#define BT_AVDTP_STREAM_STATE_IDLE 0x01 +#define BT_AVDTP_STREAM_STATE_CONFIGURED 0x02 +#define BT_AVDTP_STREAM_STATE_OPEN 0x03 +#define BT_AVDTP_STREAM_STATE_STREAMING 0x04 +#define BT_AVDTP_STREAM_STATE_CLOSING 0x05 + +/* @brief AVDTP Media TYPE */ +#define BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT 0x01 +#define BT_AVDTP_SERVICE_CAT_REPORTING 0x02 +#define BT_AVDTP_SERVICE_CAT_RECOVERY 0x03 +#define BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION 0x04 +#define BT_AVDTP_SERVICE_CAT_HDR_COMPRESSION 0x05 +#define BT_AVDTP_SERVICE_CAT_MULTIPLEXING 0x06 +#define BT_AVDTP_SERVICE_CAT_MEDIA_CODEC 0x07 +#define BT_AVDTP_SERVICE_CAT_DELAYREPORTING 0x08 + +/* @brief AVDTP Content Protection Capabilities */ +#define BT_AVDTP_CONTENT_PROTECTION_MSB 0x00 +#define BT_AVDTP_CONTENT_PROTECTION_LSB_DTCP 0x01 +#define BT_AVDTP_CONTENT_PROTECTION_LSB_SCMS_T 0x02 + +/* AVDTP Error Codes */ +#define BT_AVDTP_SUCCESS 0x00 +#define BT_AVDTP_ERR_BAD_HDR_FORMAT 0x01 +#define BT_AVDTP_ERR_BAD_LENGTH 0x11 +#define BT_AVDTP_ERR_BAD_ACP_SEID 0x12 +#define BT_AVDTP_ERR_SEP_IN_USE 0x13 +#define BT_AVDTP_ERR_SEP_NOT_IN_USE 0x14 +#define BT_AVDTP_ERR_BAD_SERV_CATEGORY 0x17 +#define BT_AVDTP_ERR_BAD_PAYLOAD_FORMAT 0x18 +#define BT_AVDTP_ERR_NOT_SUPPORTED_COMMAND 0x19 +#define BT_AVDTP_ERR_INVALID_CAPABILITIES 0x1a +#define BT_AVDTP_ERR_BAD_RECOVERY_TYPE 0x22 +#define BT_AVDTP_ERR_BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define BT_AVDTP_ERR_BAD_RECOVERY_FORMAT 0x25 +#define BT_AVDTP_ERR_BAD_ROHC_FORMAT 0x26 +#define BT_AVDTP_ERR_BAD_CP_FORMAT 0x27 +#define BT_AVDTP_ERR_BAD_MULTIPLEXING_FORMAT 0x28 +#define BT_AVDTP_ERR_UNSUPPORTED_CONFIGURAION 0x29 +#define BT_AVDTP_ERR_BAD_STATE 0x31 + +#define BT_AVDTP_MAX_MTU CONFIG_BT_L2CAP_RX_MTU + +#define BT_AVDTP_MIN_SEID 0x01 +#define BT_AVDTP_MAX_SEID 0x3E + +struct bt_avdtp; +struct bt_avdtp_req; + +typedef int (*bt_avdtp_func_t)(struct bt_avdtp *session, + struct bt_avdtp_req *req); + +struct bt_avdtp_req { + uint8_t sig; + uint8_t tid; + bt_avdtp_func_t func; + struct k_delayed_work timeout_work; +}; + +struct bt_avdtp_single_sig_hdr { + uint8_t hdr; + uint8_t signal_id; +} __packed; + +#define BT_AVDTP_SIG_HDR_LEN sizeof(struct bt_avdtp_single_sig_hdr) + +struct bt_avdtp_ind_cb { + /* + * discovery_ind; + * get_capabilities_ind; + * set_configuration_ind; + * open_ind; + * start_ind; + * suspend_ind; + * close_ind; + */ +}; + +struct bt_avdtp_cap { + uint8_t cat; + uint8_t len; + uint8_t data[0]; +}; + +struct bt_avdtp_sep { + uint8_t seid; + uint8_t len; + struct bt_avdtp_cap caps[0]; +}; + +struct bt_avdtp_discover_params { + struct bt_avdtp_req req; + uint8_t status; + struct bt_avdtp_sep *caps; +}; + +/** @brief Global AVDTP session structure. */ +struct bt_avdtp { + struct bt_l2cap_br_chan br_chan; + struct bt_avdtp_stream *streams; /* List of AV streams */ + struct bt_avdtp_req *req; +}; + +struct bt_avdtp_event_cb { + struct bt_avdtp_ind_cb *ind; + int (*accept)(struct bt_conn *conn, struct bt_avdtp **session); +}; + +/* Initialize AVDTP layer*/ +int bt_avdtp_init(void); + +/* Application register with AVDTP layer */ +int bt_avdtp_register(struct bt_avdtp_event_cb *cb); + +/* AVDTP connect */ +int bt_avdtp_connect(struct bt_conn *conn, struct bt_avdtp *session); + +/* AVDTP disconnect */ +int bt_avdtp_disconnect(struct bt_avdtp *session); + +/* AVDTP SEP register function */ +int bt_avdtp_register_sep(uint8_t media_type, uint8_t role, + struct bt_avdtp_seid_lsep *sep); + +/* AVDTP Discover Request */ +int bt_avdtp_discover(struct bt_avdtp *session, + struct bt_avdtp_discover_params *param); diff --git a/components/ble/ble_stack/host/bl_host_assist.c b/components/ble/ble_stack/host/bl_host_assist.c new file mode 100644 index 00000000..63cad223 --- /dev/null +++ b/components/ble/ble_stack/host/bl_host_assist.c @@ -0,0 +1,372 @@ +#include "ble_lib_api.h" +#include "bluetooth.h" +#include "conn.h" +#include "hci_core.h" +#include "hci_driver.h" +#include "byteorder.h" +#include "log.h" +#include "errno.h" + +struct blhast_le_adv_data{ + u8_t ad[31]; + size_t ad_len; +}; + +static struct bt_le_scan_param blhast_le_scan_param; +static struct bt_le_adv_param blhast_le_adv_param; +static struct blhast_le_adv_data blhast_le_ad; +static struct blhast_le_adv_data blhast_le_sd; +static bt_le_scan_cb_t *blhast_le_scan_cb; + +static void blhast_ble_scan_assist_cb(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb); +static void blhast_ble_adv_assist_cb(const struct bt_le_adv_param *param, const struct bt_data *ad, + size_t ad_len, const struct bt_data *sd, size_t sd_len); + +static struct blhast_cb assist_cb = { + .le_scan_cb = blhast_ble_scan_assist_cb, + .le_adv_cb = blhast_ble_adv_assist_cb, +}; + +extern struct bt_dev bt_dev; + +static void blhast_ble_scan_assist_cb(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) +{ + memcpy(&blhast_le_scan_param, param, sizeof(struct bt_le_scan_param)); + blhast_le_scan_cb = cb; +} + +static void blhast_ble_get_ad(const struct bt_data *ad, size_t ad_len, uint8_t *output) +{ + int i; + uint8_t data_len = 0; + + for (i = 0; i < ad_len; i++) { + *(output + data_len) = ad[i].type; + data_len++; + + *(output + data_len) = ad[i].data_len; + data_len++; + + memcpy(output + data_len, ad[i].data, ad[i].data_len); + + data_len += ad[i].data_len; + } +} + +static void blhast_ble_construct_ad(struct blhast_le_adv_data *adv_data, struct bt_data *output) +{ + int i; + size_t ad_len = adv_data->ad_len; + u8_t *p_ad = adv_data->ad; + + + for(i = 0; i < ad_len; i++){ + memcpy(&output[i], p_ad, 2);//type, data_len + p_ad += 2; + output[i].data = (const u8_t *)p_ad; + p_ad += output[i].data_len; + } +} + +static void blhast_ble_adv_assist_cb(const struct bt_le_adv_param *param, const struct bt_data *ad, + size_t ad_len, const struct bt_data *sd, size_t sd_len) +{ + memcpy(&blhast_le_adv_param, param, sizeof(struct bt_le_adv_param)); + + if(ad){ + blhast_le_ad.ad_len = ad_len; + memset(blhast_le_ad.ad, 0, sizeof(blhast_le_ad.ad)); + blhast_ble_get_ad(ad, ad_len, blhast_le_ad.ad); + } + + if(sd){ + blhast_le_sd.ad_len = sd_len; + memset(blhast_le_sd.ad, 0, sizeof(blhast_le_sd.ad)); + blhast_ble_get_ad(sd, sd_len, blhast_le_sd.ad); + } +} + +static int blhast_common_reset(void) +{ + + struct net_buf *rsp; + int err; + + if (!(bt_dev.drv->quirks & BT_QUIRK_NO_RESET)) { + /* Send HCI_RESET */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp); + if (err) { + return err; + } + bt_hci_reset_complete(rsp); + net_buf_unref(rsp); + } + + #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + err = bt_set_flow_control(); + if (err) { + return err; + } + #endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */ + + return 0; +} + +static int blhast_ble_reset(void) +{ + struct bt_hci_cp_write_le_host_supp *cp_le; + struct net_buf *buf, *rsp; + int err; + + if (!BT_FEAT_LE(bt_dev.features)) { + BT_ERR("Non-LE capable controller detected!"); + return -ENODEV; + } + + if (BT_FEAT_BREDR(bt_dev.features)) { + buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, + sizeof(*cp_le)); + if (!buf) { + return -ENOBUFS; + } + + cp_le = net_buf_add(buf, sizeof(*cp_le)); + + /* Explicitly enable LE for dual-mode controllers */ + cp_le->le = 0x01; + cp_le->simul = 0x00; + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf, + NULL); + if (err) { + return err; + } + } + + if (IS_ENABLED(CONFIG_BT_CONN) && + IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features)) { + struct bt_hci_cp_le_write_default_data_len *cp; + struct bt_hci_rp_le_read_max_data_len *rp; + u16_t tx_octets, tx_time; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MAX_DATA_LEN, NULL, + &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + tx_octets = sys_le16_to_cpu(rp->max_tx_octets); + tx_time = sys_le16_to_cpu(rp->max_tx_time); + net_buf_unref(rsp); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->max_tx_octets = sys_cpu_to_le16(tx_octets); + cp->max_tx_time = sys_cpu_to_le16(tx_time); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN, + buf, NULL); + if (err) { + return err; + } + } + + return bt_le_set_event_mask(); +} + +#if defined(CONFIG_BT_BREDR) +static int blhast_br_reset(void) +{ + struct net_buf *buf; + struct bt_hci_cp_write_ssp_mode *ssp_cp; + struct bt_hci_cp_write_inquiry_mode *inq_cp; + struct bt_hci_write_local_name *name_cp; + int err; + + + /* Set SSP mode */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SSP_MODE, sizeof(*ssp_cp)); + if (!buf) { + return -ENOBUFS; + } + + ssp_cp = net_buf_add(buf, sizeof(*ssp_cp)); + ssp_cp->mode = 0x01; + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SSP_MODE, buf, NULL); + if (err) { + return err; + } + + /* Enable Inquiry results with RSSI or extended Inquiry */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_INQUIRY_MODE, sizeof(*inq_cp)); + if (!buf) { + return -ENOBUFS; + } + + inq_cp = net_buf_add(buf, sizeof(*inq_cp)); + inq_cp->mode = 0x02; + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_INQUIRY_MODE, buf, NULL); + if (err) { + return err; + } + + /* Set local name */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp)); + if (!buf) { + return -ENOBUFS; + } + + name_cp = net_buf_add(buf, sizeof(*name_cp)); + strncpy((char *)name_cp->local_name, CONFIG_BT_DEVICE_NAME, + sizeof(name_cp->local_name)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL); + if (err) { + return err; + } + + /* Set page timeout*/ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_PAGE_TIMEOUT, sizeof(u16_t)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_le16(buf, CONFIG_BT_PAGE_TIMEOUT); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_PAGE_TIMEOUT, buf, NULL); + if (err) { + return err; + } + + + /* Enable BR/EDR SC if supported */ + if (BT_FEAT_SC(bt_dev.features)) { + struct bt_hci_cp_write_sc_host_supp *sc_cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP, + sizeof(*sc_cp)); + if (!buf) { + return -ENOBUFS; + } + + sc_cp = net_buf_add(buf, sizeof(*sc_cp)); + sc_cp->sc_support = 0x01; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf, + NULL); + if (err) { + return err; + } + } + + return 0; + +} +#endif + +static int blhast_host_hci_reset(void) +{ + int err; + ATOMIC_DEFINE(old_flags, BT_DEV_NUM_FLAGS); + memcpy(old_flags, bt_dev.flags, sizeof(old_flags)); + + err = blhast_common_reset(); + + if (err) { + return err; + } + + err = blhast_ble_reset(); + if (err) { + return err; + } + + #if defined(CONFIG_BT_BREDR) + if (BT_FEAT_BREDR(bt_dev.features)) { + err = blhast_br_reset(); + if (err) { + return err; + } + } + #endif + + err = bt_set_event_mask(); + if (err) { + return err; + } + + memcpy(bt_dev.flags, old_flags, sizeof(old_flags)); + + return 0; +} + +static void blhast_host_state_restore(void) +{ + struct bt_data *ad = NULL; + struct bt_data *sd = NULL; + k_sem_give(&bt_dev.ncmd_sem); + net_buf_unref(bt_dev.sent_cmd); + bt_dev.sent_cmd = NULL; + + blhast_host_hci_reset(); + + #if defined(CONFIG_BT_CONN) + bt_notify_disconnected(); + #endif + + atomic_set_bit(bt_dev.flags, BT_DEV_ASSIST_RUN); + + #if defined(CONFIG_BT_OBSERVER) + if(atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) + { + BT_WARN("Restore BLE scan\r\n"); + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING); + bt_le_scan_start((const struct bt_le_scan_param *)&blhast_le_scan_param, blhast_le_scan_cb); + } + #endif + + if(atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING)) + { + BT_WARN("Restore BLE advertising\r\n"); + if(blhast_le_ad.ad_len > 0) + { + ad = k_malloc(sizeof(struct bt_data) * blhast_le_ad.ad_len); + blhast_ble_construct_ad(&blhast_le_ad, ad); + } + if(blhast_le_sd.ad_len > 0) + { + sd = k_malloc(sizeof(struct bt_data) * blhast_le_sd.ad_len); + blhast_ble_construct_ad(&blhast_le_sd, sd); + } + + bt_le_adv_start((const struct bt_le_adv_param *)&blhast_le_adv_param, ad, + blhast_le_ad.ad_len, sd, blhast_le_sd.ad_len); + + if(ad) + k_free(ad); + if(sd) + k_free(sd); + } + + atomic_clear_bit(bt_dev.flags, BT_DEV_ASSIST_RUN); +} + +void blhast_bt_reset(void) +{ + ble_controller_reset(); + blhast_host_state_restore(); +} + +void blhast_init(void) +{ + memset(&blhast_le_ad, 0, sizeof(struct blhast_le_adv_data)); + memset(&blhast_le_sd, 0, sizeof(struct blhast_le_adv_data)); + bt_register_host_assist_cb(&assist_cb); +} diff --git a/components/ble/ble_stack/host/conn.c b/components/ble/ble_stack/host/conn.c new file mode 100644 index 00000000..147baaf8 --- /dev/null +++ b/components/ble/ble_stack/host/conn.c @@ -0,0 +1,2678 @@ +/* conn.c - Bluetooth connection handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_CONN) +#define LOG_MODULE_NAME bt_conn +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "keys.h" +#include "smp.h" +#include "att_internal.h" +#include "gatt_internal.h" +#if defined(BFLB_BLE) +#include "config.h" + +extern struct k_sem g_poll_sem; +#endif +struct tx_meta { + struct bt_conn_tx *tx; +}; + +#define tx_data(buf) ((struct tx_meta *)net_buf_user_data(buf)) + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_DEFINE(acl_tx_pool, CONFIG_BT_L2CAP_TX_BUF_COUNT, + BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU), + sizeof(struct tx_meta), NULL); +#else +struct net_buf_pool acl_tx_pool; +#endif + +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + +#if defined(BT_CTLR_TX_BUFFER_SIZE) +#define FRAG_SIZE BT_L2CAP_BUF_SIZE(BT_CTLR_TX_BUFFER_SIZE - 4) +#else +#define FRAG_SIZE BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU) +#endif + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +/* Dedicated pool for fragment buffers in case queued up TX buffers don't + * fit the controllers buffer size. We can't use the acl_tx_pool for the + * fragmentation, since it's possible that pool is empty and all buffers + * are queued up in the TX queue. In such a situation, trying to allocate + * another buffer from the acl_tx_pool would result in a deadlock. + */ +NET_BUF_POOL_FIXED_DEFINE(frag_pool, CONFIG_BT_L2CAP_TX_FRAG_COUNT, FRAG_SIZE, + NULL); +#else +struct net_buf_pool frag_pool; +#endif +#endif /* CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 */ + +/* How long until we cancel HCI_LE_Create_Connection */ +#define CONN_TIMEOUT K_SECONDS(CONFIG_BT_CREATE_CONN_TIMEOUT) + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +const struct bt_conn_auth_cb *bt_auth; +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + +static struct bt_conn conns[CONFIG_BT_MAX_CONN]; +static struct bt_conn_cb *callback_list; + +static struct bt_conn_tx conn_tx[CONFIG_BT_CONN_TX_MAX]; +K_FIFO_DEFINE(free_tx); + +#if defined(CONFIG_BT_BREDR) +static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN]; + +enum pairing_method { + LEGACY, /* Legacy (pre-SSP) pairing */ + JUST_WORKS, /* JustWorks pairing */ + PASSKEY_INPUT, /* Passkey Entry input */ + PASSKEY_DISPLAY, /* Passkey Entry display */ + PASSKEY_CONFIRM, /* Passkey confirm */ +}; + +/* based on table 5.7, Core Spec 4.2, Vol.3 Part C, 5.2.2.6 */ +static const u8_t ssp_method[4 /* remote */][4 /* local */] = { + { JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS }, + { JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS }, + { PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS }, + { JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS }, +}; +#endif /* CONFIG_BT_BREDR */ + +struct k_sem *bt_conn_get_pkts(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.mtu) { + return &bt_dev.br.pkts; + } +#endif /* CONFIG_BT_BREDR */ + + return &bt_dev.le.pkts; +} + +static inline const char *state2str(bt_conn_state_t state) +{ + switch (state) { + case BT_CONN_DISCONNECTED: + return "disconnected"; + case BT_CONN_CONNECT_SCAN: + return "connect-scan"; + case BT_CONN_CONNECT_DIR_ADV: + return "connect-dir-adv"; + case BT_CONN_CONNECT: + return "connect"; + case BT_CONN_CONNECTED: + return "connected"; + case BT_CONN_DISCONNECT: + return "disconnect"; + default: + return "(unknown)"; + } +} + +static void notify_connected(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->connected) { + cb->connected(conn, conn->err); + } + } + + if (!conn->err) { + bt_gatt_connected(conn); + } +} + +static void notify_disconnected(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->disconnected) { + cb->disconnected(conn, conn->err); + } + } +} + +void notify_le_param_updated(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + /* If new connection parameters meet requirement of pending + * parameters don't send slave conn param request anymore on timeout + */ + if (atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET) && + conn->le.interval >= conn->le.interval_min && + conn->le.interval <= conn->le.interval_max && + conn->le.latency == conn->le.pending_latency && + conn->le.timeout == conn->le.pending_timeout) { + atomic_clear_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET); + } + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->le_param_updated) { + cb->le_param_updated(conn, conn->le.interval, + conn->le.latency, + conn->le.timeout); + } + } +} + +bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) +{ + struct bt_conn_cb *cb; + + if (!bt_le_conn_params_valid(param)) { + return false; + } + + for (cb = callback_list; cb; cb = cb->_next) { + if (!cb->le_param_req) { + continue; + } + + if (!cb->le_param_req(conn, param)) { + return false; + } + + /* The callback may modify the parameters so we need to + * double-check that it returned valid parameters. + */ + if (!bt_le_conn_params_valid(param)) { + return false; + } + } + + /* Default to accepting if there's no app callback */ + return true; +} + +static int send_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + BT_DBG("conn %p features 0x%02x params (%d-%d %d %d)", conn, + conn->le.features[0], param->interval_min, + param->interval_max, param->latency, param->timeout); + + /* Use LE connection parameter request if both local and remote support + * it; or if local role is master then use LE connection update. + */ + if ((BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features) && + BT_FEAT_LE_CONN_PARAM_REQ_PROC(conn->le.features) && + !atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_L2CAP)) || + (conn->role == BT_HCI_ROLE_MASTER)) { + int rc; + + rc = bt_conn_le_conn_update(conn, param); + + /* store those in case of fallback to L2CAP */ + if (rc == 0) { + conn->le.pending_latency = param->latency; + conn->le.pending_timeout = param->timeout; + } + + return rc; + } + + /* If remote master does not support LL Connection Parameters Request + * Procedure + */ + return bt_l2cap_update_conn_param(conn, param); +} + +static void tx_free(struct bt_conn_tx *tx) +{ + tx->cb = NULL; + tx->user_data = NULL; + tx->pending_no_cb = 0U; + k_fifo_put(&free_tx, tx); +} + +static void tx_notify(struct bt_conn *conn) +{ + BT_DBG("conn %p", conn); + + while (1) { + struct bt_conn_tx *tx; + unsigned int key; + bt_conn_tx_cb_t cb; + void *user_data; + + key = irq_lock(); + if (sys_slist_is_empty(&conn->tx_complete)) { + irq_unlock(key); + break; + } + + tx = (void *)sys_slist_get_not_empty(&conn->tx_complete); + irq_unlock(key); + + BT_DBG("tx %p cb %p user_data %p", tx, tx->cb, tx->user_data); + + /* Copy over the params */ + cb = tx->cb; + user_data = tx->user_data; + + /* Free up TX notify since there may be user waiting */ + tx_free(tx); + + /* Run the callback, at this point it should be safe to + * allocate new buffers since the TX should have been + * unblocked by tx_free. + */ + cb(conn, user_data); + } +} + +static void tx_complete_work(struct k_work *work) +{ + struct bt_conn *conn = CONTAINER_OF(work, struct bt_conn, + tx_complete_work); + + BT_DBG("conn %p", conn); + + tx_notify(conn); +} + +static void conn_update_timeout(struct k_work *work) +{ + struct bt_conn *conn = CONTAINER_OF(work, struct bt_conn, update_work); + const struct bt_le_conn_param *param; + + BT_DBG("conn %p", conn); + + if (conn->state == BT_CONN_DISCONNECTED) { + bt_l2cap_disconnected(conn); + notify_disconnected(conn); + + /* Release the reference we took for the very first + * state transition. + */ + bt_conn_unref(conn); + return; + } + + if (conn->type != BT_CONN_TYPE_LE) { + return; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_CONN_ROLE_MASTER) { + /* we don't call bt_conn_disconnect as it would also clear + * auto connect flag if it was set, instead just cancel + * connection directly + */ + bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL, NULL); + return; + } + +#if defined (CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS) + /* if application set own params use those, otherwise use defaults */ + if (atomic_test_and_clear_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET)) { + param = BT_LE_CONN_PARAM(conn->le.interval_min, + conn->le.interval_max, + conn->le.pending_latency, + conn->le.pending_timeout); + + send_conn_le_param_update(conn, param); + } else { + param = BT_LE_CONN_PARAM(CONFIG_BT_PERIPHERAL_PREF_MIN_INT, + CONFIG_BT_PERIPHERAL_PREF_MAX_INT, + CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY, + CONFIG_BT_PERIPHERAL_PREF_TIMEOUT); + + send_conn_le_param_update(conn, param); + } +#else + /* update only if application set own params */ + if (atomic_test_and_clear_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET)) { + param = BT_LE_CONN_PARAM(conn->le.interval_min, + conn->le.interval_max, + conn->le.latency, + conn->le.timeout); + + send_conn_le_param_update(conn, param); + } +#endif + + atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE); +} + +#if defined(CONFIG_BT_AUDIO) +struct bt_conn *iso_conn_new(struct bt_conn *conns, size_t size) +{ + struct bt_conn *conn = NULL; + int i; + + for (i = 0; i < size; i++) { + if (atomic_cas(&conns[i].ref, 0, 1)) { + conn = &conns[i]; + break; + } + } + + if (!conn) { + return NULL; + } + + (void)memset(conn, 0, offsetof(struct bt_conn, ref)); + + return conn; +} +#endif + +static struct bt_conn *conn_new(void) +{ + struct bt_conn *conn = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + conn = &conns[i]; + break; + } + } + + if (!conn) { + return NULL; + } + + (void)memset(conn, 0, sizeof(*conn)); + k_delayed_work_init(&conn->update_work, conn_update_timeout); + + k_work_init(&conn->tx_complete_work, tx_complete_work); + + atomic_set(&conn->ref, 1); + + return conn; +} + +#if defined(BFLB_BLE) +bool le_check_valid_conn(void) +{ + int i; + + for(i= 0; i < ARRAY_SIZE(conns); i++){ + if(atomic_get(&conns[i].ref)){ + return true; + } + } + + return false; +} + +#if defined(BFLB_HOST_ASSISTANT) +void bt_notify_disconnected(void) +{ + int i; + + for(i= 0; i < ARRAY_SIZE(conns); i++){ + if(atomic_get(&conns[i].ref)){ + conns[i].err = BT_HCI_ERR_UNSPECIFIED; + notify_disconnected(&conns[i]); + } + } +} +#endif//#if defined(BFLB_HOST_ASSISTANT) +#endif + +#if defined(CONFIG_BT_BREDR) +void bt_sco_cleanup(struct bt_conn *sco_conn) +{ + bt_conn_unref(sco_conn->sco.acl); + sco_conn->sco.acl = NULL; + bt_conn_unref(sco_conn); +} + +static struct bt_conn *sco_conn_new(void) +{ + struct bt_conn *sco_conn = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { + if (!atomic_get(&sco_conns[i].ref)) { + sco_conn = &sco_conns[i]; + break; + } + } + + if (!sco_conn) { + return NULL; + } + + (void)memset(sco_conn, 0, sizeof(*sco_conn)); + + atomic_set(&sco_conn->ref, 1); + + return sco_conn; +} + +struct bt_conn *bt_conn_create_br(const bt_addr_t *peer, + const struct bt_br_conn_param *param) +{ + struct bt_hci_cp_connect *cp; + struct bt_conn *conn; + struct net_buf *buf; + + conn = bt_conn_lookup_addr_br(peer); + if (conn) { + switch (conn->state) { + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return conn; + default: + bt_conn_unref(conn); + return NULL; + } + } + + conn = bt_conn_add_br(peer); + if (!conn) { + return NULL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_CONNECT, sizeof(*cp)); + if (!buf) { + bt_conn_unref(conn); + return NULL; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + (void)memset(cp, 0, sizeof(*cp)); + + memcpy(&cp->bdaddr, peer, sizeof(cp->bdaddr)); + cp->packet_type = sys_cpu_to_le16(0xcc18); /* DM1 DH1 DM3 DH5 DM5 DH5 */ + cp->pscan_rep_mode = 0x02; /* R2 */ + cp->allow_role_switch = param->allow_role_switch ? 0x01 : 0x00; + cp->clock_offset = 0x0000; /* TODO used cached clock offset */ + + if (bt_hci_cmd_send_sync(BT_HCI_OP_CONNECT, buf, NULL) < 0) { + bt_conn_unref(conn); + return NULL; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + conn->role = BT_CONN_ROLE_MASTER; + + return conn; +} + +struct bt_conn *bt_conn_create_sco(const bt_addr_t *peer) +{ + struct bt_hci_cp_setup_sync_conn *cp; + struct bt_conn *sco_conn; + struct net_buf *buf; + int link_type; + + sco_conn = bt_conn_lookup_addr_sco(peer); + if (sco_conn) { + switch (sco_conn->state) { + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return sco_conn; + default: + bt_conn_unref(sco_conn); + return NULL; + } + } + + if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) { + link_type = BT_HCI_ESCO; + } else { + link_type = BT_HCI_SCO; + } + + sco_conn = bt_conn_add_sco(peer, link_type); + if (!sco_conn) { + return NULL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_SETUP_SYNC_CONN, sizeof(*cp)); + if (!buf) { + bt_sco_cleanup(sco_conn); + return NULL; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + (void)memset(cp, 0, sizeof(*cp)); + + BT_ERR("handle : %x", sco_conn->sco.acl->handle); + + cp->handle = sco_conn->sco.acl->handle; + cp->pkt_type = sco_conn->sco.pkt_type; + cp->tx_bandwidth = 0x00001f40; + cp->rx_bandwidth = 0x00001f40; + cp->max_latency = 0x0007; + cp->retrans_effort = 0x01; + cp->content_format = BT_VOICE_CVSD_16BIT; + + if (bt_hci_cmd_send_sync(BT_HCI_OP_SETUP_SYNC_CONN, buf, + NULL) < 0) { + bt_sco_cleanup(sco_conn); + return NULL; + } + + bt_conn_set_state(sco_conn, BT_CONN_CONNECT); + + return sco_conn; +} + +struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { + if (!atomic_get(&sco_conns[i].ref)) { + continue; + } + + if (sco_conns[i].type != BT_CONN_TYPE_SCO) { + continue; + } + + if (!bt_addr_cmp(peer, &sco_conns[i].sco.acl->br.dst)) { + return bt_conn_ref(&sco_conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_BR) { + continue; + } + + if (!bt_addr_cmp(peer, &conns[i].br.dst)) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_add_sco(const bt_addr_t *peer, int link_type) +{ + struct bt_conn *sco_conn = sco_conn_new(); + + if (!sco_conn) { + return NULL; + } + + sco_conn->sco.acl = bt_conn_lookup_addr_br(peer); + sco_conn->type = BT_CONN_TYPE_SCO; + + if (link_type == BT_HCI_SCO) { + if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) { + sco_conn->sco.pkt_type = (bt_dev.br.esco_pkt_type & + ESCO_PKT_MASK); + } else { + sco_conn->sco.pkt_type = (bt_dev.br.esco_pkt_type & + SCO_PKT_MASK); + } + } else if (link_type == BT_HCI_ESCO) { + sco_conn->sco.pkt_type = (bt_dev.br.esco_pkt_type & + ~EDR_ESCO_PKT_MASK); + } + + return sco_conn; +} + +struct bt_conn *bt_conn_add_br(const bt_addr_t *peer) +{ + struct bt_conn *conn = conn_new(); + + if (!conn) { + return NULL; + } + + bt_addr_copy(&conn->br.dst, peer); + conn->type = BT_CONN_TYPE_BR; + + return conn; +} + +static int pin_code_neg_reply(const bt_addr_t *bdaddr) +{ + struct bt_hci_cp_pin_code_neg_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + + return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_NEG_REPLY, buf, NULL); +} + +static int pin_code_reply(struct bt_conn *conn, const char *pin, u8_t len) +{ + struct bt_hci_cp_pin_code_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + cp->pin_len = len; + strncpy((char *)cp->pin_code, pin, sizeof(cp->pin_code)); + + return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_REPLY, buf, NULL); +} + +int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin) +{ + size_t len; + + if (!bt_auth) { + return -EINVAL; + } + + if (conn->type != BT_CONN_TYPE_BR) { + return -EINVAL; + } + + len = strlen(pin); + if (len > 16) { + return -EINVAL; + } + + if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) { + BT_WARN("PIN code for %s is not 16 bytes wide", + bt_addr_str(&conn->br.dst)); + return -EPERM; + } + + /* Allow user send entered PIN to remote, then reset user state. */ + if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) { + return -EPERM; + } + + if (len == 16) { + atomic_set_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE); + } + + return pin_code_reply(conn, pin, len); +} + +void bt_conn_pin_code_req(struct bt_conn *conn) +{ + if (bt_auth && bt_auth->pincode_entry) { + bool secure = false; + + if (conn->required_sec_level == BT_SECURITY_L3) { + secure = true; + } + + atomic_set_bit(conn->flags, BT_CONN_USER); + atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING); + bt_auth->pincode_entry(conn, secure); + } else { + pin_code_neg_reply(&conn->br.dst); + } +} + +u8_t bt_conn_get_io_capa(void) +{ + if (!bt_auth) { + return BT_IO_NO_INPUT_OUTPUT; + } + + if (bt_auth->passkey_confirm && bt_auth->passkey_display) { + return BT_IO_DISPLAY_YESNO; + } + + if (bt_auth->passkey_entry) { + return BT_IO_KEYBOARD_ONLY; + } + + if (bt_auth->passkey_display) { + return BT_IO_DISPLAY_ONLY; + } + + return BT_IO_NO_INPUT_OUTPUT; +} + +static u8_t ssp_pair_method(const struct bt_conn *conn) +{ + return ssp_method[conn->br.remote_io_capa][bt_conn_get_io_capa()]; +} + +u8_t bt_conn_ssp_get_auth(const struct bt_conn *conn) +{ + /* Validate no bond auth request, and if valid use it. */ + if ((conn->br.remote_auth == BT_HCI_NO_BONDING) || + ((conn->br.remote_auth == BT_HCI_NO_BONDING_MITM) && + (ssp_pair_method(conn) > JUST_WORKS))) { + return conn->br.remote_auth; + } + + /* Local & remote have enough IO capabilities to get MITM protection. */ + if (ssp_pair_method(conn) > JUST_WORKS) { + return conn->br.remote_auth | BT_MITM; + } + + /* No MITM protection possible so ignore remote MITM requirement. */ + return (conn->br.remote_auth & ~BT_MITM); +} + +static int ssp_confirm_reply(struct bt_conn *conn) +{ + struct bt_hci_cp_user_confirm_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + + return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_REPLY, buf, NULL); +} + +static int ssp_confirm_neg_reply(struct bt_conn *conn) +{ + struct bt_hci_cp_user_confirm_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + + return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, buf, + NULL); +} + +void bt_conn_ssp_auth_complete(struct bt_conn *conn, u8_t status) +{ + if (!status) { + bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND); + + if (bt_auth && bt_auth->pairing_complete) { + bt_auth->pairing_complete(conn, bond); + } + } else { + if (bt_auth && bt_auth->pairing_failed) { + bt_auth->pairing_failed(conn, status); + } + } +} + +void bt_conn_ssp_auth(struct bt_conn *conn, u32_t passkey) +{ + conn->br.pairing_method = ssp_pair_method(conn); + + /* + * If local required security is HIGH then MITM is mandatory. + * MITM protection is no achievable when SSP 'justworks' is applied. + */ + if (conn->required_sec_level > BT_SECURITY_L2 && + conn->br.pairing_method == JUST_WORKS) { + BT_DBG("MITM protection infeasible for required security"); + ssp_confirm_neg_reply(conn); + return; + } + + switch (conn->br.pairing_method) { + case PASSKEY_CONFIRM: + atomic_set_bit(conn->flags, BT_CONN_USER); + bt_auth->passkey_confirm(conn, passkey); + break; + case PASSKEY_DISPLAY: + atomic_set_bit(conn->flags, BT_CONN_USER); + bt_auth->passkey_display(conn, passkey); + break; + case PASSKEY_INPUT: + atomic_set_bit(conn->flags, BT_CONN_USER); + bt_auth->passkey_entry(conn); + break; + case JUST_WORKS: + /* + * When local host works as pairing acceptor and 'justworks' + * model is applied then notify user about such pairing request. + * [BT Core 4.2 table 5.7, Vol 3, Part C, 5.2.2.6] + */ + if (bt_auth && bt_auth->pairing_confirm && + !atomic_test_bit(conn->flags, + BT_CONN_BR_PAIRING_INITIATOR)) { + atomic_set_bit(conn->flags, BT_CONN_USER); + bt_auth->pairing_confirm(conn); + break; + } + ssp_confirm_reply(conn); + break; + default: + break; + } +} + +static int ssp_passkey_reply(struct bt_conn *conn, unsigned int passkey) +{ + struct bt_hci_cp_user_passkey_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + cp->passkey = sys_cpu_to_le32(passkey); + + return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_REPLY, buf, NULL); +} + +static int ssp_passkey_neg_reply(struct bt_conn *conn) +{ + struct bt_hci_cp_user_passkey_neg_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + + return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, buf, + NULL); +} + +static int bt_hci_connect_br_cancel(struct bt_conn *conn) +{ + struct bt_hci_cp_connect_cancel *cp; + struct bt_hci_rp_connect_cancel *rp; + struct net_buf *buf, *rsp; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_CONNECT_CANCEL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + memcpy(&cp->bdaddr, &conn->br.dst, sizeof(cp->bdaddr)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_CONNECT_CANCEL, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + + err = rp->status ? -EIO : 0; + + net_buf_unref(rsp); + + return err; +} + +static int conn_auth(struct bt_conn *conn) +{ + struct bt_hci_cp_auth_requested *auth; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_AUTH_REQUESTED, sizeof(*auth)); + if (!buf) { + return -ENOBUFS; + } + + auth = net_buf_add(buf, sizeof(*auth)); + auth->handle = sys_cpu_to_le16(conn->handle); + + atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR); + + return bt_hci_cmd_send_sync(BT_HCI_OP_AUTH_REQUESTED, buf, NULL); +} +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_SMP) +void bt_conn_identity_resolved(struct bt_conn *conn) +{ + const bt_addr_le_t *rpa; + struct bt_conn_cb *cb; + + if (conn->role == BT_HCI_ROLE_MASTER) { + rpa = &conn->le.resp_addr; + } else { + rpa = &conn->le.init_addr; + } + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->identity_resolved) { + cb->identity_resolved(conn, rpa, &conn->le.dst); + } + } +} + +int bt_conn_le_start_encryption(struct bt_conn *conn, u8_t rand[8], + u8_t ediv[2], const u8_t *ltk, size_t len) +{ + struct bt_hci_cp_le_start_encryption *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + memcpy(&cp->rand, rand, sizeof(cp->rand)); + memcpy(&cp->ediv, ediv, sizeof(cp->ediv)); + + memcpy(cp->ltk, ltk, len); + if (len < sizeof(cp->ltk)) { + (void)memset(cp->ltk + len, 0, sizeof(cp->ltk) - len); + } + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL); +} +#endif /* CONFIG_BT_SMP */ + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +u8_t bt_conn_enc_key_size(struct bt_conn *conn) +{ + //GATT/SR/GAR/BV-04-C + // if the connection instance is valid + if(!conn){ + return 0; + } + + if (!conn->encrypt) { + return 0; + } + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + struct bt_hci_cp_read_encryption_key_size *cp; + struct bt_hci_rp_read_encryption_key_size *rp; + struct net_buf *buf; + struct net_buf *rsp; + u8_t key_size; + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE, + sizeof(*cp)); + if (!buf) { + return 0; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + + if (bt_hci_cmd_send_sync(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE, + buf, &rsp)) { + return 0; + } + + rp = (void *)rsp->data; + + key_size = rp->status ? 0 : rp->key_size; + + net_buf_unref(rsp); + + return key_size; + } + + if (IS_ENABLED(CONFIG_BT_SMP)) { + return conn->le.keys ? conn->le.keys->enc_size : 0; + } + + return 0; +} + +void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->security_changed) { + cb->security_changed(conn, conn->sec_level, err); + } + } +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) + if (!err && conn->sec_level >= BT_SECURITY_L2) { + bt_keys_update_usage(conn->id, bt_conn_get_dst(conn)); + } +#endif +} + +static int start_security(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING)) { + return -EBUSY; + } + + if (conn->required_sec_level > BT_SECURITY_L3) { + return -ENOTSUP; + } + + if (bt_conn_get_io_capa() == BT_IO_NO_INPUT_OUTPUT && + conn->required_sec_level > BT_SECURITY_L2) { + return -EINVAL; + } + + return conn_auth(conn); + } +#endif /* CONFIG_BT_BREDR */ + + if (IS_ENABLED(CONFIG_BT_SMP)) { + return bt_smp_start_security(conn); + } + + return -EINVAL; +} + +int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec) +{ + int err; + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && + sec < BT_SECURITY_L4) { + return -EOPNOTSUPP; + } + + /* nothing to do */ + if (conn->sec_level >= sec || conn->required_sec_level >= sec) { + return 0; + } + + atomic_set_bit_to(conn->flags, BT_CONN_FORCE_PAIR, + sec & BT_SECURITY_FORCE_PAIR); + conn->required_sec_level = sec & ~BT_SECURITY_FORCE_PAIR; + + err = start_security(conn); + + /* reset required security level in case of error */ + if (err) { + conn->required_sec_level = conn->sec_level; + } + + return err; +} + +bt_security_t bt_conn_get_security(struct bt_conn *conn) +{ + return conn->sec_level; +} +#else +bt_security_t bt_conn_get_security(struct bt_conn *conn) +{ + return BT_SECURITY_L1; +} +#endif /* CONFIG_BT_SMP */ + +void bt_conn_cb_register(struct bt_conn_cb *cb) +{ + cb->_next = callback_list; + callback_list = cb; +} + +void bt_conn_reset_rx_state(struct bt_conn *conn) +{ + if (!conn->rx_len) { + return; + } + + net_buf_unref(conn->rx); + conn->rx = NULL; + conn->rx_len = 0U; +} + +void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, u8_t flags) +{ + struct bt_l2cap_hdr *hdr; + u16_t len; + + /* Make sure we notify any pending TX callbacks before processing + * new data for this connection. + */ + tx_notify(conn); + + BT_DBG("handle %u len %u flags %02x", conn->handle, buf->len, flags); + + /* Check packet boundary flags */ + switch (flags) { + case BT_ACL_START: + hdr = (void *)buf->data; + len = sys_le16_to_cpu(hdr->len); + + BT_DBG("First, len %u final %u", buf->len, len); + + if (conn->rx_len) { + BT_ERR("Unexpected first L2CAP frame"); + bt_conn_reset_rx_state(conn); + } + + conn->rx_len = (sizeof(*hdr) + len) - buf->len; + BT_DBG("rx_len %u", conn->rx_len); + if (conn->rx_len) { + conn->rx = buf; + return; + } + + break; + case BT_ACL_CONT: + if (!conn->rx_len) { + BT_ERR("Unexpected L2CAP continuation"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + if (buf->len > conn->rx_len) { + BT_ERR("L2CAP data overflow"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + BT_DBG("Cont, len %u rx_len %u", buf->len, conn->rx_len); + + if (buf->len > net_buf_tailroom(conn->rx)) { + BT_ERR("Not enough buffer space for L2CAP data"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + net_buf_add_mem(conn->rx, buf->data, buf->len); + conn->rx_len -= buf->len; + net_buf_unref(buf); + + if (conn->rx_len) { + return; + } + + buf = conn->rx; + conn->rx = NULL; + conn->rx_len = 0U; + + break; + default: + BT_ERR("Unexpected ACL flags (0x%02x)", flags); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + hdr = (void *)buf->data; + len = sys_le16_to_cpu(hdr->len); + + if (sizeof(*hdr) + len != buf->len) { + BT_ERR("ACL len mismatch (%u != %u)", len, buf->len); + net_buf_unref(buf); + return; + } + + BT_DBG("Successfully parsed %u byte L2CAP packet", buf->len); + + bt_l2cap_recv(conn, buf); +} + +static struct bt_conn_tx *conn_tx_alloc(void) +{ + /* The TX context always get freed in the system workqueue, + * so if we're in the same workqueue but there are no immediate + * contexts available, there's no chance we'll get one by waiting. + */ +#if !defined(BFLB_BLE) + if (k_current_get() == &k_sys_work_q.thread) { + return k_fifo_get(&free_tx, K_NO_WAIT); + } +#endif + if (IS_ENABLED(CONFIG_BT_DEBUG_CONN)) { + struct bt_conn_tx *tx = k_fifo_get(&free_tx, K_NO_WAIT); + + if (tx) { + return tx; + } + + BT_WARN("Unable to get an immediate free conn_tx"); + } + + return k_fifo_get(&free_tx, K_FOREVER); +} + +int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data) +{ + struct bt_conn_tx *tx; + + BT_DBG("conn handle %u buf len %u cb %p user_data %p", conn->handle, + buf->len, cb, user_data); + + if (conn->state != BT_CONN_CONNECTED) { + BT_ERR("not connected!"); + net_buf_unref(buf); + return -ENOTCONN; + } + + if (cb) { + tx = conn_tx_alloc(); + if (!tx) { + BT_ERR("Unable to allocate TX context"); + net_buf_unref(buf); + return -ENOBUFS; + } + + /* Verify that we're still connected after blocking */ + if (conn->state != BT_CONN_CONNECTED) { + BT_WARN("Disconnected while allocating context"); + net_buf_unref(buf); + tx_free(tx); + return -ENOTCONN; + } + + tx->cb = cb; + tx->user_data = user_data; + tx->pending_no_cb = 0U; + + tx_data(buf)->tx = tx; + } else { + tx_data(buf)->tx = NULL; + } + + net_buf_put(&conn->tx_queue, buf); +#if defined(BFLB_BLE) + k_sem_give(&g_poll_sem); +#endif + return 0; +} + +static bool send_frag(struct bt_conn *conn, struct net_buf *buf, u8_t flags, + bool always_consume) +{ + struct bt_conn_tx *tx = tx_data(buf)->tx; + struct bt_hci_acl_hdr *hdr; + u32_t *pending_no_cb; + unsigned int key; + int err; + + BT_DBG("conn %p buf %p len %u flags 0x%02x", conn, buf, buf->len, + flags); + + /* Wait until the controller can accept ACL packets */ + k_sem_take(bt_conn_get_pkts(conn), K_FOREVER); + + /* Check for disconnection while waiting for pkts_sem */ + if (conn->state != BT_CONN_CONNECTED) { + goto fail; + } + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->handle = sys_cpu_to_le16(bt_acl_handle_pack(conn->handle, flags)); + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + + /* Add to pending, it must be done before bt_buf_set_type */ + key = irq_lock(); + if (tx) { + sys_slist_append(&conn->tx_pending, &tx->node); + } else { + struct bt_conn_tx *tail_tx; + + tail_tx = (void *)sys_slist_peek_tail(&conn->tx_pending); + if (tail_tx) { + pending_no_cb = &tail_tx->pending_no_cb; + } else { + pending_no_cb = &conn->pending_no_cb; + } + + (*pending_no_cb)++; + } + irq_unlock(key); + + bt_buf_set_type(buf, BT_BUF_ACL_OUT); + + err = bt_send(buf); + if (err) { + BT_ERR("Unable to send to driver (err %d)", err); + key = irq_lock(); + /* Roll back the pending TX info */ + if (tx) { + sys_slist_find_and_remove(&conn->tx_pending, &tx->node); + } else { + __ASSERT_NO_MSG(*pending_no_cb > 0); + (*pending_no_cb)--; + } + irq_unlock(key); + goto fail; + } + + return true; + +fail: + k_sem_give(bt_conn_get_pkts(conn)); + if (tx) { + tx_free(tx); + } + + if (always_consume) { + net_buf_unref(buf); + } + return false; +} + +static inline u16_t conn_mtu(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR || !bt_dev.le.mtu) { + return bt_dev.br.mtu; + } +#endif /* CONFIG_BT_BREDR */ + + return bt_dev.le.mtu; +} + +static struct net_buf *create_frag(struct bt_conn *conn, struct net_buf *buf) +{ + struct net_buf *frag; + u16_t frag_len; + +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + frag = bt_conn_create_pdu(&frag_pool, 0); +#else + frag = bt_conn_create_pdu(NULL, 0); +#endif + + if (conn->state != BT_CONN_CONNECTED) { + net_buf_unref(frag); + return NULL; + } + + /* Fragments never have a TX completion callback */ + tx_data(frag)->tx = NULL; + + frag_len = MIN(conn_mtu(conn), net_buf_tailroom(frag)); + + net_buf_add_mem(frag, buf->data, frag_len); + net_buf_pull(buf, frag_len); + + return frag; +} + +static bool send_buf(struct bt_conn *conn, struct net_buf *buf) +{ + struct net_buf *frag; + + BT_DBG("conn %p buf %p len %u", conn, buf, buf->len); + + /* Send directly if the packet fits the ACL MTU */ + if (buf->len <= conn_mtu(conn)) { + return send_frag(conn, buf, BT_ACL_START_NO_FLUSH, false); + } + + /* Create & enqueue first fragment */ + frag = create_frag(conn, buf); + if (!frag) { + return false; + } + + if (!send_frag(conn, frag, BT_ACL_START_NO_FLUSH, true)) { + return false; + } + + /* + * Send the fragments. For the last one simply use the original + * buffer (which works since we've used net_buf_pull on it. + */ + while (buf->len > conn_mtu(conn)) { + frag = create_frag(conn, buf); + if (!frag) { + return false; + } + + if (!send_frag(conn, frag, BT_ACL_CONT, true)) { + return false; + } + } + + return send_frag(conn, buf, BT_ACL_CONT, false); +} + +static struct k_poll_signal conn_change = + K_POLL_SIGNAL_INITIALIZER(conn_change); + +static void conn_cleanup(struct bt_conn *conn) +{ + struct net_buf *buf; + + /* Give back any allocated buffers */ + while ((buf = net_buf_get(&conn->tx_queue, K_NO_WAIT))) { + if (tx_data(buf)->tx) { + tx_free(tx_data(buf)->tx); + } + + net_buf_unref(buf); + } + + __ASSERT(sys_slist_is_empty(&conn->tx_pending), "Pending TX packets"); + __ASSERT_NO_MSG(conn->pending_no_cb == 0); + + bt_conn_reset_rx_state(conn); + + k_delayed_work_submit(&conn->update_work, K_NO_WAIT); + + #ifdef BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS + k_queue_free(&conn->tx_queue._queue); + // k_queue_free(&conn->tx_notify._queue); + conn->tx_queue._queue.hdl = NULL; + //conn->tx_notify._queue.hdl = NULL; + if(conn->update_work.timer.timer.hdl) + k_delayed_work_del_timer(&conn->update_work); + #endif +} + +int bt_conn_prepare_events(struct k_poll_event events[]) +{ + int i, ev_count = 0; + + BT_DBG(""); + + conn_change.signaled = 0U; + k_poll_event_init(&events[ev_count++], K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, &conn_change); + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + struct bt_conn *conn = &conns[i]; + + if (!atomic_get(&conn->ref)) { + continue; + } + + if (conn->state == BT_CONN_DISCONNECTED && + atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) { + conn_cleanup(conn); + continue; + } + + if (conn->state != BT_CONN_CONNECTED) { + continue; + } + + BT_DBG("Adding conn %p to poll list", conn); + + k_poll_event_init(&events[ev_count], + K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &conn->tx_queue); + events[ev_count++].tag = BT_EVENT_CONN_TX_QUEUE; + } + + return ev_count; +} + +void bt_conn_process_tx(struct bt_conn *conn) +{ + struct net_buf *buf; + + BT_DBG("conn %p", conn); + + if (conn->state == BT_CONN_DISCONNECTED && + atomic_test_and_clear_bit(conn->flags, BT_CONN_CLEANUP)) { + BT_DBG("handle %u disconnected - cleaning up", conn->handle); + conn_cleanup(conn); + return; + } + + /* Get next ACL packet for connection */ + buf = net_buf_get(&conn->tx_queue, K_NO_WAIT); + BT_ASSERT(buf); + if (!send_buf(conn, buf)) { + net_buf_unref(buf); + } +} + +struct bt_conn *bt_conn_add_le(u8_t id, const bt_addr_le_t *peer) +{ + struct bt_conn *conn = conn_new(); + + if (!conn) { + return NULL; + } + + conn->id = id; + bt_addr_le_copy(&conn->le.dst, peer); +#if defined(CONFIG_BT_SMP) + conn->sec_level = BT_SECURITY_L1; + conn->required_sec_level = BT_SECURITY_L1; +#endif /* CONFIG_BT_SMP */ + conn->type = BT_CONN_TYPE_LE; + conn->le.interval_min = BT_GAP_INIT_CONN_INT_MIN; + conn->le.interval_max = BT_GAP_INIT_CONN_INT_MAX; + + return conn; +} + +static void process_unack_tx(struct bt_conn *conn) +{ + /* Return any unacknowledged packets */ + while (1) { + struct bt_conn_tx *tx; + sys_snode_t *node; + unsigned int key; + + key = irq_lock(); + + if (conn->pending_no_cb) { + conn->pending_no_cb--; + irq_unlock(key); + k_sem_give(bt_conn_get_pkts(conn)); + continue; + } + + node = sys_slist_get(&conn->tx_pending); + irq_unlock(key); + + if (!node) { + break; + } + + tx = CONTAINER_OF(node, struct bt_conn_tx, node); + + key = irq_lock(); + conn->pending_no_cb = tx->pending_no_cb; + tx->pending_no_cb = 0U; + irq_unlock(key); + + tx_free(tx); + + k_sem_give(bt_conn_get_pkts(conn)); + } +} + +void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) +{ + bt_conn_state_t old_state; + + BT_DBG("%s -> %s", state2str(conn->state), state2str(state)); + + if (conn->state == state) { + BT_WARN("no transition"); + return; + } + + old_state = conn->state; + conn->state = state; + + /* Actions needed for exiting the old state */ + switch (old_state) { + case BT_CONN_DISCONNECTED: + /* Take a reference for the first state transition after + * bt_conn_add_le() and keep it until reaching DISCONNECTED + * again. + */ + bt_conn_ref(conn); + break; + case BT_CONN_CONNECT: + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->type == BT_CONN_TYPE_LE) { + k_delayed_work_cancel(&conn->update_work); + } + break; + default: + break; + } + + /* Actions needed for entering the new state */ + switch (conn->state) { + case BT_CONN_CONNECTED: + if (conn->type == BT_CONN_TYPE_SCO) { + /* TODO: Notify sco connected */ + break; + } + k_fifo_init(&conn->tx_queue, 20); + k_poll_signal_raise(&conn_change, 0); + + sys_slist_init(&conn->channels); + + bt_l2cap_connected(conn); + notify_connected(conn); + break; + case BT_CONN_DISCONNECTED: + if (conn->type == BT_CONN_TYPE_SCO) { + /* TODO: Notify sco disconnected */ + bt_conn_unref(conn); + break; + } + /* Notify disconnection and queue a dummy buffer to wake + * up and stop the tx thread for states where it was + * running. + */ + if (old_state == BT_CONN_CONNECTED || + old_state == BT_CONN_DISCONNECT) { + process_unack_tx(conn); + tx_notify(conn); + + /* Cancel Connection Update if it is pending */ + if (conn->type == BT_CONN_TYPE_LE) { + k_delayed_work_cancel(&conn->update_work); + } + + atomic_set_bit(conn->flags, BT_CONN_CLEANUP); + k_poll_signal_raise(&conn_change, 0); + /* The last ref will be dropped during cleanup */ + } else if (old_state == BT_CONN_CONNECT) { + /* conn->err will be set in this case */ + notify_connected(conn); + bt_conn_unref(conn); + } else if (old_state == BT_CONN_CONNECT_SCAN) { + /* this indicate LE Create Connection failed */ + if (conn->err) { + notify_connected(conn); + } + + bt_conn_unref(conn); + } else if (old_state == BT_CONN_CONNECT_DIR_ADV) { + /* this indicate Directed advertising stopped */ + if (conn->err) { + notify_connected(conn); + } + + bt_conn_unref(conn); + } + + break; + case BT_CONN_CONNECT_SCAN: + break; + case BT_CONN_CONNECT_DIR_ADV: + break; + case BT_CONN_CONNECT: + if (conn->type == BT_CONN_TYPE_SCO) { + break; + } + /* + * Timer is needed only for LE. For other link types controller + * will handle connection timeout. + */ + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->type == BT_CONN_TYPE_LE) { + k_delayed_work_submit(&conn->update_work, CONN_TIMEOUT); + } + + break; + case BT_CONN_DISCONNECT: + break; + default: + BT_WARN("no valid (%u) state was set", state); + + break; + } +} + +struct bt_conn *bt_conn_lookup_handle(u16_t handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + /* We only care about connections with a valid handle */ + if (conns[i].state != BT_CONN_CONNECTED && + conns[i].state != BT_CONN_DISCONNECT) { + continue; + } + + if (conns[i].handle == handle) { + return bt_conn_ref(&conns[i]); + } + } + +#if defined(CONFIG_BT_BREDR) + for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { + if (!atomic_get(&sco_conns[i].ref)) { + continue; + } + + /* We only care about connections with a valid handle */ + if (sco_conns[i].state != BT_CONN_CONNECTED && + sco_conns[i].state != BT_CONN_DISCONNECT) { + continue; + } + + if (sco_conns[i].handle == handle) { + return bt_conn_ref(&sco_conns[i]); + } + } +#endif + + return NULL; +} + +int bt_conn_addr_le_cmp(const struct bt_conn *conn, const bt_addr_le_t *peer) +{ + /* Check against conn dst address as it may be the identity address */ + if (!bt_addr_le_cmp(peer, &conn->le.dst)) { + return 0; + } + + /* Check against initial connection address */ + if (conn->role == BT_HCI_ROLE_MASTER) { + return bt_addr_le_cmp(peer, &conn->le.resp_addr); + } + + return bt_addr_le_cmp(peer, &conn->le.init_addr); +} + +struct bt_conn *bt_conn_lookup_addr_le(u8_t id, const bt_addr_le_t *peer) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_LE) { + continue; + } + + if (conns[i].id == id && + !bt_conn_addr_le_cmp(&conns[i], peer)) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, + const bt_conn_state_t state) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_LE) { + continue; + } + + if (peer && bt_conn_addr_le_cmp(&conns[i], peer)) { + continue; + } + + if (conns[i].state == state) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data), + void *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (!(conns[i].type & type)) { + continue; + } + + func(&conns[i], data); + } +#if defined(CONFIG_BT_BREDR) + if (type & BT_CONN_TYPE_SCO) { + for (i = 0; i < ARRAY_SIZE(sco_conns); i++) { + if (!atomic_get(&sco_conns[i].ref)) { + continue; + } + + func(&sco_conns[i], data); + } + } +#endif /* defined(CONFIG_BT_BREDR) */ +} + +static void disconnect_all(struct bt_conn *conn, void *data) +{ + u8_t *id = (u8_t *)data; + + if (conn->id == *id && conn->state == BT_CONN_CONNECTED) { + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } +} + +void bt_conn_disconnect_all(u8_t id) +{ + bt_conn_foreach(BT_CONN_TYPE_ALL, disconnect_all, &id); +} + +struct bt_conn *bt_conn_ref(struct bt_conn *conn) +{ + atomic_inc(&conn->ref); + + BT_DBG("handle %u ref %u", conn->handle, atomic_get(&conn->ref)); + + return conn; +} + +void bt_conn_unref(struct bt_conn *conn) +{ + atomic_dec(&conn->ref); + + BT_DBG("handle %u ref %u", conn->handle, atomic_get(&conn->ref)); +} + +const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn) +{ + return &conn->le.dst; +} + +int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info) +{ + info->type = conn->type; + info->role = conn->role; + info->id = conn->id; + + switch (conn->type) { + case BT_CONN_TYPE_LE: + info->le.dst = &conn->le.dst; + info->le.src = &bt_dev.id_addr[conn->id]; + if (conn->role == BT_HCI_ROLE_MASTER) { + info->le.local = &conn->le.init_addr; + info->le.remote = &conn->le.resp_addr; + } else { + info->le.local = &conn->le.resp_addr; + info->le.remote = &conn->le.init_addr; + } + info->le.interval = conn->le.interval; + info->le.latency = conn->le.latency; + info->le.timeout = conn->le.timeout; + return 0; +#if defined(CONFIG_BT_BREDR) + case BT_CONN_TYPE_BR: + info->br.dst = &conn->br.dst; + return 0; +#endif + } + + return -EINVAL; +} + +int bt_conn_get_remote_dev_info(struct bt_conn_info *info) +{ + int link_num = 0; + + for (int i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + bt_conn_get_info(&conns[i], &info[link_num]); + link_num ++; + } + + return link_num; +} + +static int bt_hci_disconnect(struct bt_conn *conn, u8_t reason) +{ + struct net_buf *buf; + struct bt_hci_cp_disconnect *disconn; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn)); + if (!buf) { + return -ENOBUFS; + } + + disconn = net_buf_add(buf, sizeof(*disconn)); + disconn->handle = sys_cpu_to_le16(conn->handle); + disconn->reason = reason; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_DISCONNECT, buf, NULL); + if (err) { + return err; + } + + bt_conn_set_state(conn, BT_CONN_DISCONNECT); + + return 0; +} + +#if defined(CONFIG_BT_STACK_PTS) +int pts_bt_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + + BT_DBG("conn %p features 0x%02x params (%d-%d %d %d)", conn, + conn->le.features[0], param->interval_min, + param->interval_max, param->latency, param->timeout); + + /* Check if there's a need to update conn params */ + if (conn->le.interval >= param->interval_min && + conn->le.interval <= param->interval_max && + conn->le.latency == param->latency && + conn->le.timeout == param->timeout) { + return -EALREADY; + } + + /* Cancel any pending update */ + k_delayed_work_cancel(&conn->update_work); + + return bt_l2cap_update_conn_param(conn, param); + +} +#endif +int bt_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + BT_DBG("conn %p features 0x%02x params (%d-%d %d %d)", conn, + conn->le.features[0], param->interval_min, + param->interval_max, param->latency, param->timeout); + + /* Check if there's a need to update conn params */ + if (conn->le.interval >= param->interval_min && + conn->le.interval <= param->interval_max && + conn->le.latency == param->latency && + conn->le.timeout == param->timeout) { + atomic_clear_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET); + return -EALREADY; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_CONN_ROLE_MASTER) { + return send_conn_le_param_update(conn, param); + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + /* if slave conn param update timer expired just send request */ + if (atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE)) { + return send_conn_le_param_update(conn, param); + } + + /* store new conn params to be used by update timer */ + conn->le.interval_min = param->interval_min; + conn->le.interval_max = param->interval_max; + conn->le.pending_latency = param->latency; + conn->le.pending_timeout = param->timeout; + atomic_set_bit(conn->flags, BT_CONN_SLAVE_PARAM_SET); + } + + return 0; +} + +int bt_conn_disconnect(struct bt_conn *conn, u8_t reason) +{ + /* Disconnection is initiated by us, so auto connection shall + * be disabled. Otherwise the passive scan would be enabled + * and we could send LE Create Connection as soon as the remote + * starts advertising. + */ +#if !defined(CONFIG_BT_WHITELIST) + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->type == BT_CONN_TYPE_LE) { + bt_le_set_auto_conn(&conn->le.dst, NULL); + } +#endif /* !defined(CONFIG_BT_WHITELIST) */ + + switch (conn->state) { + case BT_CONN_CONNECT_SCAN: + conn->err = reason; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + bt_le_scan_update(false); + } + return 0; + case BT_CONN_CONNECT_DIR_ADV: + conn->err = reason; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + /* User should unref connection object when receiving + * error in connection callback. + */ + return bt_le_adv_stop(); + } + return 0; + case BT_CONN_CONNECT: +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + return bt_hci_connect_br_cancel(conn); + } +#endif /* CONFIG_BT_BREDR */ + + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + k_delayed_work_cancel(&conn->update_work); + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, + NULL, NULL); + } + + return 0; + case BT_CONN_CONNECTED: + return bt_hci_disconnect(conn, reason); + case BT_CONN_DISCONNECT: + return 0; + case BT_CONN_DISCONNECTED: + default: + return -ENOTCONN; + } +} + +#if defined(CONFIG_BT_CENTRAL) +static void bt_conn_set_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + conn->le.interval_min = param->interval_min; + conn->le.interval_max = param->interval_max; + conn->le.latency = param->latency; + conn->le.timeout = param->timeout; + + #if defined(CONFIG_BT_STACK_PTS) + conn->le.own_adder_type = param->own_address_type; + #endif +} + +#if defined(CONFIG_BT_WHITELIST) +int bt_conn_create_auto_le(const struct bt_le_conn_param *param) +{ + struct bt_conn *conn; + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EINVAL; + } + + if (!bt_le_conn_params_valid(param)) { + return -EINVAL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EINVAL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_AUTO_CONN)) { + return -EALREADY; + } + + if (!bt_dev.le.wl_entries) { + return -EINVAL; + } + + /* Don't start initiator if we have general discovery procedure. */ + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT_SCAN); + if (conn) { + bt_conn_unref(conn); + return -EINVAL; + } + + /* Don't start initiator if we have direct discovery procedure. */ + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT); + if (conn) { + bt_conn_unref(conn); + return -EINVAL; + } + + err = bt_le_auto_conn(param); + if (err) { + BT_ERR("Failed to start whitelist scan"); + return err; + } + + return 0; +} + +int bt_conn_create_auto_stop(void) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EINVAL; + } + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_AUTO_CONN)) { + return -EINVAL; + } + + err = bt_le_auto_conn_cancel(); + if (err) { + BT_ERR("Failed to stop initiator"); + return err; + } + + return 0; +} +#endif /* defined(CONFIG_BT_WHITELIST) */ + +struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer, + const struct bt_le_conn_param *param) +{ + struct bt_conn *conn; + bt_addr_le_t dst; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return NULL; + } + + if (!bt_le_conn_params_valid(param)) { + return NULL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return NULL; + } + + if (IS_ENABLED(CONFIG_BT_WHITELIST) && + atomic_test_bit(bt_dev.flags, BT_DEV_AUTO_CONN)) { + return NULL; + } + + conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, peer); + if (conn) { + switch (conn->state) { + case BT_CONN_CONNECT_SCAN: + bt_conn_set_param_le(conn, param); + return conn; + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return conn; + case BT_CONN_DISCONNECTED: + BT_WARN("Found valid but disconnected conn object"); + goto start_scan; + default: + bt_conn_unref(conn); + return NULL; + } + } + + if (peer->type == BT_ADDR_LE_PUBLIC_ID || + peer->type == BT_ADDR_LE_RANDOM_ID) { + bt_addr_le_copy(&dst, peer); + dst.type -= BT_ADDR_LE_PUBLIC_ID; + } else { + bt_addr_le_copy(&dst, bt_lookup_id_addr(BT_ID_DEFAULT, peer)); + } + + /* Only default identity supported for now */ + conn = bt_conn_add_le(BT_ID_DEFAULT, &dst); + if (!conn) { + return NULL; + } + +start_scan: + bt_conn_set_param_le(conn, param); + + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + + bt_le_scan_update(true); + + return conn; +} + +#if !defined(CONFIG_BT_WHITELIST) +int bt_le_set_auto_conn(const bt_addr_le_t *addr, + const struct bt_le_conn_param *param) +{ + struct bt_conn *conn; + + if (param && !bt_le_conn_params_valid(param)) { + return -EINVAL; + } + + /* Only default identity is supported */ + conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr); + if (!conn) { + conn = bt_conn_add_le(BT_ID_DEFAULT, addr); + if (!conn) { + return -ENOMEM; + } + } + + if (param) { + bt_conn_set_param_le(conn, param); + + if (!atomic_test_and_set_bit(conn->flags, + BT_CONN_AUTO_CONNECT)) { + bt_conn_ref(conn); + } + } else { + if (atomic_test_and_clear_bit(conn->flags, + BT_CONN_AUTO_CONNECT)) { + bt_conn_unref(conn); + if (conn->state == BT_CONN_CONNECT_SCAN) { + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + } + } + } + + if (conn->state == BT_CONN_DISCONNECTED && + atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + if (param) { + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + } + bt_le_scan_update(false); + } + + bt_conn_unref(conn); + + return 0; +} +#endif /* !defined(CONFIG_BT_WHITELIST) */ +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) +struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer, + const struct bt_le_adv_param *param) +{ + int err; + struct bt_conn *conn; + struct bt_le_adv_param param_int; + + memcpy(¶m_int, param, sizeof(param_int)); + param_int.options |= (BT_LE_ADV_OPT_CONNECTABLE | + BT_LE_ADV_OPT_ONE_TIME); + + conn = bt_conn_lookup_addr_le(param->id, peer); + if (conn) { + switch (conn->state) { + case BT_CONN_CONNECT_DIR_ADV: + /* Handle the case when advertising is stopped with + * bt_le_adv_stop function + */ + err = bt_le_adv_start_internal(¶m_int, NULL, 0, + NULL, 0, peer); + if (err && (err != -EALREADY)) { + BT_WARN("Directed advertising could not be" + " started: %d", err); + bt_conn_unref(conn); + return NULL; + } + __attribute__((fallthrough)); + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return conn; + case BT_CONN_DISCONNECTED: + BT_WARN("Found valid but disconnected conn object"); + goto start_adv; + default: + bt_conn_unref(conn); + return NULL; + } + } + + conn = bt_conn_add_le(param->id, peer); + if (!conn) { + return NULL; + } + +start_adv: + bt_conn_set_state(conn, BT_CONN_CONNECT_DIR_ADV); + + err = bt_le_adv_start_internal(¶m_int, NULL, 0, NULL, 0, peer); + if (err) { + BT_WARN("Directed advertising could not be started: %d", err); + + bt_conn_unref(conn); + return NULL; + } + + return conn; +} +#endif /* CONFIG_BT_PERIPHERAL */ + +int bt_conn_le_conn_update(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + struct hci_cp_le_conn_update *conn_update; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE, + sizeof(*conn_update)); + if (!buf) { + return -ENOBUFS; + } + + conn_update = net_buf_add(buf, sizeof(*conn_update)); + (void)memset(conn_update, 0, sizeof(*conn_update)); + conn_update->handle = sys_cpu_to_le16(conn->handle); + conn_update->conn_interval_min = sys_cpu_to_le16(param->interval_min); + conn_update->conn_interval_max = sys_cpu_to_le16(param->interval_max); + conn_update->conn_latency = sys_cpu_to_le16(param->latency); + conn_update->supervision_timeout = sys_cpu_to_le16(param->timeout); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CONN_UPDATE, buf, NULL); +} + +struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, s32_t timeout) +{ + struct net_buf *buf; + + /* + * PDU must not be allocated from ISR as we block with 'K_FOREVER' + * during the allocation + */ + __ASSERT_NO_MSG(!k_is_in_isr()); + + if (!pool) { + pool = &acl_tx_pool; + } + + if (IS_ENABLED(CONFIG_BT_DEBUG_CONN)) { + buf = net_buf_alloc(pool, K_NO_WAIT); + if (!buf) { + BT_WARN("Unable to allocate buffer with K_NO_WAIT"); + buf = net_buf_alloc(pool, timeout); + } + } else { + buf = net_buf_alloc(pool, timeout); + } + + if (!buf) { + BT_WARN("Unable to allocate buffer: timeout %d", timeout); + return NULL; + } + + reserve += sizeof(struct bt_hci_acl_hdr) + BT_BUF_RESERVE; + net_buf_reserve(buf, reserve); + + return buf; +} + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb) +{ + if (!cb) { + bt_auth = NULL; + return 0; + } + + if (bt_auth) { + return -EALREADY; + } + + /* The cancel callback must always be provided if the app provides + * interactive callbacks. + */ + if (!cb->cancel && + (cb->passkey_display || cb->passkey_entry || cb->passkey_confirm || +#if defined(CONFIG_BT_BREDR) + cb->pincode_entry || +#endif + cb->pairing_confirm)) { + return -EINVAL; + } + + bt_auth = cb; + return 0; +} + +int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey) +{ + if (!bt_auth) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_SMP) && conn->type == BT_CONN_TYPE_LE) { + bt_smp_auth_passkey_entry(conn, passkey); + return 0; + } + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + /* User entered passkey, reset user state. */ + if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) { + return -EPERM; + } + + if (conn->br.pairing_method == PASSKEY_INPUT) { + return ssp_passkey_reply(conn, passkey); + } + } +#endif /* CONFIG_BT_BREDR */ + + return -EINVAL; +} + +int bt_conn_auth_passkey_confirm(struct bt_conn *conn) +{ + if (!bt_auth) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_SMP) && + conn->type == BT_CONN_TYPE_LE) { + return bt_smp_auth_passkey_confirm(conn); + } + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + /* Allow user confirm passkey value, then reset user state. */ + if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) { + return -EPERM; + } + + return ssp_confirm_reply(conn); + } +#endif /* CONFIG_BT_BREDR */ + + return -EINVAL; +} + +int bt_conn_auth_cancel(struct bt_conn *conn) +{ + if (!bt_auth) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_SMP) && conn->type == BT_CONN_TYPE_LE) { + return bt_smp_auth_cancel(conn); + } + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + /* Allow user cancel authentication, then reset user state. */ + if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) { + return -EPERM; + } + + switch (conn->br.pairing_method) { + case JUST_WORKS: + case PASSKEY_CONFIRM: + return ssp_confirm_neg_reply(conn); + case PASSKEY_INPUT: + return ssp_passkey_neg_reply(conn); + case PASSKEY_DISPLAY: + return bt_conn_disconnect(conn, + BT_HCI_ERR_AUTH_FAIL); + case LEGACY: + return pin_code_neg_reply(&conn->br.dst); + default: + break; + } + } +#endif /* CONFIG_BT_BREDR */ + + return -EINVAL; +} + +int bt_conn_auth_pairing_confirm(struct bt_conn *conn) +{ + if (!bt_auth) { + return -EINVAL; + } + + switch (conn->type) { +#if defined(CONFIG_BT_SMP) + case BT_CONN_TYPE_LE: + return bt_smp_auth_pairing_confirm(conn); +#endif /* CONFIG_BT_SMP */ +#if defined(CONFIG_BT_BREDR) + case BT_CONN_TYPE_BR: + return ssp_confirm_reply(conn); +#endif /* CONFIG_BT_BREDR */ + default: + return -EINVAL; + } +} +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + +u8_t bt_conn_index(struct bt_conn *conn) +{ + u8_t index = conn - conns; + + __ASSERT(index < CONFIG_BT_MAX_CONN, "Invalid bt_conn pointer"); + return index; +} + +struct bt_conn *bt_conn_lookup_id(u8_t id) +{ + struct bt_conn *conn; + + if (id >= ARRAY_SIZE(conns)) { + return NULL; + } + + conn = &conns[id]; + + if (!atomic_get(&conn->ref)) { + return NULL; + } + + return bt_conn_ref(conn); +} + +extern bool queue_inited; +int bt_conn_init(void) +{ + #if defined(CONFIG_BT_SMP) + int err; + #endif + int i; + +#if defined(BFLB_BLE) +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + net_buf_init(&acl_tx_pool, CONFIG_BT_L2CAP_TX_BUF_COUNT, BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU), NULL); +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + net_buf_init(&frag_pool, CONFIG_BT_L2CAP_TX_FRAG_COUNT, FRAG_SIZE, NULL); +#endif +#else //BFLB_DYNAMIC_ALLOC_MEM + struct net_buf_pool num_complete_pool; + struct net_buf_pool acl_tx_pool; +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + struct net_buf_pool frag_pool; +#endif +#endif//BFLB_DYNAMIC_ALLOC_MEM + if(queue_inited == false) + k_lifo_init(&acl_tx_pool.free, CONFIG_BT_L2CAP_TX_BUF_COUNT); + k_fifo_init(&free_tx, 20); +#endif + for (i = 0; i < ARRAY_SIZE(conn_tx); i++) { + k_fifo_put(&free_tx, &conn_tx[i]); + } + + bt_att_init(); + + #if defined(CONFIG_BT_SMP) + err = bt_smp_init(); + if (err) { + return err; + } + #endif + + bt_l2cap_init(); + + /* Initialize background scan */ + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + for (i = 0; i < ARRAY_SIZE(conns); i++) { + struct bt_conn *conn = &conns[i]; + + if (!atomic_get(&conn->ref)) { + continue; + } + + if (atomic_test_bit(conn->flags, + BT_CONN_AUTO_CONNECT)) { + /* Only the default identity is supported */ + conn->id = BT_ID_DEFAULT; + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + } + } + } + + return 0; +} diff --git a/components/ble/ble_stack/host/conn_internal.h b/components/ble/ble_stack/host/conn_internal.h new file mode 100644 index 00000000..399b684a --- /dev/null +++ b/components/ble/ble_stack/host/conn_internal.h @@ -0,0 +1,344 @@ +/** @file + * @brief Internal APIs for Bluetooth connection handling. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +typedef enum __packed { + BT_CONN_DISCONNECTED, + BT_CONN_CONNECT_SCAN, + BT_CONN_CONNECT_DIR_ADV, + BT_CONN_CONNECT, + BT_CONN_CONNECTED, + BT_CONN_DISCONNECT, +} bt_conn_state_t; + +/* bt_conn flags: the flags defined here represent connection parameters */ +enum { + BT_CONN_AUTO_CONNECT, + BT_CONN_BR_LEGACY_SECURE, /* 16 digits legacy PIN tracker */ + BT_CONN_USER, /* user I/O when pairing */ + BT_CONN_BR_PAIRING, /* BR connection in pairing context */ + BT_CONN_BR_NOBOND, /* SSP no bond pairing tracker */ + BT_CONN_BR_PAIRING_INITIATOR, /* local host starts authentication */ + BT_CONN_CLEANUP, /* Disconnected, pending cleanup */ + BT_CONN_AUTO_PHY_UPDATE, /* Auto-update PHY */ + BT_CONN_SLAVE_PARAM_UPDATE, /* If slave param update timer fired */ + BT_CONN_SLAVE_PARAM_SET, /* If slave param were set from app */ + BT_CONN_SLAVE_PARAM_L2CAP, /* If should force L2CAP for CPUP */ + BT_CONN_FORCE_PAIR, /* Pairing even with existing keys. */ + + BT_CONN_AUTO_PHY_COMPLETE, /* Auto-initiated PHY procedure done */ + BT_CONN_AUTO_FEATURE_EXCH, /* Auto-initiated LE Feat done */ + BT_CONN_AUTO_VERSION_INFO, /* Auto-initiated LE version done */ + + /* Total number of flags - must be at the end of the enum */ + BT_CONN_NUM_FLAGS, +}; + +struct bt_conn_le { + bt_addr_le_t dst; + + bt_addr_le_t init_addr; + bt_addr_le_t resp_addr; + + u16_t interval; + u16_t interval_min; + u16_t interval_max; + + u16_t latency; + u16_t timeout; + u16_t pending_latency; + u16_t pending_timeout; + + u8_t features[8]; + + struct bt_keys *keys; + +#if defined(CONFIG_BT_STACK_PTS) + u8_t own_adder_type; +#endif + +}; + +#if defined(CONFIG_BT_BREDR) +/* For now reserve space for 2 pages of LMP remote features */ +#define LMP_MAX_PAGES 2 + +struct bt_conn_br { + bt_addr_t dst; + u8_t remote_io_capa; + u8_t remote_auth; + u8_t pairing_method; + /* remote LMP features pages per 8 bytes each */ + u8_t features[LMP_MAX_PAGES][8]; + + struct bt_keys_link_key *link_key; +}; + +struct bt_conn_sco { + /* Reference to ACL Connection */ + struct bt_conn *acl; + u16_t pkt_type; +}; +#endif + +struct bt_conn_iso { + /* Reference to ACL Connection */ + struct bt_conn *acl; + /* CIG ID */ + uint8_t cig_id; + /* CIS ID */ + uint8_t cis_id; +}; + +typedef void (*bt_conn_tx_cb_t)(struct bt_conn *conn, void *user_data); + +struct bt_conn_tx { + sys_snode_t node; + + bt_conn_tx_cb_t cb; + void *user_data; + + /* Number of pending packets without a callback after this one */ + u32_t pending_no_cb; +}; + +struct bt_conn { + u16_t handle; + u8_t type; + u8_t role; + + ATOMIC_DEFINE(flags, BT_CONN_NUM_FLAGS); + + /* Which local identity address this connection uses */ + u8_t id; + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) + bt_security_t sec_level; + bt_security_t required_sec_level; + u8_t encrypt; +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + + /* Connection error or reason for disconnect */ + u8_t err; + + bt_conn_state_t state; + + u16_t rx_len; + struct net_buf *rx; + + /* Sent but not acknowledged TX packets with a callback */ + sys_slist_t tx_pending; + /* Sent but not acknowledged TX packets without a callback before + * the next packet (if any) in tx_pending. + */ + u32_t pending_no_cb; + + /* Completed TX for which we need to call the callback */ + sys_slist_t tx_complete; + struct k_work tx_complete_work; + + + /* Queue for outgoing ACL data */ + struct k_fifo tx_queue; + + /* Active L2CAP channels */ + sys_slist_t channels; + + atomic_t ref; + + /* Delayed work for connection update and other deferred tasks */ + struct k_delayed_work update_work; + + union { + struct bt_conn_le le; +#if defined(CONFIG_BT_BREDR) + struct bt_conn_br br; + struct bt_conn_sco sco; +#endif +#if defined(CONFIG_BT_AUDIO) + struct bt_conn_iso iso; +#endif + }; + +#if defined(CONFIG_BT_REMOTE_VERSION) + struct bt_conn_rv { + u8_t version; + u16_t manufacturer; + u16_t subversion; + } rv; +#endif +}; + +void bt_conn_reset_rx_state(struct bt_conn *conn); + +/* Process incoming data for a connection */ +void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, u8_t flags); + +/* Send data over a connection */ +int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data); + +static inline int bt_conn_send(struct bt_conn *conn, struct net_buf *buf) +{ + return bt_conn_send_cb(conn, buf, NULL, NULL); +} + +/* Add a new LE connection */ +struct bt_conn *bt_conn_add_le(u8_t id, const bt_addr_le_t *peer); + +/** Connection parameters for ISO connections */ +struct bt_iso_create_param { + uint8_t id; + uint8_t num_conns; + struct bt_conn **conns; + struct bt_iso_chan **chans; +}; + +/* Bind ISO connections parameters */ +int bt_conn_bind_iso(struct bt_iso_create_param *param); + +/* Connect ISO connections */ +int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns); + +/* Add a new ISO connection */ +struct bt_conn *bt_conn_add_iso(struct bt_conn *acl); + +/* Cleanup ISO references */ +void bt_iso_cleanup(struct bt_conn *iso_conn); + +/* Allocate new ISO connection */ +struct bt_conn *iso_conn_new(struct bt_conn *conns, size_t size); + +/* Add a new BR/EDR connection */ +struct bt_conn *bt_conn_add_br(const bt_addr_t *peer); + +/* Add a new SCO connection */ +struct bt_conn *bt_conn_add_sco(const bt_addr_t *peer, int link_type); + +/* Cleanup SCO references */ +void bt_sco_cleanup(struct bt_conn *sco_conn); + +/* Look up an existing sco connection by BT address */ +struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer); + +/* Look up an existing connection by BT address */ +struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); + +void bt_conn_pin_code_req(struct bt_conn *conn); +u8_t bt_conn_get_io_capa(void); +u8_t bt_conn_ssp_get_auth(const struct bt_conn *conn); +void bt_conn_ssp_auth(struct bt_conn *conn, u32_t passkey); +void bt_conn_ssp_auth_complete(struct bt_conn *conn, u8_t status); + +void bt_conn_disconnect_all(u8_t id); + +/* Look up an existing connection */ +struct bt_conn *bt_conn_lookup_handle(u16_t handle); + +/* Compare an address with bt_conn destination address */ +int bt_conn_addr_le_cmp(const struct bt_conn *conn, const bt_addr_le_t *peer); + + +/* Helpers for identifying & looking up connections based on the the index to + * the connection list. This is useful for O(1) lookups, but can't be used + * e.g. as the handle since that's assigned to us by the controller. + */ +#define BT_CONN_ID_INVALID 0xff +struct bt_conn *bt_conn_lookup_id(u8_t id); + +/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection + * with the specific state + */ +struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, + const bt_conn_state_t state); + +/* Set connection object in certain state and perform action related to state */ +void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state); + +int bt_conn_le_conn_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +void notify_remote_info(struct bt_conn *conn); + +void notify_le_param_updated(struct bt_conn *conn); + +bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); + +#if defined(CONFIG_BT_SMP) +/* rand and ediv should be in BT order */ +int bt_conn_le_start_encryption(struct bt_conn *conn, u8_t rand[8], + u8_t ediv[2], const u8_t *ltk, size_t len); + +/* Notify higher layers that RPA was resolved */ +void bt_conn_identity_resolved(struct bt_conn *conn); +#endif /* CONFIG_BT_SMP */ + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +/* Notify higher layers that connection security changed */ +void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err); +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + +/* Prepare a PDU to be sent over a connection */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, s32_t timeout, + const char *func, int line); +#define bt_conn_create_pdu_timeout(_pool, _reserve, _timeout) \ + bt_conn_create_pdu_timeout_debug(_pool, _reserve, _timeout, \ + __func__, __LINE__) + +#define bt_conn_create_pdu(_pool, _reserve) \ + bt_conn_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \ + __func__, __line__) +#else +struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, s32_t timeout); + +#define bt_conn_create_pdu(_pool, _reserve) \ + bt_conn_create_pdu_timeout(_pool, _reserve, K_FOREVER) +#endif + +/* Prepare a PDU to be sent over a connection */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_conn_create_frag_timeout_debug(size_t reserve, s32_t timeout, + const char *func, int line); + +#define bt_conn_create_frag_timeout(_reserve, _timeout) \ + bt_conn_create_frag_timeout_debug(_reserve, _timeout, \ + __func__, __LINE__) + +#define bt_conn_create_frag(_reserve) \ + bt_conn_create_frag_timeout_debug(_reserve, K_FOREVER, \ + __func__, __LINE__) +#else +struct net_buf *bt_conn_create_frag_timeout(size_t reserve, s32_t timeout); + +#define bt_conn_create_frag(_reserve) \ + bt_conn_create_frag_timeout(_reserve, K_FOREVER) +#endif + +/* Initialize connection management */ +int bt_conn_init(void); + +/* Selects based on connecton type right semaphore for ACL packets */ +struct k_sem *bt_conn_get_pkts(struct bt_conn *conn); + +/* k_poll related helpers for the TX thread */ +int bt_conn_prepare_events(struct k_poll_event events[]); +void bt_conn_process_tx(struct bt_conn *conn); + +#if defined(BFLB_BLE) +/** @brief Get connection handle for a connection. + * + * @param conn Connection object. + * @param conn_handle Place to store the Connection handle. + * + * @return 0 on success or negative error value on failure. + */ +int bt_hci_get_conn_handle(const struct bt_conn *conn, u16_t *conn_handle); +#endif diff --git a/components/ble/ble_stack/host/crypto.c b/components/ble/ble_stack/host/crypto.c new file mode 100644 index 00000000..30b14fbf --- /dev/null +++ b/components/ble/ble_stack/host/crypto.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#define LOG_MODULE_NAME bt_crypto +#include "log.h" + +#include "hci_core.h" + +static struct tc_hmac_prng_struct prng; + +static int prng_reseed(struct tc_hmac_prng_struct *h) +{ + u8_t seed[32]; + s64_t extra; + int ret, i; + + for (i = 0; i < (sizeof(seed) / 8); i++) { + struct bt_hci_rp_le_rand *rp; + struct net_buf *rsp; + + ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp); + if (ret) { + return ret; + } + + rp = (void *)rsp->data; + memcpy(&seed[i * 8], rp->rand, 8); + + net_buf_unref(rsp); + } + + extra = k_uptime_get(); + + ret = tc_hmac_prng_reseed(h, seed, sizeof(seed), (u8_t *)&extra, + sizeof(extra)); + if (ret == TC_CRYPTO_FAIL) { + BT_ERR("Failed to re-seed PRNG"); + return -EIO; + } + + return 0; +} + +int prng_init(void) +{ + struct bt_hci_rp_le_rand *rp; + struct net_buf *rsp; + int ret; + + /* Check first that HCI_LE_Rand is supported */ + if (!BT_CMD_TEST(bt_dev.supported_commands, 27, 7)) { + return -ENOTSUP; + } + + ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp); + if (ret) { + return ret; + } + + rp = (void *)rsp->data; + + ret = tc_hmac_prng_init(&prng, rp->rand, sizeof(rp->rand)); + + net_buf_unref(rsp); + + if (ret == TC_CRYPTO_FAIL) { + BT_ERR("Failed to initialize PRNG"); + return -EIO; + } + + /* re-seed is needed after init */ + return prng_reseed(&prng); +} + +int bt_rand(void *buf, size_t len) +{ +#if !defined(CONFIG_BT_GEN_RANDOM_BY_SW) + k_get_random_byte_array(buf, len); + return 0; +#else + int ret; + ret = tc_hmac_prng_generate(buf, len, &prng); + if (ret == TC_HMAC_PRNG_RESEED_REQ) { + ret = prng_reseed(&prng); + if (ret) { + return ret; + } + + ret = tc_hmac_prng_generate(buf, len, &prng); + } + + if (ret == TC_CRYPTO_SUCCESS) { + return 0; + } + + return -EIO; +#endif +} + +int bt_encrypt_le(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]) +{ + struct tc_aes_key_sched_struct s; + u8_t tmp[16]; + + BT_DBG("key %s", bt_hex(key, 16)); + BT_DBG("plaintext %s", bt_hex(plaintext, 16)); + + sys_memcpy_swap(tmp, key, 16); + + if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + sys_memcpy_swap(tmp, plaintext, 16); + + if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + sys_mem_swap(enc_data, 16); + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + + return 0; +} + +int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]) +{ + struct tc_aes_key_sched_struct s; + + BT_DBG("key %s", bt_hex(key, 16)); + BT_DBG("plaintext %s", bt_hex(plaintext, 16)); + + if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) { + return -EINVAL; + } + + BT_DBG("enc_data %s", bt_hex(enc_data, 16)); + + return 0; +} diff --git a/components/ble/ble_stack/host/crypto.h b/components/ble/ble_stack/host/crypto.h new file mode 100644 index 00000000..7b3aa0f7 --- /dev/null +++ b/components/ble/ble_stack/host/crypto.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2016-2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +int prng_init(void); diff --git a/components/ble/ble_stack/host/ecc.h b/components/ble/ble_stack/host/ecc.h new file mode 100644 index 00000000..05538101 --- /dev/null +++ b/components/ble/ble_stack/host/ecc.h @@ -0,0 +1,63 @@ +/* ecc.h - ECDH helpers */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* @brief Container for public key callback */ +struct bt_pub_key_cb { + /** @brief Callback type for Public Key generation. + * + * Used to notify of the local public key or that the local key is not + * available (either because of a failure to read it or because it is + * being regenerated). + * + * @param key The local public key, or NULL in case of no key. + */ + void (*func)(const u8_t key[64]); + + struct bt_pub_key_cb *_next; +}; + +/* @brief Generate a new Public Key. + * + * Generate a new ECC Public Key. The callback will persist even after the + * key has been generated, and will be used to notify of new generation + * processes (NULL as key). + * + * @param cb Callback to notify the new key, or NULL to request an update + * without registering any new callback. + * + * @return Zero on success or negative error code otherwise + */ +int bt_pub_key_gen(struct bt_pub_key_cb *cb); + +/* @brief Get the current Public Key. + * + * Get the current ECC Public Key. + * + * @return Current key, or NULL if not available. + */ +const u8_t *bt_pub_key_get(void); + +/* @typedef bt_dh_key_cb_t + * @brief Callback type for DH Key calculation. + * + * Used to notify of the calculated DH Key. + * + * @param key The DH Key, or NULL in case of failure. + */ +typedef void (*bt_dh_key_cb_t)(const u8_t key[32]); + +/* @brief Calculate a DH Key from a remote Public Key. + * + * Calculate a DH Key from the remote Public Key. + * + * @param remote_pk Remote Public Key. + * @param cb Callback to notify the calculated key. + * + * @return Zero on success or negative error code otherwise + */ +int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb); diff --git a/components/ble/ble_stack/host/gatt.c b/components/ble/ble_stack/host/gatt.c new file mode 100644 index 00000000..11a10527 --- /dev/null +++ b/components/ble/ble_stack/host/gatt.c @@ -0,0 +1,4683 @@ +/* gatt.c - Generic Attribute Profile handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_BT_GATT_CACHING) +#include +#include +#include +#include +#include +#endif /* CONFIG_BT_GATT_CACHING */ +#include +#include +#include +#include +#include +#if defined(BFLB_BLE) +#include "config.h" +#include +#endif + +#if defined(CONFIG_BT_STACK_PTS) +extern u8_t event_flag; +#endif + +#ifdef BT_DBG_ENABLED +#undef BT_DBG_ENABLED +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_GATT) +#else +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_GATT) +#endif + +#define LOG_MODULE_NAME bt_gatt +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "keys.h" +#include "l2cap_internal.h" +#include "att_internal.h" +#include "smp.h" +#include "settings.h" +#include "gatt_internal.h" + +#define SC_TIMEOUT K_MSEC(10) +#define CCC_STORE_DELAY K_SECONDS(1) + +#define DB_HASH_TIMEOUT K_MSEC(10) + +static u16_t last_static_handle; + +/* Persistent storage format for GATT CCC */ +struct ccc_store { + u16_t handle; + u16_t value; +}; + +#if defined(CONFIG_BT_GATT_CLIENT) +static sys_slist_t subscriptions; +#endif /* CONFIG_BT_GATT_CLIENT */ + +static const u16_t gap_appearance = CONFIG_BT_DEVICE_APPEARANCE; + +#if defined(CONFIG_BT_GATT_DYNAMIC_DB) +static sys_slist_t db; +#endif /* CONFIG_BT_GATT_DYNAMIC_DB */ + +static atomic_t init; + +#if defined(BFLB_BLE_MTU_CHANGE_CB) +bt_gatt_mtu_changed_cb_t gatt_mtu_changed_cb; +#endif + +static ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + const char *name = bt_get_name(); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, name, + strlen(name)); +} + +#if defined(CONFIG_BT_DEVICE_NAME_GATT_WRITABLE) + +static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, u16_t len, u16_t offset, + u8_t flags) +{ + char value[CONFIG_BT_DEVICE_NAME_MAX] = {}; + + if (offset) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (len >= sizeof(value)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + memcpy(value, buf, len); + + bt_set_name(value); + + return len; +} + +#endif /* CONFIG_BT_DEVICE_NAME_GATT_WRITABLE */ + +static ssize_t read_appearance(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + u16_t appearance = sys_cpu_to_le16(gap_appearance); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance, + sizeof(appearance)); +} + +#if defined (CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS) +/* This checks if the range entered is valid */ +BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_MIN_INT > 3200 && + CONFIG_BT_PERIPHERAL_PREF_MIN_INT < 0xffff)); +BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_MAX_INT > 3200 && + CONFIG_BT_PERIPHERAL_PREF_MAX_INT < 0xffff)); +BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_TIMEOUT > 3200 && + CONFIG_BT_PERIPHERAL_PREF_TIMEOUT < 0xffff)); +BUILD_ASSERT((CONFIG_BT_PERIPHERAL_PREF_MIN_INT == 0xffff) || + (CONFIG_BT_PERIPHERAL_PREF_MIN_INT <= + CONFIG_BT_PERIPHERAL_PREF_MAX_INT)); + +static ssize_t read_ppcp(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct __packed { + u16_t min_int; + u16_t max_int; + u16_t latency; + u16_t timeout; + } ppcp; + + ppcp.min_int = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_MIN_INT); + ppcp.max_int = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_MAX_INT); + ppcp.latency = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY); + ppcp.timeout = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_TIMEOUT); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &ppcp, + sizeof(ppcp)); +} +#endif + +#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_PRIVACY) +static ssize_t read_central_addr_res(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + u8_t central_addr_res = BT_GATT_CENTRAL_ADDR_RES_SUPP; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + ¢ral_addr_res, sizeof(central_addr_res)); +} +#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_PRIVACY */ + +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +static struct bt_gatt_attr gap_attrs[] = { +#else +BT_GATT_SERVICE_DEFINE(_2_gap_svc, +#endif + BT_GATT_PRIMARY_SERVICE(BT_UUID_GAP), + +#if defined(CONFIG_BT_DEVICE_NAME_GATT_WRITABLE) + /* Require pairing for writes to device name */ + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_name, write_name, bt_dev.name), +#else + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME, BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, read_name, NULL, NULL), +#endif /* CONFIG_BT_DEVICE_NAME_GATT_WRITABLE */ + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_APPEARANCE, BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, read_appearance, NULL, NULL), +#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_PRIVACY) + BT_GATT_CHARACTERISTIC(BT_UUID_CENTRAL_ADDR_RES, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_central_addr_res, NULL, NULL), +#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_PRIVACY */ +#if defined(CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS) + BT_GATT_CHARACTERISTIC(BT_UUID_GAP_PPCP, BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, read_ppcp, NULL, NULL), +#endif +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +}; +#else +); +#endif + +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +static struct bt_gatt_service gap_svc = BT_GATT_SERVICE(gap_attrs); +#endif + +struct sc_data { + u16_t start; + u16_t end; +} __packed; + +struct gatt_sc_cfg { + u8_t id; + bt_addr_le_t peer; + struct { + u16_t start; + u16_t end; + } data; +}; + +#define SC_CFG_MAX (CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN) +static struct gatt_sc_cfg sc_cfg[SC_CFG_MAX]; +BUILD_ASSERT(sizeof(struct sc_data) == sizeof(sc_cfg[0].data)); + +static struct gatt_sc_cfg *find_sc_cfg(u8_t id, bt_addr_le_t *addr) +{ + BT_DBG("id: %u, addr: %s", id, bt_addr_le_str(addr)); + + for (size_t i = 0; i < ARRAY_SIZE(sc_cfg); i++) { + if (id == sc_cfg[i].id && + !bt_addr_le_cmp(&sc_cfg[i].peer, addr)) { + return &sc_cfg[i]; + } + } + + return NULL; +} + +static void sc_store(struct gatt_sc_cfg *cfg) +{ + char key[BT_SETTINGS_KEY_MAX]; + int err; + + if (cfg->id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), cfg->id); + bt_settings_encode_key(key, sizeof(key), "sc", + &cfg->peer, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "sc", + &cfg->peer, NULL); + } + + err = settings_save_one(key, (u8_t *)&cfg->data, sizeof(cfg->data)); + if (err) { + BT_ERR("failed to store SC (err %d)", err); + return; + } + + BT_DBG("stored SC for %s (%s, 0x%04x-0x%04x)", + bt_addr_le_str(&cfg->peer), log_strdup(key), cfg->data.start, + cfg->data.end); +} + +static void sc_clear(struct gatt_sc_cfg *cfg) +{ + BT_DBG("peer %s", bt_addr_le_str(&cfg->peer)); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bool modified = false; + + if (cfg->data.start || cfg->data.end) { + modified = true; + } + + if (modified && bt_addr_le_is_bonded(cfg->id, &cfg->peer)) { + char key[BT_SETTINGS_KEY_MAX]; + int err; + + if (cfg->id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), cfg->id); + bt_settings_encode_key(key, sizeof(key), "sc", + &cfg->peer, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "sc", + &cfg->peer, NULL); + } + + err = settings_delete(key); + if (err) { + BT_ERR("failed to delete SC (err %d)", err); + } else { + BT_DBG("deleted SC for %s (%s)", + bt_addr_le_str(&cfg->peer), + log_strdup(key)); + } + } + } + + memset(cfg, 0, sizeof(*cfg)); +} + +static void sc_reset(struct gatt_sc_cfg *cfg) +{ + BT_DBG("peer %s", bt_addr_le_str(&cfg->peer)); + + memset(&cfg->data, 0, sizeof(cfg->data)); + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + sc_store(cfg); + } +} + +static bool update_range(u16_t *start, u16_t *end, u16_t new_start, + u16_t new_end) +{ + BT_DBG("start 0x%04x end 0x%04x new_start 0x%04x new_end 0x%04x", + *start, *end, new_start, new_end); + + /* Check if inside existing range */ + if (new_start >= *start && new_end <= *end) { + return false; + } + + /* Update range */ + if (*start > new_start) { + *start = new_start; + } + + if (*end < new_end) { + *end = new_end; + } + + return true; +} + +static void sc_save(u8_t id, bt_addr_le_t *peer, u16_t start, u16_t end) +{ + struct gatt_sc_cfg *cfg; + bool modified = false; + + BT_DBG("peer %s start 0x%04x end 0x%04x", bt_addr_le_str(peer), start, + end); + + cfg = find_sc_cfg(id, peer); + if (!cfg) { + /* Find and initialize a free sc_cfg entry */ + cfg = find_sc_cfg(BT_ID_DEFAULT, BT_ADDR_LE_ANY); + if (!cfg) { + BT_ERR("unable to save SC: no cfg left"); + return; + } + + cfg->id = id; + bt_addr_le_copy(&cfg->peer, peer); + } + + /* Check if there is any change stored */ + if (!(cfg->data.start || cfg->data.end)) { + cfg->data.start = start; + cfg->data.end = end; + modified = true; + goto done; + } + + modified = update_range(&cfg->data.start, &cfg->data.end, start, end); + +done: + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + modified && bt_addr_le_is_bonded(cfg->id, &cfg->peer)) { + sc_store(cfg); + } +} + +static bool sc_ccc_cfg_write(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + u16_t value) +{ + BT_DBG("value 0x%04x", value); + + if (value == BT_GATT_CCC_INDICATE) { + /* Create a new SC configuration entry if subscribed */ + sc_save(conn->id, &conn->le.dst, 0, 0); + } else { + struct gatt_sc_cfg *cfg; + + /* Clear SC configuration if unsubscribed */ + cfg = find_sc_cfg(conn->id, &conn->le.dst); + if (cfg) { + sc_clear(cfg); + } + } + + return true; +} + +static struct _bt_gatt_ccc sc_ccc = BT_GATT_CCC_INITIALIZER(NULL, + sc_ccc_cfg_write, + NULL); + +#if defined(CONFIG_BT_GATT_CACHING) +enum { + CF_CHANGE_AWARE, /* Client is changed aware */ + CF_OUT_OF_SYNC, /* Client is out of sync */ + + /* Total number of flags - must be at the end of the enum */ + CF_NUM_FLAGS, +}; + +#define CF_ROBUST_CACHING(_cfg) (_cfg->data[0] & BIT(0)) + +struct gatt_cf_cfg { + u8_t id; + bt_addr_le_t peer; + u8_t data[1]; + ATOMIC_DEFINE(flags, CF_NUM_FLAGS); +}; + +#define CF_CFG_MAX (CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN) +static struct gatt_cf_cfg cf_cfg[CF_CFG_MAX] = {}; + +static struct gatt_cf_cfg *find_cf_cfg(struct bt_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) { + if (!conn) { + if (!bt_addr_le_cmp(&cf_cfg[i].peer, BT_ADDR_LE_ANY)) { + return &cf_cfg[i]; + } + } else if (!bt_conn_addr_le_cmp(conn, &cf_cfg[i].peer)) { + return &cf_cfg[i]; + } + } + + return NULL; +} + +static ssize_t cf_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct gatt_cf_cfg *cfg; + u8_t data[1] = {}; + + cfg = find_cf_cfg(conn); + if (cfg) { + memcpy(data, cfg->data, sizeof(data)); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, data, + sizeof(data)); +} + +static bool cf_set_value(struct gatt_cf_cfg *cfg, const u8_t *value, u16_t len) +{ + u16_t i; + u8_t last_byte = 1U; + u8_t last_bit = 1U; + + /* Validate the bits */ + for (i = 0U; i < len && i < last_byte; i++) { + u8_t chg_bits = value[i] ^ cfg->data[i]; + u8_t bit; + + for (bit = 0U; bit < last_bit; bit++) { + /* A client shall never clear a bit it has set */ + if ((BIT(bit) & chg_bits) && + (BIT(bit) & cfg->data[i])) { + return false; + } + } + } + + /* Set the bits for each octect */ + for (i = 0U; i < len && i < last_byte; i++) { + cfg->data[i] |= value[i] & ((1 << last_bit) - 1); + BT_DBG("byte %u: data 0x%02x value 0x%02x", i, cfg->data[i], + value[i]); + } + + return true; +} + +static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, u16_t len, u16_t offset, u8_t flags) +{ + struct gatt_cf_cfg *cfg; + const u8_t *value = buf; + + if (offset > sizeof(cfg->data)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (offset + len > sizeof(cfg->data)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + cfg = find_cf_cfg(conn); + if (!cfg) { + cfg = find_cf_cfg(NULL); + } + + if (!cfg) { + BT_WARN("No space to store Client Supported Features"); + return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES); + } + + BT_DBG("handle 0x%04x len %u", attr->handle, len); + + if (!cf_set_value(cfg, value, len)) { + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + bt_addr_le_copy(&cfg->peer, &conn->le.dst); + atomic_set_bit(cfg->flags, CF_CHANGE_AWARE); + + return len; +} + +static u8_t db_hash[16]; +struct k_delayed_work db_hash_work; + +struct gen_hash_state { + struct tc_cmac_struct state; + int err; +}; + +static u8_t gen_hash_m(const struct bt_gatt_attr *attr, void *user_data) +{ + struct gen_hash_state *state = user_data; + struct bt_uuid_16 *u16; + u8_t data[16]; + ssize_t len; + u16_t value; + + if (attr->uuid->type != BT_UUID_TYPE_16) + return BT_GATT_ITER_CONTINUE; + + u16 = (struct bt_uuid_16 *)attr->uuid; + + switch (u16->val) { + /* Attributes to hash: handle + UUID + value */ + case 0x2800: /* GATT Primary Service */ + case 0x2801: /* GATT Secondary Service */ + case 0x2802: /* GATT Include Service */ + case 0x2803: /* GATT Characteristic */ + case 0x2900: /* GATT Characteristic Extended Properties */ + value = sys_cpu_to_le16(attr->handle); + if (tc_cmac_update(&state->state, (uint8_t *)&value, + sizeof(attr->handle)) == TC_CRYPTO_FAIL) { + state->err = -EINVAL; + return BT_GATT_ITER_STOP; + } + + value = sys_cpu_to_le16(u16->val); + if (tc_cmac_update(&state->state, (uint8_t *)&value, + sizeof(u16->val)) == TC_CRYPTO_FAIL) { + state->err = -EINVAL; + return BT_GATT_ITER_STOP; + } + + len = attr->read(NULL, attr, data, sizeof(data), 0); + if (len < 0) { + state->err = len; + return BT_GATT_ITER_STOP; + } + + if (tc_cmac_update(&state->state, data, len) == + TC_CRYPTO_FAIL) { + state->err = -EINVAL; + return BT_GATT_ITER_STOP; + } + + break; + /* Attributes to hash: handle + UUID */ + case 0x2901: /* GATT Characteristic User Descriptor */ + case 0x2902: /* GATT Client Characteristic Configuration */ + case 0x2903: /* GATT Server Characteristic Configuration */ + case 0x2904: /* GATT Characteristic Presentation Format */ + case 0x2905: /* GATT Characteristic Aggregated Format */ + value = sys_cpu_to_le16(attr->handle); + if (tc_cmac_update(&state->state, (uint8_t *)&value, + sizeof(attr->handle)) == TC_CRYPTO_FAIL) { + state->err = -EINVAL; + return BT_GATT_ITER_STOP; + } + + value = sys_cpu_to_le16(u16->val); + if (tc_cmac_update(&state->state, (uint8_t *)&value, + sizeof(u16->val)) == TC_CRYPTO_FAIL) { + state->err = -EINVAL; + return BT_GATT_ITER_STOP; + } + break; + default: + return BT_GATT_ITER_CONTINUE; + } + + return BT_GATT_ITER_CONTINUE; +} + +static void db_hash_store(void) +{ + int err; + + err = settings_save_one("bt/hash", &db_hash, sizeof(db_hash)); + if (err) { + BT_ERR("Failed to save Database Hash (err %d)", err); + } + + BT_DBG("Database Hash stored"); +} + +static void db_hash_gen(bool store) +{ + u8_t key[16] = {}; + struct tc_aes_key_sched_struct sched; + struct gen_hash_state state; + + if (tc_cmac_setup(&state.state, key, &sched) == TC_CRYPTO_FAIL) { + BT_ERR("Unable to setup AES CMAC"); + return; + } + + bt_gatt_foreach_attr(0x0001, 0xffff, gen_hash_m, &state); + + if (tc_cmac_final(db_hash, &state.state) == TC_CRYPTO_FAIL) { + BT_ERR("Unable to calculate hash"); + return; + } + + /** + * Core 5.1 does not state the endianess of the hash. + * However Vol 3, Part F, 3.3.1 says that multi-octet Characteristic + * Values shall be LE unless otherwise defined. PTS expects hash to be + * in little endianess as well. bt_smp_aes_cmac calculates the hash in + * big endianess so we have to swap. + */ + sys_mem_swap(db_hash, sizeof(db_hash)); + + #if !defined(BFLB_BLE) + BT_HEXDUMP_DBG(db_hash, sizeof(db_hash), "Hash: "); + #endif + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { + db_hash_store(); + } +} + +static void db_hash_process(struct k_work *work) +{ + db_hash_gen(true); +} + +static ssize_t db_hash_read(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + /* Check if db_hash is already pending in which case it shall be + * generated immediately instead of waiting the work to complete. + */ + if (k_delayed_work_remaining_get(&db_hash_work)) { + k_delayed_work_cancel(&db_hash_work); + db_hash_gen(true); + } + + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347: + * 2.5.2.1 Robust Caching + * A connected client becomes change-aware when... + * The client reads the Database Hash characteristic and then the server + * receives another ATT request from the client. + */ + bt_gatt_change_aware(conn, true); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, db_hash, + sizeof(db_hash)); +} + +static void clear_cf_cfg(struct gatt_cf_cfg *cfg) +{ + bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY); + memset(cfg->data, 0, sizeof(cfg->data)); + atomic_set(cfg->flags, 0); +} + +static void remove_cf_cfg(struct bt_conn *conn) +{ + struct gatt_cf_cfg *cfg; + + cfg = find_cf_cfg(conn); + if (!cfg) { + return; + } + + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2405: + * For clients with a trusted relationship, the characteristic value + * shall be persistent across connections. For clients without a + * trusted relationship the characteristic value shall be set to the + * default value at each connection. + */ + if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + clear_cf_cfg(cfg); + } else { + /* Update address in case it has changed */ + bt_addr_le_copy(&cfg->peer, &conn->le.dst); + } +} +#endif /* CONFIG_BT_GATT_CACHING */ + +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +static struct bt_gatt_attr gatt_attrs[] = { +#else +BT_GATT_SERVICE_DEFINE(_1_gatt_svc, +#endif + BT_GATT_PRIMARY_SERVICE(BT_UUID_GATT), + +#if defined(CONFIG_BT_GATT_SERVICE_CHANGED) + /* Bluetooth 5.0, Vol3 Part G: + * The Service Changed characteristic Attribute Handle on the server + * shall not change if the server has a trusted relationship with any + * client. + */ + BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SC, BT_GATT_CHRC_INDICATE, + BT_GATT_PERM_NONE, NULL, NULL, NULL), + + BT_GATT_CCC_MANAGED(&sc_ccc, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + +#if defined(CONFIG_BT_GATT_CACHING) + BT_GATT_CHARACTERISTIC(BT_UUID_GATT_CLIENT_FEATURES, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + cf_read, cf_write, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_GATT_DB_HASH, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + db_hash_read, NULL, NULL), +#endif /* CONFIG_BT_GATT_CACHING */ +#endif /* CONFIG_BT_GATT_SERVICE_CHANGED */ +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +}; +#else +); +#endif + +#if defined(BFLB_BLE_DISABLE_STATIC_ATTR) +static struct bt_gatt_service gatt_svc = BT_GATT_SERVICE(gatt_attrs); +#endif + + +#if defined(CONFIG_BT_GATT_DYNAMIC_DB) +static u8_t found_attr(const struct bt_gatt_attr *attr, void *user_data) +{ + const struct bt_gatt_attr **found = user_data; + + *found = attr; + + return BT_GATT_ITER_STOP; +} + +static const struct bt_gatt_attr *find_attr(uint16_t handle) +{ + const struct bt_gatt_attr *attr = NULL; + + bt_gatt_foreach_attr(handle, handle, found_attr, &attr); + + return attr; +} + +static void gatt_insert(struct bt_gatt_service *svc, u16_t last_handle) +{ + struct bt_gatt_service *tmp, *prev = NULL; + + if (last_handle == 0 || svc->attrs[0].handle > last_handle) { + sys_slist_append(&db, &svc->node); + return; + } + + /* DB shall always have its service in ascending order */ + SYS_SLIST_FOR_EACH_CONTAINER(&db, tmp, node) { + if (tmp->attrs[0].handle > svc->attrs[0].handle) { + if (prev) { + sys_slist_insert(&db, &prev->node, &svc->node); + } else { + sys_slist_prepend(&db, &svc->node); + } + return; + } + + prev = tmp; + } +} + +static int gatt_register(struct bt_gatt_service *svc) +{ + struct bt_gatt_service *last; + u16_t handle, last_handle; + struct bt_gatt_attr *attrs = svc->attrs; + u16_t count = svc->attr_count; + + if (sys_slist_is_empty(&db)) { + handle = last_static_handle; + last_handle = 0; + goto populate; + } + + last = SYS_SLIST_PEEK_TAIL_CONTAINER(&db, last, node); + handle = last->attrs[last->attr_count - 1].handle; + last_handle = handle; + +populate: + /* Populate the handles and append them to the list */ + for (; attrs && count; attrs++, count--) { + if (!attrs->handle) { + /* Allocate handle if not set already */ + attrs->handle = ++handle; + } else if (attrs->handle > handle) { + /* Use existing handle if valid */ + handle = attrs->handle; + } else if (find_attr(attrs->handle)) { + /* Service has conflicting handles */ + BT_ERR("Unable to register handle 0x%04x", + attrs->handle); + return -EINVAL; + } + + BT_DBG("attr %p handle 0x%04x uuid %s perm 0x%02x", + attrs, attrs->handle, bt_uuid_str(attrs->uuid), + attrs->perm); + } + + gatt_insert(svc, last_handle); + + return 0; +} +#endif /* CONFIG_BT_GATT_DYNAMIC_DB */ + +enum { + SC_RANGE_CHANGED, /* SC range changed */ + SC_INDICATE_PENDING, /* SC indicate pending */ + + /* Total number of flags - must be at the end of the enum */ + SC_NUM_FLAGS, +}; + +static struct gatt_sc { + struct bt_gatt_indicate_params params; + u16_t start; + u16_t end; + struct k_delayed_work work; + ATOMIC_DEFINE(flags, SC_NUM_FLAGS); +} gatt_sc; + +static void sc_indicate_rsp(struct bt_conn *conn, + const struct bt_gatt_attr *attr, u8_t err) +{ +#if defined(CONFIG_BT_GATT_CACHING) + struct gatt_cf_cfg *cfg; +#endif + + BT_DBG("err 0x%02x", err); + + atomic_clear_bit(gatt_sc.flags, SC_INDICATE_PENDING); + + /* Check if there is new change in the meantime */ + if (atomic_test_bit(gatt_sc.flags, SC_RANGE_CHANGED)) { + /* Reschedule without any delay since it is waiting already */ + k_delayed_work_submit(&gatt_sc.work, K_NO_WAIT); + } + +#if defined(CONFIG_BT_GATT_CACHING) + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347: + * 2.5.2.1 Robust Caching + * A connected client becomes change-aware when... + * The client receives and confirms a Service Changed indication. + */ + cfg = find_cf_cfg(conn); + if (cfg && CF_ROBUST_CACHING(cfg)) { + atomic_set_bit(cfg->flags, CF_CHANGE_AWARE); + BT_DBG("%s change-aware", bt_addr_le_str(&cfg->peer)); + } +#endif +} + +static void sc_process(struct k_work *work) +{ + struct gatt_sc *sc = CONTAINER_OF(work, struct gatt_sc, work); + u16_t sc_range[2]; + + __ASSERT(!atomic_test_bit(sc->flags, SC_INDICATE_PENDING), + "Indicate already pending"); + + BT_DBG("start 0x%04x end 0x%04x", sc->start, sc->end); + + sc_range[0] = sys_cpu_to_le16(sc->start); + sc_range[1] = sys_cpu_to_le16(sc->end); + + atomic_clear_bit(sc->flags, SC_RANGE_CHANGED); + sc->start = 0U; + sc->end = 0U; + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + sc->params.attr = &gatt_attrs[2]; + #else + sc->params.attr = &_1_gatt_svc.attrs[2]; + #endif + sc->params.func = sc_indicate_rsp; + sc->params.data = &sc_range[0]; + sc->params.len = sizeof(sc_range); + + if (bt_gatt_indicate(NULL, &sc->params)) { + /* No connections to indicate */ + return; + } + + atomic_set_bit(sc->flags, SC_INDICATE_PENDING); +} + +#if defined(CONFIG_BT_STACK_PTS) +int service_change_test(struct bt_gatt_indicate_params *params,const struct bt_conn *con) +{ + u16_t sc_range[2]; + + if(!params->attr){ + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + params->attr = &gatt_attrs[2]; + #else + params->attr = &_1_gatt_svc.attrs[2]; + #endif + } + sc_range[0] = 0x000e; + sc_range[1] = 0x001e; + + params->data = &sc_range[0]; + params->len = sizeof(sc_range); + + if (bt_gatt_indicate(con, params)) { + /* No connections to indicate */ + return; + } +} +#endif + +#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) +static struct gatt_ccc_store { + struct bt_conn *conn_list[CONFIG_BT_MAX_CONN]; + struct k_delayed_work work; +} gatt_ccc_store; + +static bool gatt_ccc_conn_is_queued(struct bt_conn *conn) +{ + return (conn == gatt_ccc_store.conn_list[bt_conn_index(conn)]); +} + +static void gatt_ccc_conn_unqueue(struct bt_conn *conn) +{ + u8_t index = bt_conn_index(conn); + + if (gatt_ccc_store.conn_list[index] != NULL) { + bt_conn_unref(gatt_ccc_store.conn_list[index]); + gatt_ccc_store.conn_list[index] = NULL; + } +} + +static bool gatt_ccc_conn_queue_is_empty(void) +{ + for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) { + if (gatt_ccc_store.conn_list[i]) { + return false; + } + } + + return true; +} + +static void ccc_delayed_store(struct k_work *work) +{ + struct gatt_ccc_store *ccc_store = + CONTAINER_OF(work, struct gatt_ccc_store, work); + + for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) { + struct bt_conn *conn = ccc_store->conn_list[i]; + + if (!conn) { + continue; + } + + if (bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + bt_gatt_store_ccc(conn->id, &conn->le.dst); + bt_conn_unref(conn); + ccc_store->conn_list[i] = NULL; + } + } +} +#endif + +void bt_gatt_init(void) +{ + if (!atomic_cas(&init, 0, 1)) { + return; + } + + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + /* Register mandatory services */ + gatt_register(&gap_svc); + gatt_register(&gatt_svc); + + #else + Z_STRUCT_SECTION_FOREACH(bt_gatt_service_static, svc) { + last_static_handle += svc->attr_count; + } + #endif + +#if defined(CONFIG_BT_GATT_CACHING) + k_delayed_work_init(&db_hash_work, db_hash_process); + + /* Submit work to Generate initial hash as there could be static + * services already in the database. + */ + k_delayed_work_submit(&db_hash_work, DB_HASH_TIMEOUT); +#endif /* CONFIG_BT_GATT_CACHING */ + + if (IS_ENABLED(CONFIG_BT_GATT_SERVICE_CHANGED)) { + k_delayed_work_init(&gatt_sc.work, sc_process); + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + /* Make sure to not send SC indications until SC + * settings are loaded + */ + atomic_set_bit(gatt_sc.flags, SC_INDICATE_PENDING); + } + } + +#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) + k_delayed_work_init(&gatt_ccc_store.work, ccc_delayed_store); +#endif +} + +#if defined(BFLB_BLE) +void bt_gatt_deinit(void) +{ + #if defined(CONFIG_BT_GATT_CACHING) + k_delayed_work_del_timer(&db_hash_work); + #endif + + if (IS_ENABLED(CONFIG_BT_GATT_SERVICE_CHANGED)){ + k_delayed_work_del_timer(&gatt_sc.work); + } + + #if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) + k_delayed_work_del_timer(&gatt_ccc_store.work); + #endif +} +#endif + +#if defined(CONFIG_BT_GATT_DYNAMIC_DB) || \ + (defined(CONFIG_BT_GATT_CACHING) && defined(CONFIG_BT_SETTINGS)) +static void sc_indicate(u16_t start, u16_t end) +{ + BT_DBG("start 0x%04x end 0x%04x", start, end); + + #if defined (BFLB_BLE_PATCH_SET_SCRANGE_CHAGD_ONLY_IN_CONNECTED_STATE) + struct bt_conn *conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECTED); + if(conn){ + #endif + if (!atomic_test_and_set_bit(gatt_sc.flags, SC_RANGE_CHANGED)) { + gatt_sc.start = start; + gatt_sc.end = end; + goto submit; + } + #if defined (BFLB_BLE_PATCH_SET_SCRANGE_CHAGD_ONLY_IN_CONNECTED_STATE) + } + #endif + + if (!update_range(&gatt_sc.start, &gatt_sc.end, start, end)) { + return; + } + +submit: + if (atomic_test_bit(gatt_sc.flags, SC_INDICATE_PENDING)) { + BT_DBG("indicate pending, waiting until complete..."); + return; + } + +#if defined (BFLB_BLE_PATCH_SET_SCRANGE_CHAGD_ONLY_IN_CONNECTED_STATE) + if(conn){ +#endif + /* Reschedule since the range has changed */ + k_delayed_work_submit(&gatt_sc.work, SC_TIMEOUT); +#if defined (BFLB_BLE_PATCH_SET_SCRANGE_CHAGD_ONLY_IN_CONNECTED_STATE) + bt_conn_unref(conn); +} +#endif + +} +#endif /* BT_GATT_DYNAMIC_DB || (BT_GATT_CACHING && BT_SETTINGS) */ + +#if defined(CONFIG_BT_GATT_DYNAMIC_DB) +static void db_changed(void) +{ +#if defined(CONFIG_BT_GATT_CACHING) + int i; + + k_delayed_work_submit(&db_hash_work, DB_HASH_TIMEOUT); + + for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) { + struct gatt_cf_cfg *cfg = &cf_cfg[i]; + + if (!bt_addr_le_cmp(&cfg->peer, BT_ADDR_LE_ANY)) { + continue; + } + + if (CF_ROBUST_CACHING(cfg)) { + /* Core Spec 5.1 | Vol 3, Part G, 2.5.2.1 Robust Caching + *... the database changes again before the client + * becomes change-aware in which case the error response + * shall be sent again. + */ + atomic_clear_bit(cfg->flags, CF_OUT_OF_SYNC); + if (atomic_test_and_clear_bit(cfg->flags, + CF_CHANGE_AWARE)) { + BT_DBG("%s change-unaware", + bt_addr_le_str(&cfg->peer)); + } + } + } +#endif +} + +int bt_gatt_service_register(struct bt_gatt_service *svc) +{ + int err; + + __ASSERT(svc, "invalid parameters\n"); + __ASSERT(svc->attrs, "invalid parameters\n"); + __ASSERT(svc->attr_count, "invalid parameters\n"); + + /* Init GATT core services */ + bt_gatt_init(); + + /* Do no allow to register mandatory services twice */ + if (!bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GAP) || + !bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GATT)) { + return -EALREADY; + } + + err = gatt_register(svc); + if (err < 0) { + return err; + } + + sc_indicate(svc->attrs[0].handle, + svc->attrs[svc->attr_count - 1].handle); + + db_changed(); + + return 0; +} + +int bt_gatt_service_unregister(struct bt_gatt_service *svc) +{ + __ASSERT(svc, "invalid parameters\n"); + + if (!sys_slist_find_and_remove(&db, &svc->node)) { + return -ENOENT; + } + + sc_indicate(svc->attrs[0].handle, + svc->attrs[svc->attr_count - 1].handle); + + db_changed(); + + return 0; +} +#endif /* CONFIG_BT_GATT_DYNAMIC_DB */ + +ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t buf_len, u16_t offset, + const void *value, u16_t value_len) +{ + u16_t len; + #if defined(CONFIG_BT_STACK_PTS) + u8_t *data = NULL; + u8_t i = 0; + #endif + + if (offset > value_len) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + len = MIN(buf_len, value_len - offset); + + BT_DBG("handle 0x%04x offset %u length %u", attr->handle, offset, + len); + + memcpy(buf, (u8_t *)value + offset, len); + + #if defined(CONFIG_BT_STACK_PTS) + /* PTS sends a request to iut read all primary services it contains. + * Set event flags to avoid comflicts when other test cases need to add reference codes. + */ + if(event_flag == att_read_by_group_type_ind){ + data = (u8_t *)buf; + for(i=0;ihandle, i, data[i]); + } + } + #endif + + return len; +} + +ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct bt_uuid *uuid = attr->user_data; + + if (uuid->type == BT_UUID_TYPE_16) { + u16_t uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + &uuid16, 2); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + BT_UUID_128(uuid)->val, 16); +} + +struct gatt_incl { + u16_t start_handle; + u16_t end_handle; + u16_t uuid16; +} __packed; + +static u8_t get_service_handles(const struct bt_gatt_attr *attr, + void *user_data) +{ + struct gatt_incl *include = user_data; + + /* Stop if attribute is a service */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) || + !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) { + return BT_GATT_ITER_STOP; + } + + include->end_handle = attr->handle; + + return BT_GATT_ITER_CONTINUE; +} + +#if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) +static u16_t find_static_attr(const struct bt_gatt_attr *attr) +{ + u16_t handle = 1; + + Z_STRUCT_SECTION_FOREACH(bt_gatt_service_static, static_svc) { + for (int i = 0; i < static_svc->attr_count; i++, handle++) { + if (attr == &static_svc->attrs[i]) { + return handle; + } + } + } + + return 0; +} +#endif + +ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + struct bt_gatt_attr *incl = attr->user_data; + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + u16_t handle = incl->handle; + #else + u16_t handle = incl->handle ? : find_static_attr(incl); + #endif + struct bt_uuid *uuid = incl->user_data; + struct gatt_incl pdu; + u8_t value_len; + + /* first attr points to the start handle */ + pdu.start_handle = sys_cpu_to_le16(handle); + value_len = sizeof(pdu.start_handle) + sizeof(pdu.end_handle); + + /* + * Core 4.2, Vol 3, Part G, 3.2, + * The Service UUID shall only be present when the UUID is a + * 16-bit Bluetooth UUID. + */ + if (uuid->type == BT_UUID_TYPE_16) { + pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val); + value_len += sizeof(pdu.uuid16); + } + + /* Lookup for service end handle */ + bt_gatt_foreach_attr(handle + 1, 0xffff, get_service_handles, &pdu); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +struct gatt_chrc { + u8_t properties; + u16_t value_handle; + union { + u16_t uuid16; + u8_t uuid[16]; + }; +} __packed; + +uint16_t bt_gatt_attr_value_handle(const struct bt_gatt_attr *attr) +{ + u16_t handle = 0; + + if ((attr != NULL) + && (attr->read == bt_gatt_attr_read_chrc)) { + struct bt_gatt_chrc *chrc = attr->user_data; + + handle = chrc->value_handle; + #if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + if (handle == 0) { + /* Fall back to Zephyr value handle policy */ + handle = (attr->handle ? : find_static_attr(attr)) + 1U; + } + #endif + } + + return handle; +} + +ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + struct bt_gatt_chrc *chrc = attr->user_data; + struct gatt_chrc pdu; + u8_t value_len; + + pdu.properties = chrc->properties; + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 534: + * 3.3.2 Characteristic Value Declaration + * The Characteristic Value declaration contains the value of the + * characteristic. It is the first Attribute after the characteristic + * declaration. All characteristic definitions shall have a + * Characteristic Value declaration. + */ + pdu.value_handle = sys_cpu_to_le16(bt_gatt_attr_value_handle(attr)); + + value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle); + + if (chrc->uuid->type == BT_UUID_TYPE_16) { + pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(chrc->uuid)->val); + value_len += 2U; + } else { + memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16); + value_len += 16U; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +static u8_t gatt_foreach_iter(const struct bt_gatt_attr *attr, + u16_t start_handle, u16_t end_handle, + const struct bt_uuid *uuid, + const void *attr_data, uint16_t *num_matches, + bt_gatt_attr_func_t func, void *user_data) +{ + u8_t result; + + /* Stop if over the requested range */ + if (attr->handle > end_handle) { + return BT_GATT_ITER_STOP; + } + + /* Check if attribute handle is within range */ + if (attr->handle < start_handle) { + return BT_GATT_ITER_CONTINUE; + } + + /* Match attribute UUID if set */ + if (uuid && bt_uuid_cmp(uuid, attr->uuid)) { + return BT_GATT_ITER_CONTINUE; + } + + /* Match attribute user_data if set */ + if (attr_data && attr_data != attr->user_data) { + return BT_GATT_ITER_CONTINUE; + } + + *num_matches -= 1; + + result = func(attr, user_data); + + if (!*num_matches) { + return BT_GATT_ITER_STOP; + } + + return result; +} + +static void foreach_attr_type_dyndb(u16_t start_handle, u16_t end_handle, + const struct bt_uuid *uuid, + const void *attr_data, uint16_t num_matches, + bt_gatt_attr_func_t func, void *user_data) +{ +#if defined(CONFIG_BT_GATT_DYNAMIC_DB) + int i; + + struct bt_gatt_service *svc; + + SYS_SLIST_FOR_EACH_CONTAINER(&db, svc, node) { + struct bt_gatt_service *next; + + next = SYS_SLIST_PEEK_NEXT_CONTAINER(svc, node); + if (next) { + /* Skip ahead if start is not within service handles */ + if (next->attrs[0].handle <= start_handle) { + continue; + } + } + + for (i = 0; i < svc->attr_count; i++) { + struct bt_gatt_attr *attr = &svc->attrs[i]; + + if (gatt_foreach_iter(attr, + start_handle, + end_handle, + uuid, attr_data, + &num_matches, + func, user_data) == + BT_GATT_ITER_STOP) { + return; + } + } + } +#endif /* CONFIG_BT_GATT_DYNAMIC_DB */ +} + +void bt_gatt_foreach_attr_type(u16_t start_handle, u16_t end_handle, + const struct bt_uuid *uuid, + const void *attr_data, uint16_t num_matches, + bt_gatt_attr_func_t func, void *user_data) +{ + int i; + + if (!num_matches) { + num_matches = UINT16_MAX; + } +#if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + if (start_handle <= last_static_handle) { + u16_t handle = 1; + + Z_STRUCT_SECTION_FOREACH(bt_gatt_service_static, static_svc) { + /* Skip ahead if start is not within service handles */ + if (handle + static_svc->attr_count < start_handle) { + handle += static_svc->attr_count; + continue; + } + + for (i = 0; i < static_svc->attr_count; i++, handle++) { + struct bt_gatt_attr attr; + + memcpy(&attr, &static_svc->attrs[i], + sizeof(attr)); + + attr.handle = handle; + + if (gatt_foreach_iter(&attr, start_handle, + end_handle, uuid, + attr_data, &num_matches, + func, user_data) == + BT_GATT_ITER_STOP) { + return; + } + } + } + } +#endif + /* Iterate over dynamic db */ + foreach_attr_type_dyndb(start_handle, end_handle, uuid, attr_data, + num_matches, func, user_data); +} + +static u8_t find_next(const struct bt_gatt_attr *attr, void *user_data) +{ + struct bt_gatt_attr **next = user_data; + + *next = (struct bt_gatt_attr *)attr; + + return BT_GATT_ITER_STOP; +} + +struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr) +{ + struct bt_gatt_attr *next = NULL; + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + u16_t handle = attr->handle; + #else + u16_t handle = attr->handle ? : find_static_attr(attr); + #endif + bt_gatt_foreach_attr(handle + 1, handle + 1, find_next, &next); + + return next; +} + +static void clear_ccc_cfg(struct bt_gatt_ccc_cfg *cfg) +{ + bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY); + cfg->id = 0U; + cfg->value = 0U; +} + +static struct bt_gatt_ccc_cfg *find_ccc_cfg(const struct bt_conn *conn, + struct _bt_gatt_ccc *ccc) +{ + for (size_t i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + if (conn) { + if (conn->id == ccc->cfg[i].id && + !bt_conn_addr_le_cmp(conn, &ccc->cfg[i].peer)) { + return &ccc->cfg[i]; + } + } else if (!bt_addr_le_cmp(&ccc->cfg[i].peer, BT_ADDR_LE_ANY)) { + return &ccc->cfg[i]; + } + } + + return NULL; +} + +ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + struct _bt_gatt_ccc *ccc = attr->user_data; + const struct bt_gatt_ccc_cfg *cfg; + u16_t value; + + cfg = find_ccc_cfg(conn, ccc); + if (cfg) { + value = sys_cpu_to_le16(cfg->value); + } else { + /* Default to disable if there is no cfg for the peer */ + value = 0x0000; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, + sizeof(value)); +} + +static void gatt_ccc_changed(const struct bt_gatt_attr *attr, + struct _bt_gatt_ccc *ccc) +{ + int i; + u16_t value = 0x0000; + + for (i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + if (ccc->cfg[i].value > value) { + value = ccc->cfg[i].value; + } + } + + BT_DBG("ccc %p value 0x%04x", ccc, value); + + if (value != ccc->value) { + ccc->value = value; + if (ccc->cfg_changed) { + ccc->cfg_changed(attr, value); + } + } +} + +ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags) +{ + struct _bt_gatt_ccc *ccc = attr->user_data; + struct bt_gatt_ccc_cfg *cfg; + u16_t value; + + if (offset) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (!len || len > sizeof(u16_t)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + if (len < sizeof(u16_t)) { + value = *(u8_t *)buf; + } else { + value = sys_get_le16(buf); + } + + cfg = find_ccc_cfg(conn, ccc); + if (!cfg) { + /* If there's no existing entry, but the new value is zero, + * we don't need to do anything, since a disabled CCC is + * behavioraly the same as no written CCC. + */ + if (!value) { + return len; + } + + cfg = find_ccc_cfg(NULL, ccc); + if (!cfg) { + BT_WARN("No space to store CCC cfg"); + return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES); + } + + bt_addr_le_copy(&cfg->peer, &conn->le.dst); + cfg->id = conn->id; + } + + /* Confirm write if cfg is managed by application */ + if (ccc->cfg_write && !ccc->cfg_write(conn, attr, value)) { + return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED); + } + + cfg->value = value; + + BT_DBG("handle 0x%04x value %u", attr->handle, cfg->value); + + /* Update cfg if don't match */ + if (cfg->value != ccc->value) { + gatt_ccc_changed(attr, ccc); + +#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) + if ((!gatt_ccc_conn_is_queued(conn)) && + bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + /* Store the connection with the same index it has in + * the conns array + */ + gatt_ccc_store.conn_list[bt_conn_index(conn)] = + bt_conn_ref(conn); + k_delayed_work_submit(&gatt_ccc_store.work, + CCC_STORE_DELAY); + } +#endif + } + + /* Disabled CCC is the same as no configured CCC, so clear the entry */ + if (!value) { + clear_ccc_cfg(cfg); + } + + return len; +} + +ssize_t bt_gatt_attr_read_cep(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + const struct bt_gatt_cep *value = attr->user_data; + u16_t props = sys_cpu_to_le16(value->properties); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &props, + sizeof(props)); +} + +ssize_t bt_gatt_attr_read_cud(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + const char *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + strlen(value)); +} + +ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + const struct bt_gatt_cpf *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + sizeof(*value)); +} + +struct notify_data { + int err; + u16_t type; + union { + struct bt_gatt_notify_params *nfy_params; + struct bt_gatt_indicate_params *ind_params; + }; +}; + +static int gatt_notify(struct bt_conn *conn, u16_t handle, + struct bt_gatt_notify_params *params) +{ + struct net_buf *buf; + struct bt_att_notify *nfy; + +#if defined(CONFIG_BT_GATT_ENFORCE_CHANGE_UNAWARE) + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350: + * Except for the Handle Value indication, the server shall not send + * notifications and indications to such a client until it becomes + * change-aware. + */ + if (!bt_gatt_change_aware(conn, false)) { + return -EAGAIN; + } +#endif + + buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY, + sizeof(*nfy) + params->len); + if (!buf) { + BT_WARN("No buffer available to send notification"); + return -ENOMEM; + } + + BT_DBG("conn %p handle 0x%04x", conn, handle); + + nfy = net_buf_add(buf, sizeof(*nfy)); + nfy->handle = sys_cpu_to_le16(handle); + + net_buf_add(buf, params->len); + memcpy(nfy->value, params->data, params->len); + + return bt_att_send(conn, buf, params->func, params->user_data); +} + +static void gatt_indicate_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, void *user_data) +{ + struct bt_gatt_indicate_params *params = user_data; + + params->func(conn, params->attr, err); +} + +static int gatt_send(struct bt_conn *conn, struct net_buf *buf, + bt_att_func_t func, void *params, + bt_att_destroy_t destroy) +{ + int err; + + if (params) { + struct bt_att_req *req = params; + req->buf = buf; + req->func = func; + req->destroy = destroy; + + err = bt_att_req_send(conn, req); + } else { + err = bt_att_send(conn, buf, NULL, NULL); + } + + + if (err) { + BT_ERR("Error sending ATT PDU: %d", err); + } + + return err; +} + +static int gatt_indicate(struct bt_conn *conn, u16_t handle, + struct bt_gatt_indicate_params *params) +{ + struct net_buf *buf; + struct bt_att_indicate *ind; + +#if defined(CONFIG_BT_GATT_ENFORCE_CHANGE_UNAWARE) + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350: + * Except for the Handle Value indication, the server shall not send + * notifications and indications to such a client until it becomes + * change-aware. + */ + if (!(params->func && (params->func == sc_indicate_rsp || + params->func == sc_restore_rsp)) && + !bt_gatt_change_aware(conn, false)) { + return -EAGAIN; + } +#endif + + buf = bt_att_create_pdu(conn, BT_ATT_OP_INDICATE, + sizeof(*ind) + params->len); + if (!buf) { + BT_WARN("No buffer available to send indication"); + return -ENOMEM; + } + + BT_DBG("conn %p handle 0x%04x", conn, handle); + + ind = net_buf_add(buf, sizeof(*ind)); + ind->handle = sys_cpu_to_le16(handle); + + net_buf_add(buf, params->len); + memcpy(ind->value, params->data, params->len); + + if (!params->func) { + return gatt_send(conn, buf, NULL, NULL, NULL); + } + + return gatt_send(conn, buf, gatt_indicate_rsp, params, NULL); +} + +static u8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct notify_data *data = user_data; + struct _bt_gatt_ccc *ccc; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Save Service Changed data if peer is not connected */ + if (IS_ENABLED(CONFIG_BT_GATT_SERVICE_CHANGED) && ccc == &sc_ccc) { + for (i = 0; i < ARRAY_SIZE(sc_cfg); i++) { + struct gatt_sc_cfg *cfg = &sc_cfg[i]; + struct bt_conn *conn; + + if (!bt_addr_le_cmp(&cfg->peer, BT_ADDR_LE_ANY)) { + continue; + } + + conn = bt_conn_lookup_state_le(&cfg->peer, + BT_CONN_CONNECTED); + if (!conn) { + struct sc_data *sc; + + sc = (struct sc_data *)data->ind_params->data; + sc_save(cfg->id, &cfg->peer, + sys_le16_to_cpu(sc->start), + sys_le16_to_cpu(sc->end)); + continue; + } + bt_conn_unref(conn); + } + } + + /* Notify all peers configured */ + for (i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i]; + struct bt_conn *conn; + int err; + + /* Check if config value matches data type since consolidated + * value may be for a different peer. + */ + if (cfg->value != data->type) { + continue; + } + + conn = bt_conn_lookup_addr_le(cfg->id, &cfg->peer); + if (!conn) { + continue; + } + + if (conn->state != BT_CONN_CONNECTED) { + bt_conn_unref(conn); + continue; + } + + /* Confirm match if cfg is managed by application */ + if (ccc->cfg_match && !ccc->cfg_match(conn, attr)) { + bt_conn_unref(conn); + continue; + } + + if (data->type == BT_GATT_CCC_INDICATE) { + err = gatt_indicate(conn, attr->handle - 1, + data->ind_params); + } else { + err = gatt_notify(conn, attr->handle - 1, + data->nfy_params); + } + + bt_conn_unref(conn); + + if (err < 0) { + return BT_GATT_ITER_STOP; + } + + data->err = 0; + } + + return BT_GATT_ITER_CONTINUE; +} + +static u8_t match_uuid(const struct bt_gatt_attr *attr, void *user_data) +{ + const struct bt_gatt_attr **found = user_data; + + *found = attr; + + return BT_GATT_ITER_STOP; +} + +int bt_gatt_notify_cb(struct bt_conn *conn, + struct bt_gatt_notify_params *params) +{ + struct notify_data data; + const struct bt_gatt_attr *attr; + u16_t handle; + + __ASSERT(params, "invalid parameters\n"); + __ASSERT(params->attr, "invalid parameters\n"); + + attr = params->attr; + + if (conn && conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + #if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + handle = attr->handle ? : find_static_attr(attr); + #endif + if (!handle) { + return -ENOENT; + } + + /* Lookup UUID if it was given */ + if (params->uuid) { + attr = NULL; + + bt_gatt_foreach_attr_type(handle, 0xffff, params->uuid, + NULL, 1, match_uuid, &attr); + if (!attr) { + return -ENOENT; + } + #if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + handle = attr->handle ? : find_static_attr(attr); + #endif + if (!handle) { + return -ENOENT; + } + } + + /* Check if attribute is a characteristic then adjust the handle */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) { + struct bt_gatt_chrc *chrc = attr->user_data; + + if (!(chrc->properties & BT_GATT_CHRC_NOTIFY)) { + return -EINVAL; + } + + handle = bt_gatt_attr_value_handle(attr); + } + + if (conn) { + return gatt_notify(conn, handle, params); + } + + data.err = -ENOTCONN; + data.type = BT_GATT_CCC_NOTIFY; + data.nfy_params = params; + + bt_gatt_foreach_attr_type(handle, 0xffff, BT_UUID_GATT_CCC, NULL, 1, + notify_cb, &data); + + return data.err; +} + +int bt_gatt_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params) +{ + struct notify_data data; + const struct bt_gatt_attr *attr; + u16_t handle; + + __ASSERT(params, "invalid parameters\n"); + __ASSERT(params->attr, "invalid parameters\n"); + + attr = params->attr; + + if (conn && conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + #if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + handle = attr->handle ? : find_static_attr(attr); + #endif + if (!handle) { + return -ENOENT; + } + + /* Lookup UUID if it was given */ + if (params->uuid) { + attr = NULL; + + bt_gatt_foreach_attr_type(handle, 0xffff, params->uuid, + NULL, 1, match_uuid, &attr); + if (!attr) { + return -ENOENT; + } + #if !defined(BFLB_BLE_DISABLE_STATIC_ATTR) + handle = attr->handle ? : find_static_attr(attr); + #endif + if (!handle) { + return -ENOENT; + } + } + + /* Check if attribute is a characteristic then adjust the handle */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) { + struct bt_gatt_chrc *chrc = params->attr->user_data; + + if (!(chrc->properties & BT_GATT_CHRC_INDICATE)) { + return -EINVAL; + } + + handle = bt_gatt_attr_value_handle(params->attr); + } + + if (conn) { + return gatt_indicate(conn, handle, params); + } + + data.err = -ENOTCONN; + data.type = BT_GATT_CCC_INDICATE; + data.ind_params = params; + + bt_gatt_foreach_attr_type(handle, 0xffff, BT_UUID_GATT_CCC, NULL, 1, + notify_cb, &data); + + return data.err; +} + +u16_t bt_gatt_get_mtu(struct bt_conn *conn) +{ + return bt_att_get_mtu(conn); +} + +u8_t bt_gatt_check_perm(struct bt_conn *conn, const struct bt_gatt_attr *attr, + u8_t mask) +{ + if ((mask & BT_GATT_PERM_READ) && + (!(attr->perm & BT_GATT_PERM_READ_MASK) || !attr->read)) { + return BT_ATT_ERR_READ_NOT_PERMITTED; + } + + if ((mask & BT_GATT_PERM_WRITE) && + (!(attr->perm & BT_GATT_PERM_WRITE_MASK) || !attr->write)) { + return BT_ATT_ERR_WRITE_NOT_PERMITTED; + } + + mask &= attr->perm; + if (mask & BT_GATT_PERM_AUTHEN_MASK) { + if (bt_conn_get_security(conn) < BT_SECURITY_L3) { + return BT_ATT_ERR_AUTHENTICATION; + } + } + + if ((mask & BT_GATT_PERM_ENCRYPT_MASK)) { +#if defined(CONFIG_BT_SMP) + if (!conn->encrypt) { + return BT_ATT_ERR_INSUFFICIENT_ENCRYPTION; + } +#else + return BT_ATT_ERR_INSUFFICIENT_ENCRYPTION; +#endif /* CONFIG_BT_SMP */ + } + + return 0; +} + +static void sc_restore_rsp(struct bt_conn *conn, + const struct bt_gatt_attr *attr, u8_t err) +{ +#if defined(CONFIG_BT_GATT_CACHING) + struct gatt_cf_cfg *cfg; +#endif + + BT_DBG("err 0x%02x", err); + +#if defined(CONFIG_BT_GATT_CACHING) + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347: + * 2.5.2.1 Robust Caching + * A connected client becomes change-aware when... + * The client receives and confirms a Service Changed indication. + */ + cfg = find_cf_cfg(conn); + if (cfg && CF_ROBUST_CACHING(cfg)) { + atomic_set_bit(cfg->flags, CF_CHANGE_AWARE); + BT_DBG("%s change-aware", bt_addr_le_str(&cfg->peer)); + } +#endif +} + +static struct bt_gatt_indicate_params sc_restore_params[CONFIG_BT_MAX_CONN]; + +static void sc_restore(struct bt_conn *conn) +{ + struct gatt_sc_cfg *cfg; + u16_t sc_range[2]; + u8_t index; + + cfg = find_sc_cfg(conn->id, &conn->le.dst); + if (!cfg) { + BT_DBG("no SC data found"); + return; + } + + if (!(cfg->data.start || cfg->data.end)) { + return; + } + + BT_DBG("peer %s start 0x%04x end 0x%04x", bt_addr_le_str(&cfg->peer), + cfg->data.start, cfg->data.end); + + sc_range[0] = sys_cpu_to_le16(cfg->data.start); + sc_range[1] = sys_cpu_to_le16(cfg->data.end); + + index = bt_conn_index(conn); + #if defined(BFLB_BLE_DISABLE_STATIC_ATTR) + sc_restore_params[index].attr = &gatt_attrs[2]; + #else + sc_restore_params[index].attr = &_1_gatt_svc.attrs[2]; + #endif + sc_restore_params[index].func = sc_restore_rsp; + sc_restore_params[index].data = &sc_range[0]; + sc_restore_params[index].len = sizeof(sc_range); + + if (bt_gatt_indicate(conn, &sc_restore_params[index])) { + BT_ERR("SC restore indication failed"); + } + + /* Reset config data */ + sc_reset(cfg); +} + +struct conn_data { + struct bt_conn *conn; + bt_security_t sec; +}; + +static u8_t update_ccc(const struct bt_gatt_attr *attr, void *user_data) +{ + struct conn_data *data = user_data; + struct bt_conn *conn = data->conn; + struct _bt_gatt_ccc *ccc; + size_t i; + u8_t err; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + for (i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + /* Ignore configuration for different peer */ + if (bt_conn_addr_le_cmp(conn, &ccc->cfg[i].peer)) { + continue; + } + + /* Check if attribute requires encryption/authentication */ + err = bt_gatt_check_perm(conn, attr, BT_GATT_PERM_WRITE_MASK); + if (err) { + bt_security_t sec; + + if (err == BT_ATT_ERR_WRITE_NOT_PERMITTED) { + BT_WARN("CCC %p not writable", attr); + continue; + } + + sec = BT_SECURITY_L2; + + if (err == BT_ATT_ERR_AUTHENTICATION) { + sec = BT_SECURITY_L3; + } + + /* Check if current security is enough */ + if (IS_ENABLED(CONFIG_BT_SMP) && + bt_conn_get_security(conn) < sec) { + if (data->sec < sec) { + data->sec = sec; + } + continue; + } + } + + if (ccc->cfg[i].value) { + gatt_ccc_changed(attr, ccc); + if (IS_ENABLED(CONFIG_BT_GATT_SERVICE_CHANGED) && + ccc == &sc_ccc) { + sc_restore(conn); + } + return BT_GATT_ITER_CONTINUE; + } + } + + return BT_GATT_ITER_CONTINUE; +} + +static u8_t disconnected_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct bt_conn *conn = user_data; + struct _bt_gatt_ccc *ccc; + bool value_used; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* If already disabled skip */ + if (!ccc->value) { + return BT_GATT_ITER_CONTINUE; + } + + /* Checking if all values are disabled */ + value_used = false; + + for (i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i]; + + /* Ignore configurations with disabled value */ + if (!cfg->value) { + continue; + } + + if (conn->id != cfg->id || + bt_conn_addr_le_cmp(conn, &cfg->peer)) { + struct bt_conn *tmp; + + /* Skip if there is another peer connected */ + tmp = bt_conn_lookup_addr_le(cfg->id, &cfg->peer); + if (tmp) { + if (tmp->state == BT_CONN_CONNECTED) { + value_used = true; + } + + bt_conn_unref(tmp); + } + } else { + /* Clear value if not paired */ + if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + clear_ccc_cfg(cfg); + } else { + /* Update address in case it has changed */ + bt_addr_le_copy(&cfg->peer, &conn->le.dst); + } + } + } + + /* If all values are now disabled, reset value while disconnected */ + if (!value_used) { + ccc->value = 0U; + if (ccc->cfg_changed) { + ccc->cfg_changed(attr, ccc->value); + } + + BT_DBG("ccc %p reseted", ccc); + } + + return BT_GATT_ITER_CONTINUE; +} + +bool bt_gatt_is_subscribed(struct bt_conn *conn, + const struct bt_gatt_attr *attr, u16_t ccc_value) +{ + const struct _bt_gatt_ccc *ccc; + + __ASSERT(conn, "invalid parameter\n"); + __ASSERT(attr, "invalid parameter\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return false; + } + + /* Check if attribute is a characteristic declaration */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) { + struct bt_gatt_chrc *chrc = attr->user_data; + + if (!(chrc->properties & + (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) { + /* Characteristic doesn't support subscription */ + return false; + } + + attr = bt_gatt_attr_next(attr); + } + + /* Check if attribute is a characteristic value */ + if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC) != 0) { + attr = bt_gatt_attr_next(attr); + } + + /* Check if the attribute is the CCC Descriptor */ + if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC) != 0) { + return false; + } + + ccc = attr->user_data; + + /* Check if the connection is subscribed */ + for (size_t i = 0; i < BT_GATT_CCC_MAX; i++) { + if (conn->id == ccc->cfg[i].id && + !bt_conn_addr_le_cmp(conn, &ccc->cfg[i].peer) && + (ccc_value & ccc->cfg[i].value)) { + return true; + } + } + + return false; +} + +#if defined(CONFIG_BT_GATT_CLIENT) +void bt_gatt_notification(struct bt_conn *conn, u16_t handle, + const void *data, u16_t length) +{ + struct bt_gatt_subscribe_params *params, *tmp; + + BT_DBG("handle 0x%04x length %u", handle, length); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { + if (bt_conn_addr_le_cmp(conn, ¶ms->_peer) || + handle != params->value_handle) { + continue; + } + + if (params->notify(conn, params, data, length) == + BT_GATT_ITER_STOP) { + bt_gatt_unsubscribe(conn, params); + } + } +} + +static void update_subscription(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + if (params->_peer.type == BT_ADDR_LE_PUBLIC) { + return; + } + + /* Update address */ + bt_addr_le_copy(¶ms->_peer, &conn->le.dst); +} + +static void gatt_subscription_remove(struct bt_conn *conn, sys_snode_t *prev, + struct bt_gatt_subscribe_params *params) +{ + /* Remove subscription from the list*/ + sys_slist_remove(&subscriptions, prev, ¶ms->node); + + params->notify(conn, params, NULL, 0); +} + +static void remove_subscriptions(struct bt_conn *conn) +{ + struct bt_gatt_subscribe_params *params, *tmp; + sys_snode_t *prev = NULL; + + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { + if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) { + prev = ¶ms->node; + continue; + } + + if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst) || + (atomic_test_bit(params->flags, + BT_GATT_SUBSCRIBE_FLAG_VOLATILE))) { + /* Remove subscription */ + params->value = 0U; + gatt_subscription_remove(conn, prev, params); + } else { + update_subscription(conn, params); + prev = ¶ms->node; + } + } +} + +static void gatt_mtu_rsp(struct bt_conn *conn, u8_t err, const void *pdu, + u16_t length, void *user_data) +{ + struct bt_gatt_exchange_params *params = user_data; + + params->func(conn, err, params); +} +#if defined(CONFIG_BLE_AT_CMD) +int bt_at_gatt_exchange_mtu(struct bt_conn *conn, struct bt_gatt_exchange_params *params,u16_t mtu_size) +{ + struct bt_att_exchange_mtu_req *req; + struct net_buf *buf; + u16_t mtu; + + __ASSERT(conn, "invalid parameter\n"); + __ASSERT(params && params->func, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + mtu = mtu_size; + + #if defined(CONFIG_BLE_AT_CMD) + set_mtu_size(mtu); + #endif + + BT_DBG("Client MTU %u", mtu); + + req = net_buf_add(buf, sizeof(*req)); + req->mtu = sys_cpu_to_le16(mtu); + + return gatt_send(conn, buf, gatt_mtu_rsp, params, NULL); + +} +#endif + +int bt_gatt_exchange_mtu(struct bt_conn *conn, + struct bt_gatt_exchange_params *params) +{ + struct bt_att_exchange_mtu_req *req; + struct net_buf *buf; + u16_t mtu; + + __ASSERT(conn, "invalid parameter\n"); + __ASSERT(params && params->func, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + mtu = BT_ATT_MTU; + + BT_DBG("Client MTU %u", mtu); + + req = net_buf_add(buf, sizeof(*req)); + req->mtu = sys_cpu_to_le16(mtu); + + return gatt_send(conn, buf, gatt_mtu_rsp, params, NULL); +} + +static void gatt_discover_next(struct bt_conn *conn, u16_t last_handle, + struct bt_gatt_discover_params *params) +{ + /* Skip if last_handle is not set */ + if (!last_handle) + goto discover; + + /* Continue from the last found handle */ + params->start_handle = last_handle; + if (params->start_handle < UINT16_MAX) { + params->start_handle++; + } else { + goto done; + } + + /* Stop if over the range or the requests */ + if (params->start_handle > params->end_handle) { + goto done; + } + +discover: + /* Discover next range */ + if (!bt_gatt_discover(conn, params)) { + return; + } + +done: + params->func(conn, NULL, params); +} + +static void gatt_find_type_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + const struct bt_att_find_type_rsp *rsp = pdu; + struct bt_gatt_discover_params *params = user_data; + u8_t i; + u16_t end_handle = 0U, start_handle; + + BT_DBG("err 0x%02x", err); + + if (err) { + goto done; + } + + /* Parse attributes found */ + for (i = 0U; length >= sizeof(rsp->list[i]); + i++, length -= sizeof(rsp->list[i])) { + struct bt_gatt_attr attr = {}; + struct bt_gatt_service_val value; + + start_handle = sys_le16_to_cpu(rsp->list[i].start_handle); + end_handle = sys_le16_to_cpu(rsp->list[i].end_handle); + + BT_DBG("start_handle 0x%04x end_handle 0x%04x", start_handle, + end_handle); + + if (params->type == BT_GATT_DISCOVER_PRIMARY) { + attr.uuid = BT_UUID_GATT_PRIMARY; + } else { + attr.uuid = BT_UUID_GATT_SECONDARY; + } + + value.end_handle = end_handle; + value.uuid = params->uuid; + + attr.handle = start_handle; + attr.user_data = &value; + + if (params->func(conn, &attr, params) == BT_GATT_ITER_STOP) { + return; + } + } + + /* Stop if could not parse the whole PDU */ + if (length > 0) { + goto done; + } + + gatt_discover_next(conn, end_handle, params); + + return; +done: + params->func(conn, NULL, params); +} + +static int gatt_find_type(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct net_buf *buf; + struct bt_att_find_type_req *req; + struct bt_uuid *uuid; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->start_handle = sys_cpu_to_le16(params->start_handle); + req->end_handle = sys_cpu_to_le16(params->end_handle); + + if (params->type == BT_GATT_DISCOVER_PRIMARY) { + uuid = BT_UUID_GATT_PRIMARY; + } else { + uuid = BT_UUID_GATT_SECONDARY; + } + + req->type = sys_cpu_to_le16(BT_UUID_16(uuid)->val); + + BT_DBG("uuid %s start_handle 0x%04x end_handle 0x%04x", + bt_uuid_str(params->uuid), params->start_handle, + params->end_handle); + + switch (params->uuid->type) { + case BT_UUID_TYPE_16: + net_buf_add_le16(buf, BT_UUID_16(params->uuid)->val); + break; + case BT_UUID_TYPE_128: + net_buf_add_mem(buf, BT_UUID_128(params->uuid)->val, 16); + break; + default: + BT_ERR("Unknown UUID type %u", params->uuid->type); + net_buf_unref(buf); + return -EINVAL; + } + + return gatt_send(conn, buf, gatt_find_type_rsp, params, NULL); +} + +static void read_included_uuid_cb(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_discover_params *params = user_data; + struct bt_gatt_include value; + struct bt_gatt_attr *attr; + union { + struct bt_uuid uuid; + struct bt_uuid_128 u128; + } u; + + if (length != 16U) { + BT_ERR("Invalid data len %u", length); + params->func(conn, NULL, params); + return; + } + + value.start_handle = params->_included.start_handle; + value.end_handle = params->_included.end_handle; + value.uuid = &u.uuid; + u.uuid.type = BT_UUID_TYPE_128; + memcpy(u.u128.val, pdu, length); + + BT_DBG("handle 0x%04x uuid %s start_handle 0x%04x " + "end_handle 0x%04x\n", params->_included.attr_handle, + bt_uuid_str(&u.uuid), value.start_handle, value.end_handle); + + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) { + goto next; + } + + attr = (&(struct bt_gatt_attr) { + .uuid = BT_UUID_GATT_INCLUDE, + .user_data = &value, }); + attr->handle = params->_included.attr_handle; + + if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { + return; + } +next: + gatt_discover_next(conn, params->start_handle, params); + + return; +} + +static int read_included_uuid(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct net_buf *buf; + struct bt_att_read_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(params->_included.start_handle); + + BT_DBG("handle 0x%04x", params->_included.start_handle); + + return gatt_send(conn, buf, read_included_uuid_cb, params, NULL); +} + +static u16_t parse_include(struct bt_conn *conn, const void *pdu, + struct bt_gatt_discover_params *params, + u16_t length) +{ + const struct bt_att_read_type_rsp *rsp = pdu; + u16_t handle = 0U; + struct bt_gatt_include value; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + + /* Data can be either in UUID16 or UUID128 */ + switch (rsp->len) { + case 8: /* UUID16 */ + u.uuid.type = BT_UUID_TYPE_16; + break; + case 6: /* UUID128 */ + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 550 + * To get the included service UUID when the included service + * uses a 128-bit UUID, the Read Request is used. + */ + u.uuid.type = BT_UUID_TYPE_128; + break; + default: + BT_ERR("Invalid data len %u", rsp->len); + goto done; + } + + /* Parse include found */ + for (length--, pdu = rsp->data; length >= rsp->len; + length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) { + struct bt_gatt_attr *attr; + const struct bt_att_data *data = pdu; + struct gatt_incl *incl = (void *)data->value; + + handle = sys_le16_to_cpu(data->handle); + /* Handle 0 is invalid */ + if (!handle) { + goto done; + } + + /* Convert include data, bt_gatt_incl and gatt_incl + * have different formats so the conversion have to be done + * field by field. + */ + value.start_handle = sys_le16_to_cpu(incl->start_handle); + value.end_handle = sys_le16_to_cpu(incl->end_handle); + + switch (u.uuid.type) { + case BT_UUID_TYPE_16: + value.uuid = &u.uuid; + u.u16.val = sys_le16_to_cpu(incl->uuid16); + break; + case BT_UUID_TYPE_128: + params->_included.attr_handle = handle; + params->_included.start_handle = value.start_handle; + params->_included.end_handle = value.end_handle; + + return read_included_uuid(conn, params); + } + + BT_DBG("handle 0x%04x uuid %s start_handle 0x%04x " + "end_handle 0x%04x\n", handle, bt_uuid_str(&u.uuid), + value.start_handle, value.end_handle); + + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) { + continue; + } + + attr = (&(struct bt_gatt_attr) { + .uuid = BT_UUID_GATT_INCLUDE, + .user_data = &value, }); + attr->handle = handle; + + if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { + return 0; + } + } + + /* Whole PDU read without error */ + if (length == 0U && handle) { + return handle; + } + +done: + params->func(conn, NULL, params); + return 0; +} + +#define BT_GATT_CHRC(_uuid, _handle, _props) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_chrc, NULL, \ + (&(struct bt_gatt_chrc) { .uuid = _uuid, \ + .value_handle = _handle, \ + .properties = _props, })) + +static u16_t parse_characteristic(struct bt_conn *conn, const void *pdu, + struct bt_gatt_discover_params *params, + u16_t length) +{ + const struct bt_att_read_type_rsp *rsp = pdu; + u16_t handle = 0U; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + + /* Data can be either in UUID16 or UUID128 */ + switch (rsp->len) { + case 7: /* UUID16 */ + u.uuid.type = BT_UUID_TYPE_16; + break; + case 21: /* UUID128 */ + u.uuid.type = BT_UUID_TYPE_128; + break; + default: + BT_ERR("Invalid data len %u", rsp->len); + goto done; + } + + /* Parse characteristics found */ + for (length--, pdu = rsp->data; length >= rsp->len; + length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) { + struct bt_gatt_attr *attr; + const struct bt_att_data *data = pdu; + struct gatt_chrc *chrc = (void *)data->value; + + handle = sys_le16_to_cpu(data->handle); + /* Handle 0 is invalid */ + if (!handle) { + goto done; + } + + switch (u.uuid.type) { + case BT_UUID_TYPE_16: + u.u16.val = sys_le16_to_cpu(chrc->uuid16); + break; + case BT_UUID_TYPE_128: + memcpy(u.u128.val, chrc->uuid, sizeof(chrc->uuid)); + break; + } + + BT_DBG("handle 0x%04x uuid %s properties 0x%02x", handle, + bt_uuid_str(&u.uuid), chrc->properties); + + #if defined(CONFIG_BT_STACK_PTS) + if(event_flag != gatt_discover_chara){ + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) + continue; + } + #else + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) { + continue; + } + #endif + + attr = (&(struct bt_gatt_attr)BT_GATT_CHRC(&u.uuid, + chrc->value_handle, + chrc->properties)); + attr->handle = handle; + + if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { + return 0; + } + } + + /* Whole PDU read without error */ + if (length == 0U && handle) { + return handle; + } + +done: + params->func(conn, NULL, params); + return 0; +} + +static void gatt_read_type_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_discover_params *params = user_data; + u16_t handle; + + BT_DBG("err 0x%02x", err); + + if (err) { + params->func(conn, NULL, params); + return; + } + + if (params->type == BT_GATT_DISCOVER_INCLUDE) { + handle = parse_include(conn, pdu, params, length); + } else { + handle = parse_characteristic(conn, pdu, params, length); + } + + if (!handle) { + return; + } + + gatt_discover_next(conn, handle, params); +} + +static int gatt_read_type(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct net_buf *buf; + struct bt_att_read_type_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->start_handle = sys_cpu_to_le16(params->start_handle); + req->end_handle = sys_cpu_to_le16(params->end_handle); + + if (params->type == BT_GATT_DISCOVER_INCLUDE) { + net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_INCLUDE)->val); + } else { + net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_CHRC)->val); + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle, + params->end_handle); + + return gatt_send(conn, buf, gatt_read_type_rsp, params, NULL); +} + +static u16_t parse_service(struct bt_conn *conn, const void *pdu, + struct bt_gatt_discover_params *params, + u16_t length) +{ + const struct bt_att_read_group_rsp *rsp = pdu; + u16_t start_handle, end_handle = 0U; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + + /* Data can be either in UUID16 or UUID128 */ + switch (rsp->len) { + case 6: /* UUID16 */ + u.uuid.type = BT_UUID_TYPE_16; + break; + case 20: /* UUID128 */ + u.uuid.type = BT_UUID_TYPE_128; + break; + default: + BT_ERR("Invalid data len %u", rsp->len); + goto done; + } + + /* Parse services found */ + for (length--, pdu = rsp->data; length >= rsp->len; + length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) { + struct bt_gatt_attr attr = {}; + struct bt_gatt_service_val value; + const struct bt_att_group_data *data = pdu; + + start_handle = sys_le16_to_cpu(data->start_handle); + if (!start_handle) { + goto done; + } + + end_handle = sys_le16_to_cpu(data->end_handle); + if (!end_handle || end_handle < start_handle) { + goto done; + } + + switch (u.uuid.type) { + case BT_UUID_TYPE_16: + memcpy(&u.u16.val, data->value, sizeof(u.u16.val)); + u.u16.val = sys_le16_to_cpu(u.u16.val); + break; + case BT_UUID_TYPE_128: + memcpy(u.u128.val, data->value, sizeof(u.u128.val)); + break; + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x uuid %s", + start_handle, end_handle, bt_uuid_str(&u.uuid)); + + if (params->type == BT_GATT_DISCOVER_PRIMARY) { + attr.uuid = BT_UUID_GATT_PRIMARY; + } else { + attr.uuid = BT_UUID_GATT_SECONDARY; + } + + value.end_handle = end_handle; + value.uuid = &u.uuid; + + attr.handle = start_handle; + attr.user_data = &value; + + if (params->func(conn, &attr, params) == BT_GATT_ITER_STOP) { + return 0; + } + } + + /* Whole PDU read without error */ + if (length == 0U && end_handle) { + return end_handle; + } + +done: + params->func(conn, NULL, params); + return 0; +} + +static void gatt_read_group_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_discover_params *params = user_data; + u16_t handle; + + BT_DBG("err 0x%02x", err); + + if (err) { + params->func(conn, NULL, params); + return; + } + + handle = parse_service(conn, pdu, params, length); + if (!handle) { + return; + } + + gatt_discover_next(conn, handle, params); +} + +static int gatt_read_group(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct net_buf *buf; + struct bt_att_read_group_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->start_handle = sys_cpu_to_le16(params->start_handle); + req->end_handle = sys_cpu_to_le16(params->end_handle); + + if (params->type == BT_GATT_DISCOVER_PRIMARY) { + net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_PRIMARY)->val); + } else { + net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_SECONDARY)->val); + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle, + params->end_handle); + + return gatt_send(conn, buf, gatt_read_group_rsp, params, NULL); +} + +static void gatt_find_info_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + const struct bt_att_find_info_rsp *rsp = pdu; + struct bt_gatt_discover_params *params = user_data; + u16_t handle = 0U; + u16_t len; + union { + const struct bt_att_info_16 *i16; + const struct bt_att_info_128 *i128; + } info; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_128 u128; + } u; + int i; + bool skip = false; + + BT_DBG("err 0x%02x", err); + + if (err) { + goto done; + } + + /* Data can be either in UUID16 or UUID128 */ + switch (rsp->format) { + case BT_ATT_INFO_16: + u.uuid.type = BT_UUID_TYPE_16; + len = sizeof(*info.i16); + break; + case BT_ATT_INFO_128: + u.uuid.type = BT_UUID_TYPE_128; + len = sizeof(*info.i128); + break; + default: + BT_ERR("Invalid format %u", rsp->format); + goto done; + } + + length--; + + /* Check if there is a least one descriptor in the response */ + if (length < len) { + goto done; + } + + /* Parse descriptors found */ + for (i = length / len, pdu = rsp->info; i != 0; + i--, pdu = (const u8_t *)pdu + len) { + struct bt_gatt_attr *attr; + + info.i16 = pdu; + handle = sys_le16_to_cpu(info.i16->handle); + + if (skip) { + skip = false; + continue; + } + + switch (u.uuid.type) { + case BT_UUID_TYPE_16: + u.u16.val = sys_le16_to_cpu(info.i16->uuid); + break; + case BT_UUID_TYPE_128: + memcpy(u.u128.val, info.i128->uuid, 16); + break; + } + + BT_DBG("handle 0x%04x uuid %s", handle, bt_uuid_str(&u.uuid)); + + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) { + continue; + } + + if (params->type == BT_GATT_DISCOVER_DESCRIPTOR) { + /* Skip attributes that are not considered + * descriptors. + */ + if (!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_PRIMARY) || + !bt_uuid_cmp(&u.uuid, BT_UUID_GATT_SECONDARY) || + !bt_uuid_cmp(&u.uuid, BT_UUID_GATT_INCLUDE)) { + continue; + } + + /* If Characteristic Declaration skip ahead as the next + * entry must be its value. + */ + if (!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_CHRC)) { + skip = true; + continue; + } + } + + attr = (&(struct bt_gatt_attr) + BT_GATT_DESCRIPTOR(&u.uuid, 0, NULL, NULL, NULL)); + attr->handle = handle; + + if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) { + return; + } + } + + gatt_discover_next(conn, handle, params); + + return; + +done: + params->func(conn, NULL, params); +} + +static int gatt_find_info(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct net_buf *buf; + struct bt_att_find_info_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->start_handle = sys_cpu_to_le16(params->start_handle); + req->end_handle = sys_cpu_to_le16(params->end_handle); + + BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle, + params->end_handle); + + return gatt_send(conn, buf, gatt_find_info_rsp, params, NULL); +} + +int bt_gatt_discover(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(params && params->func, "invalid parameters\n"); + __ASSERT((params->start_handle && params->end_handle), + "invalid parameters\n"); + __ASSERT((params->start_handle <= params->end_handle), + "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + switch (params->type) { + case BT_GATT_DISCOVER_PRIMARY: + case BT_GATT_DISCOVER_SECONDARY: + if (params->uuid) { + return gatt_find_type(conn, params); + } + return gatt_read_group(conn, params); + case BT_GATT_DISCOVER_INCLUDE: + case BT_GATT_DISCOVER_CHARACTERISTIC: + return gatt_read_type(conn, params); + case BT_GATT_DISCOVER_DESCRIPTOR: + /* Only descriptors can be filtered */ + if (params->uuid && + (!bt_uuid_cmp(params->uuid, BT_UUID_GATT_PRIMARY) || + !bt_uuid_cmp(params->uuid, BT_UUID_GATT_SECONDARY) || + !bt_uuid_cmp(params->uuid, BT_UUID_GATT_INCLUDE) || + !bt_uuid_cmp(params->uuid, BT_UUID_GATT_CHRC))) { + return -EINVAL; + } + + #if defined(BFLB_BLE) + __attribute__((fallthrough)); + #endif + + /* Fallthrough. */ + case BT_GATT_DISCOVER_ATTRIBUTE: + return gatt_find_info(conn, params); + default: + BT_ERR("Invalid discovery type: %u", params->type); + } + + return -EINVAL; +} + +static void parse_read_by_uuid(struct bt_conn *conn, + struct bt_gatt_read_params *params, + const void *pdu, u16_t length) +{ + const struct bt_att_read_type_rsp *rsp = pdu; + + /* Parse values found */ + for (length--, pdu = rsp->data; length; + length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) { + const struct bt_att_data *data = pdu; + u16_t handle; + u16_t len; + + handle = sys_le16_to_cpu(data->handle); + + /* Handle 0 is invalid */ + if (!handle) { + BT_ERR("Invalid handle"); + return; + } + + len = rsp->len > length ? length - 2 : rsp->len - 2; + + BT_DBG("handle 0x%04x len %u value %u", handle, rsp->len, len); + + /* Update start_handle */ + params->by_uuid.start_handle = handle; + + if (params->func(conn, 0, params, data->value, len) == + BT_GATT_ITER_STOP) { + return; + } + + /* Check if long attribute */ + if (rsp->len > length) { + break; + } + + /* Stop if it's the last handle to be read */ + if (params->by_uuid.start_handle == params->by_uuid.end_handle) { + params->func(conn, 0, params, NULL, 0); + return; + } + + params->by_uuid.start_handle++; + } + + /* Continue reading the attributes */ + if (bt_gatt_read(conn, params) < 0) { + params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0); + } +} + +static void gatt_read_rsp(struct bt_conn *conn, u8_t err, const void *pdu, + u16_t length, void *user_data) +{ + struct bt_gatt_read_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + if (err || !length) { + params->func(conn, err, params, NULL, 0); + return; + } + + if (!params->handle_count) { + parse_read_by_uuid(conn, params, pdu, length); + return; + } + + if (params->func(conn, 0, params, pdu, length) == BT_GATT_ITER_STOP) { + return; + } + + /* + * Core Spec 4.2, Vol. 3, Part G, 4.8.1 + * If the Characteristic Value is greater than (ATT_MTU - 1) octets + * in length, the Read Long Characteristic Value procedure may be used + * if the rest of the Characteristic Value is required. + */ + if (length < (bt_att_get_mtu(conn) - 1)) { + params->func(conn, 0, params, NULL, 0); + return; + } + + params->single.offset += length; + + /* Continue reading the attribute */ + if (bt_gatt_read(conn, params) < 0) { + params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0); + } + + +} + +static int gatt_read_blob(struct bt_conn *conn, + struct bt_gatt_read_params *params) +{ + struct net_buf *buf; + struct bt_att_read_blob_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_BLOB_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(params->single.handle); + req->offset = sys_cpu_to_le16(params->single.offset); + + BT_DBG("handle 0x%04x offset 0x%04x", params->single.handle, + params->single.offset); + + return gatt_send(conn, buf, gatt_read_rsp, params, NULL); +} + +static int gatt_read_uuid(struct bt_conn *conn, + struct bt_gatt_read_params *params) +{ + struct net_buf *buf; + struct bt_att_read_type_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->start_handle = sys_cpu_to_le16(params->by_uuid.start_handle); + req->end_handle = sys_cpu_to_le16(params->by_uuid.end_handle); + + if (params->by_uuid.uuid->type == BT_UUID_TYPE_16) { + net_buf_add_le16(buf, BT_UUID_16(params->by_uuid.uuid)->val); + } else { + net_buf_add_mem(buf, BT_UUID_128(params->by_uuid.uuid)->val, 16); + } + + BT_DBG("start_handle 0x%04x end_handle 0x%04x uuid %s", + params->by_uuid.start_handle, params->by_uuid.end_handle, + bt_uuid_str(params->by_uuid.uuid)); + + return gatt_send(conn, buf, gatt_read_rsp, params, NULL); +} + +#if defined(CONFIG_BT_GATT_READ_MULTIPLE) +static void gatt_read_multiple_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_read_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + if (err || !length) { + params->func(conn, err, params, NULL, 0); + return; + } + + params->func(conn, 0, params, pdu, length); + + /* mark read as complete since read multiple is single response */ + params->func(conn, 0, params, NULL, 0); +} + +static int gatt_read_multiple(struct bt_conn *conn, + struct bt_gatt_read_params *params) +{ + struct net_buf *buf; + u8_t i; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ, + params->handle_count * sizeof(u16_t)); + if (!buf) { + return -ENOMEM; + } + + for (i = 0U; i < params->handle_count; i++) { + net_buf_add_le16(buf, params->handles[i]); + } + + return gatt_send(conn, buf, gatt_read_multiple_rsp, params, NULL); +} +#else +static int gatt_read_multiple(struct bt_conn *conn, + struct bt_gatt_read_params *params) +{ + return -ENOTSUP; +} +#endif /* CONFIG_BT_GATT_READ_MULTIPLE */ + +int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params) +{ + struct net_buf *buf; + struct bt_att_read_req *req; + + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(params && params->func, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (params->handle_count == 0) { + return gatt_read_uuid(conn, params); + } + + if (params->handle_count > 1) { + return gatt_read_multiple(conn, params); + } + + if (params->single.offset) { + return gatt_read_blob(conn, params); + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(params->single.handle); + + BT_DBG("handle 0x%04x", params->single.handle); + + return gatt_send(conn, buf, gatt_read_rsp, params, NULL); +} + +static void gatt_write_rsp(struct bt_conn *conn, u8_t err, const void *pdu, + u16_t length, void *user_data) +{ + struct bt_gatt_write_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + params->func(conn, err, params); +} + +int bt_gatt_write_without_response_cb(struct bt_conn *conn, u16_t handle, + const void *data, u16_t length, bool sign, + bt_gatt_complete_func_t func, + void *user_data) +{ + struct net_buf *buf; + struct bt_att_write_cmd *cmd; + + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(handle, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + +#if defined(CONFIG_BT_SMP) + if (conn->encrypt) { + /* Don't need to sign if already encrypted */ + sign = false; + } +#endif + + if (sign) { + buf = bt_att_create_pdu(conn, BT_ATT_OP_SIGNED_WRITE_CMD, + sizeof(*cmd) + length + 12); + } else { + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_CMD, + sizeof(*cmd) + length); + } + if (!buf) { + return -ENOMEM; + } + + cmd = net_buf_add(buf, sizeof(*cmd)); + cmd->handle = sys_cpu_to_le16(handle); + memcpy(cmd->value, data, length); + net_buf_add(buf, length); + + BT_DBG("handle 0x%04x length %u", handle, length); + + return bt_att_send(conn, buf, func, user_data); +} + +static int gatt_exec_write(struct bt_conn *conn, + struct bt_gatt_write_params *params) +{ + struct net_buf *buf; + struct bt_att_exec_write_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_REQ, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + #if defined(CONFIG_BT_STACK_PTS) + if(event_flag == gatt_cancel_write_req) + req->flags = BT_ATT_FLAG_CANCEL; + else + req->flags = BT_ATT_FLAG_EXEC; + #else + req->flags = BT_ATT_FLAG_EXEC; + #endif + + BT_DBG(""); + + return gatt_send(conn, buf, gatt_write_rsp, params, NULL); +} + +static void gatt_prepare_write_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_write_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + + /* Don't continue in case of error */ + if (err) { + params->func(conn, err, params); + return; + } + /* If there is no more data execute */ + if (!params->length) { + gatt_exec_write(conn, params); + return; + } + + /* Write next chunk */ + bt_gatt_write(conn, params); + +} + +static int gatt_prepare_write(struct bt_conn *conn, + struct bt_gatt_write_params *params) + +{ + struct net_buf *buf; + struct bt_att_prepare_write_req *req; + u16_t len; + + len = MIN(params->length, bt_att_get_mtu(conn) - sizeof(*req) - 1); + + buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_REQ, + sizeof(*req) + len); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(params->handle); + req->offset = sys_cpu_to_le16(params->offset); + memcpy(req->value, params->data, len); + net_buf_add(buf, len); + + /* Update params */ + params->offset += len; + params->data = (const u8_t *)params->data + len; + params->length -= len; + + BT_DBG("handle 0x%04x offset %u len %u", params->handle, params->offset, + params->length); + + return gatt_send(conn, buf, gatt_prepare_write_rsp, params, NULL); +} + +#if defined(CONFIG_BT_STACK_PTS) +int bt_gatt_prepare_write(struct bt_conn *conn, + struct bt_gatt_write_params *params) +{ + return gatt_prepare_write(conn, params); +} +#endif + + +int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params) +{ + struct net_buf *buf; + struct bt_att_write_req *req; + + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(params && params->func, "invalid parameters\n"); + __ASSERT(params->handle, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + /* Use Prepare Write if offset is set or Long Write is required */ + if (params->offset || + params->length > (bt_att_get_mtu(conn) - sizeof(*req) - 1)) { + return gatt_prepare_write(conn, params); + } + + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ, + sizeof(*req) + params->length); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(params->handle); + memcpy(req->value, params->data, params->length); + net_buf_add(buf, params->length); + + BT_DBG("handle 0x%04x length %u", params->handle, params->length); + + return gatt_send(conn, buf, gatt_write_rsp, params, NULL); +} + +static void gatt_subscription_add(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + bt_addr_le_copy(¶ms->_peer, &conn->le.dst); + + /* Prepend subscription */ + sys_slist_prepend(&subscriptions, ¶ms->node); +} + +static void gatt_write_ccc_rsp(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data) +{ + struct bt_gatt_subscribe_params *params = user_data; + + BT_DBG("err 0x%02x", err); + + atomic_clear_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING); + + /* if write to CCC failed we remove subscription and notify app */ + if (err) { + sys_snode_t *node, *tmp, *prev = NULL; + UNUSED(prev); + + SYS_SLIST_FOR_EACH_NODE_SAFE(&subscriptions, node, tmp) { + if (node == ¶ms->node) { + gatt_subscription_remove(conn, tmp, params); + break; + } + + prev = node; + } + } else if (!params->value) { + /* Notify with NULL data to complete unsubscribe */ + params->notify(conn, params, NULL, 0); + } + #if defined(BFLB_BLE_PATCH_NOTIFY_WRITE_CCC_RSP) + else{ + params->notify(conn, params, NULL, 0); + } + #endif +} + +static int gatt_write_ccc(struct bt_conn *conn, u16_t handle, u16_t value, + bt_att_func_t func, + struct bt_gatt_subscribe_params *params) +{ + struct net_buf *buf; + struct bt_att_write_req *req; + + buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ, + sizeof(*req) + sizeof(u16_t)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->handle = sys_cpu_to_le16(handle); + net_buf_add_le16(buf, value); + + BT_DBG("handle 0x%04x value 0x%04x", handle, value); + + atomic_set_bit(params->flags, BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING); + + return gatt_send(conn, buf, func, params, NULL); +} + +int bt_gatt_subscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + struct bt_gatt_subscribe_params *tmp; + bool has_subscription = false; + + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(params && params->notify, "invalid parameters\n"); + __ASSERT(params->value, "invalid parameters\n"); + __ASSERT(params->ccc_handle, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, tmp, node) { + /* Fail if entry already exists */ + if (tmp == params) { + return -EALREADY; + } + + /* Check if another subscription exists */ + if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) && + tmp->value_handle == params->value_handle && + tmp->value >= params->value) { + has_subscription = true; + } + } + + /* Skip write if already subscribed */ + if (!has_subscription) { + int err; + + err = gatt_write_ccc(conn, params->ccc_handle, params->value, + gatt_write_ccc_rsp, params); + if (err) { + return err; + } + } + + /* + * Add subscription before write complete as some implementation were + * reported to send notification before reply to CCC write. + */ + gatt_subscription_add(conn, params); + + return 0; +} + +int bt_gatt_unsubscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + struct bt_gatt_subscribe_params *tmp, *next; + bool has_subscription = false, found = false; + sys_snode_t *prev = NULL; + + __ASSERT(conn, "invalid parameters\n"); + __ASSERT(params, "invalid parameters\n"); + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, tmp, next, node) { + /* Remove subscription */ + if (params == tmp) { + found = true; + sys_slist_remove(&subscriptions, prev, &tmp->node); + /* Attempt to cancel if write is pending */ + if (atomic_test_bit(params->flags, + BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING)) { + bt_gatt_cancel(conn, params); + } + continue; + } else { + prev = &tmp->node; + } + + /* Check if there still remains any other subscription */ + if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) && + tmp->value_handle == params->value_handle) { + has_subscription = true; + } + } + + if (!found) { + return -EINVAL; + } + + if (has_subscription) { + /* Notify with NULL data to complete unsubscribe */ + params->notify(conn, params, NULL, 0); + return 0; + } + + params->value = 0x0000; + + return gatt_write_ccc(conn, params->ccc_handle, params->value, + gatt_write_ccc_rsp, params); +} + +void bt_gatt_cancel(struct bt_conn *conn, void *params) +{ + bt_att_req_cancel(conn, params); +} + +static void add_subscriptions(struct bt_conn *conn) +{ + struct bt_gatt_subscribe_params *params; + + /* Lookup existing subscriptions */ + SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, params, node) { + if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) { + continue; + } + + /* Force write to CCC to workaround devices that don't track + * it properly. + */ + gatt_write_ccc(conn, params->ccc_handle, params->value, + gatt_write_ccc_rsp, params); + } +} + +#endif /* CONFIG_BT_GATT_CLIENT */ + +#define CCC_STORE_MAX 48 + +static struct bt_gatt_ccc_cfg *ccc_find_cfg(struct _bt_gatt_ccc *ccc, + const bt_addr_le_t *addr, + u8_t id) +{ + for (size_t i = 0; i < ARRAY_SIZE(ccc->cfg); i++) { + if (id == ccc->cfg[i].id && + !bt_addr_le_cmp(&ccc->cfg[i].peer, addr)) { + return &ccc->cfg[i]; + } + } + + return NULL; +} + +struct addr_with_id { + const bt_addr_le_t *addr; + u8_t id; +}; + +struct ccc_load { + struct addr_with_id addr_with_id; + struct ccc_store *entry; + size_t count; +}; + +static void ccc_clear(struct _bt_gatt_ccc *ccc, + const bt_addr_le_t *addr, + u8_t id) +{ + struct bt_gatt_ccc_cfg *cfg; + + cfg = ccc_find_cfg(ccc, addr, id); + if (!cfg) { + BT_DBG("Unable to clear CCC: cfg not found"); + return; + } + + clear_ccc_cfg(cfg); +} + +static u8_t ccc_load(const struct bt_gatt_attr *attr, void *user_data) +{ + struct ccc_load *load = user_data; + struct _bt_gatt_ccc *ccc; + struct bt_gatt_ccc_cfg *cfg; + + /* Check if attribute is a CCC */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Clear if value was invalidated */ + if (!load->entry) { + ccc_clear(ccc, load->addr_with_id.addr, load->addr_with_id.id); + return BT_GATT_ITER_CONTINUE; + } else if (!load->count) { + return BT_GATT_ITER_STOP; + } + + /* Skip if value is not for the given attribute */ + if (load->entry->handle != attr->handle) { + /* If attribute handle is bigger then it means + * the attribute no longer exists and cannot + * be restored. + */ + if (load->entry->handle < attr->handle) { + BT_DBG("Unable to restore CCC: handle 0x%04x cannot be" + " found", load->entry->handle); + goto next; + } + return BT_GATT_ITER_CONTINUE; + } + + BT_DBG("Restoring CCC: handle 0x%04x value 0x%04x", load->entry->handle, + load->entry->value); + + cfg = ccc_find_cfg(ccc, load->addr_with_id.addr, load->addr_with_id.id); + if (!cfg) { + cfg = ccc_find_cfg(ccc, BT_ADDR_LE_ANY, 0); + if (!cfg) { + BT_DBG("Unable to restore CCC: no cfg left"); + goto next; + } + bt_addr_le_copy(&cfg->peer, load->addr_with_id.addr); + cfg->id = load->addr_with_id.id; + } + + cfg->value = load->entry->value; + +next: + load->entry++; + load->count--; + + return load->count ? BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; +} + +#if defined(BFLB_BLE) +static int ccc_set(const char *key, u8_t id, bt_addr_le_t *addr) +#else +static int ccc_set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +#endif +{ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + struct ccc_store ccc_store[CCC_STORE_MAX]; + struct ccc_load load; + #if defined(BFLB_BLE) + size_t len; + int err; + #else + bt_addr_le_t addr; + const char *next; + int len, err; + #endif + + #if defined(BFLB_BLE) + err = bt_settings_get_bin(key, (u8_t *)ccc_store, CCC_STORE_MAX, &len); + if(err) + return err; + + load.addr_with_id.id = id; + load.addr_with_id.addr = addr; + load.entry = ccc_store; + load.count = len / sizeof(*ccc_store); + #else + settings_name_next(name, &next); + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } else if (!next) { + load.addr_with_id.id = BT_ID_DEFAULT; + } else { + load.addr_with_id.id = strtol(next, NULL, 10); + } + + err = bt_settings_decode_key(name, &addr); + if (err) { + BT_ERR("Unable to decode address %s", log_strdup(name)); + return -EINVAL; + } + + load.addr_with_id.addr = &addr; + + if (len_rd) { + len = read_cb(cb_arg, ccc_store, sizeof(ccc_store)); + + if (len < 0) { + BT_ERR("Failed to decode value (err %d)", len); + return len; + } + + load.entry = ccc_store; + load.count = len / sizeof(*ccc_store); + + for (int i = 0; i < load.count; i++) { + BT_DBG("Read CCC: handle 0x%04x value 0x%04x", + ccc_store[i].handle, ccc_store[i].value); + } + } else { + load.entry = NULL; + load.count = 0; + } + #endif + + bt_gatt_foreach_attr(0x0001, 0xffff, ccc_load, &load); + + BT_DBG("Restored CCC for id:%x" "PRIu8" " addr:%s", + load.addr_with_id.id, + bt_addr_le_str(load.addr_with_id.addr)); + } + + return 0; +} + +#if !defined(BFLB_BLE) +#if !IS_ENABLED(CONFIG_BT_SETTINGS_CCC_LAZY_LOADING) +/* Only register the ccc_set settings handler when not loading on-demand */ +SETTINGS_STATIC_HANDLER_DEFINE(bt_ccc, "bt/ccc", NULL, ccc_set, NULL, NULL); +#endif /* CONFIG_BT_SETTINGS_CCC_LAZY_LOADING */ +#endif + +#if !defined(BFLB_BLE) +static int ccc_set_direct(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + const char *name; + + BT_DBG("key: %s", log_strdup((const char *)param)); + + /* Only "bt/ccc" settings should ever come here */ + if (!settings_name_steq((const char *)param, "bt/ccc", &name)) { + BT_ERR("Invalid key"); + return -EINVAL; + } + + return ccc_set(name, len, read_cb, cb_arg); + } + return 0; +} +#endif + +#if defined(BFLB_BLE) +#if defined(CONFIG_BT_GATT_SERVICE_CHANGED) +#if defined(CONFIG_BT_SETTINGS) +static int sc_set(u8_t id, bt_addr_le_t *addr); +static int sc_commit(void); +#endif +#endif +#endif +void bt_gatt_connected(struct bt_conn *conn) +{ + struct conn_data data; + + BT_DBG("conn %p", conn); + + data.conn = conn; + data.sec = BT_SECURITY_L1; + + /* Load CCC settings from backend if bonded */ + if (IS_ENABLED(CONFIG_BT_SETTINGS_CCC_LAZY_LOADING) && + bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + char key[BT_SETTINGS_KEY_MAX]; + + if (conn->id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), conn->id); + bt_settings_encode_key(key, sizeof(key), "ccc", + &conn->le.dst, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "ccc", + &conn->le.dst, NULL); + } + #if defined(BFLB_BLE) + ccc_set(key, conn->id, &conn->le.dst); + #else + settings_load_subtree_direct(key, ccc_set_direct, (void *)key); + #endif + } + + bt_gatt_foreach_attr(0x0001, 0xffff, update_ccc, &data); + + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part C page 2192: + * + * 10.3.1.1 Handling of GATT indications and notifications + * + * A client requests a server to send indications and notifications + * by appropriately configuring the server via a Client Characteristic + * Configuration Descriptor. Since the configuration is persistent + * across a disconnection and reconnection, security requirements must + * be checked against the configuration upon a reconnection before + * sending indications or notifications. When a server reconnects to a + * client to send an indication or notification for which security is + * required, the server shall initiate or request encryption with the + * client prior to sending an indication or notification. If the client + * does not have an LTK indicating that the client has lost the bond, + * enabling encryption will fail. + */ + if (IS_ENABLED(CONFIG_BT_SMP) && + bt_conn_get_security(conn) < data.sec) { + bt_conn_set_security(conn, data.sec); + } + +#if defined(CONFIG_BT_GATT_CLIENT) + add_subscriptions(conn); +#endif /* CONFIG_BT_GATT_CLIENT */ + +#if defined(BFLB_BLE) +#if defined(CONFIG_BT_GATT_SERVICE_CHANGED) +#if defined(CONFIG_BT_SETTINGS) + sc_set(conn->id, &conn->le.dst); + sc_commit(); +#endif +#endif +#endif +} + +void bt_gatt_encrypt_change(struct bt_conn *conn) +{ + struct conn_data data; + + BT_DBG("conn %p", conn); + + data.conn = conn; + data.sec = BT_SECURITY_L1; + + bt_gatt_foreach_attr(0x0001, 0xffff, update_ccc, &data); +} + +bool bt_gatt_change_aware(struct bt_conn *conn, bool req) +{ +#if defined(CONFIG_BT_GATT_CACHING) + struct gatt_cf_cfg *cfg; + + cfg = find_cf_cfg(conn); + if (!cfg || !CF_ROBUST_CACHING(cfg)) { + return true; + } + + if (atomic_test_bit(cfg->flags, CF_CHANGE_AWARE)) { + return true; + } + + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350: + * If a change-unaware client sends an ATT command, the server shall + * ignore it. + */ + if (!req) { + return false; + } + + /* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347: + * 2.5.2.1 Robust Caching + * A connected client becomes change-aware when... + * The server sends the client a response with the error code set to + * Database Out Of Sync and then the server receives another ATT + * request from the client. + */ + if (atomic_test_bit(cfg->flags, CF_OUT_OF_SYNC)) { + atomic_clear_bit(cfg->flags, CF_OUT_OF_SYNC); + atomic_set_bit(cfg->flags, CF_CHANGE_AWARE); + BT_DBG("%s change-aware", bt_addr_le_str(&cfg->peer)); + return true; + } + + atomic_set_bit(cfg->flags, CF_OUT_OF_SYNC); + + return false; +#else + return true; +#endif +} + +static int bt_gatt_store_cf(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_GATT_CACHING) + struct gatt_cf_cfg *cfg; + char key[BT_SETTINGS_KEY_MAX]; + char *str; + size_t len; + int err; + + cfg = find_cf_cfg(conn); + if (!cfg) { + /* No cfg found, just clear it */ + BT_DBG("No config for CF"); + str = NULL; + len = 0; + } else { + str = (char *)cfg->data; + len = sizeof(cfg->data); + + if (conn->id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), conn->id); + bt_settings_encode_key(key, sizeof(key), "cf", + &conn->le.dst, id_str); + } + } + + if (!cfg || !conn->id) { + bt_settings_encode_key(key, sizeof(key), "cf", + &conn->le.dst, NULL); + } + + #if defined(BFLB_BLE) + err = settings_save_one(key, (u8_t*)str, len); + #else + err = settings_save_one(key, str, len); + #endif + if (err) { + BT_ERR("Failed to store Client Features (err %d)", err); + return err; + } + + BT_DBG("Stored CF for %s (%s)", bt_addr_le_str(&conn->le.dst), log_strdup(key)); +#endif /* CONFIG_BT_GATT_CACHING */ + return 0; + +} + +void bt_gatt_disconnected(struct bt_conn *conn) +{ + BT_DBG("conn %p", conn); + bt_gatt_foreach_attr(0x0001, 0xffff, disconnected_cb, conn); + +#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE) + gatt_ccc_conn_unqueue(conn); + + if (gatt_ccc_conn_queue_is_empty()) { + k_delayed_work_cancel(&gatt_ccc_store.work); + } +#endif + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + bt_addr_le_is_bonded(conn->id, &conn->le.dst)) { + bt_gatt_store_ccc(conn->id, &conn->le.dst); + bt_gatt_store_cf(conn); + } + +#if defined(CONFIG_BT_GATT_CLIENT) + remove_subscriptions(conn); +#endif /* CONFIG_BT_GATT_CLIENT */ + +#if defined(CONFIG_BT_GATT_CACHING) + remove_cf_cfg(conn); +#endif +} + +#if defined(BFLB_BLE_MTU_CHANGE_CB) +void bt_gatt_mtu_changed(struct bt_conn *conn, u16_t mtu) +{ + if(gatt_mtu_changed_cb) + gatt_mtu_changed_cb(conn, (int)mtu); +} + +void bt_gatt_register_mtu_callback(bt_gatt_mtu_changed_cb_t cb) +{ + gatt_mtu_changed_cb = cb; +} +#endif + +#if defined(CONFIG_BT_SETTINGS) + +struct ccc_save { + struct addr_with_id addr_with_id; + struct ccc_store store[CCC_STORE_MAX]; + size_t count; +}; + +static u8_t ccc_save(const struct bt_gatt_attr *attr, void *user_data) +{ + struct ccc_save *save = user_data; + struct _bt_gatt_ccc *ccc; + struct bt_gatt_ccc_cfg *cfg; + + /* Check if attribute is a CCC */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Check if there is a cfg for the peer */ + cfg = ccc_find_cfg(ccc, save->addr_with_id.addr, save->addr_with_id.id); + if (!cfg) { + return BT_GATT_ITER_CONTINUE; + } + + BT_DBG("Storing CCCs handle 0x%04x value 0x%04x", attr->handle, + cfg->value); + + save->store[save->count].handle = attr->handle; + save->store[save->count].value = cfg->value; + save->count++; + + return BT_GATT_ITER_CONTINUE; +} + +int bt_gatt_store_ccc(u8_t id, const bt_addr_le_t *addr) +{ + struct ccc_save save; + char key[BT_SETTINGS_KEY_MAX]; + size_t len; + char *str; + int err; + + save.addr_with_id.addr = addr; + save.addr_with_id.id = id; + save.count = 0; + + bt_gatt_foreach_attr(0x0001, 0xffff, ccc_save, &save); + + if (id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), id); + bt_settings_encode_key(key, sizeof(key), "ccc", + (bt_addr_le_t *)addr, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "ccc", + (bt_addr_le_t *)addr, NULL); + } + + if (save.count) { + str = (char *)save.store; + len = save.count * sizeof(*save.store); + } else { + /* No entries to encode, just clear */ + str = NULL; + len = 0; + } + + err = settings_save_one(key, (const u8_t *)str, len); + if (err) { + BT_ERR("Failed to store CCCs (err %d)", err); + return err; + } + + BT_DBG("Stored CCCs for %s (%s)", bt_addr_le_str(addr), + log_strdup(key)); + if (len) { + for (int i = 0; i < save.count; i++) { + BT_DBG(" CCC: handle 0x%04x value 0x%04x", + save.store[i].handle, save.store[i].value); + } + } else { + BT_DBG(" CCC: NULL"); + } + + return 0; +} + +static u8_t remove_peer_from_attr(const struct bt_gatt_attr *attr, + void *user_data) +{ + const struct addr_with_id *addr_with_id = user_data; + struct _bt_gatt_ccc *ccc; + struct bt_gatt_ccc_cfg *cfg; + + /* Check if attribute is a CCC */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Check if there is a cfg for the peer */ + cfg = ccc_find_cfg(ccc, addr_with_id->addr, addr_with_id->id); + if (cfg) { + memset(cfg, 0, sizeof(*cfg)); + } + + return BT_GATT_ITER_CONTINUE; +} + +static int bt_gatt_clear_ccc(u8_t id, const bt_addr_le_t *addr) +{ + char key[BT_SETTINGS_KEY_MAX]; + struct addr_with_id addr_with_id = { + .addr = addr, + .id = id, + }; + + if (id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), id); + bt_settings_encode_key(key, sizeof(key), "ccc", + (bt_addr_le_t *)addr, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "ccc", + (bt_addr_le_t *)addr, NULL); + } + + bt_gatt_foreach_attr(0x0001, 0xffff, remove_peer_from_attr, + &addr_with_id); + + return settings_delete(key); +} + +#if defined(CONFIG_BT_GATT_CACHING) +static struct gatt_cf_cfg *find_cf_cfg_by_addr(const bt_addr_le_t *addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) { + if (!bt_addr_le_cmp(addr, &cf_cfg[i].peer)) { + return &cf_cfg[i]; + } + } + + return NULL; +} +#endif /* CONFIG_BT_GATT_CACHING */ + +static int bt_gatt_clear_cf(u8_t id, const bt_addr_le_t *addr) +{ +#if defined(CONFIG_BT_GATT_CACHING) + char key[BT_SETTINGS_KEY_MAX]; + struct gatt_cf_cfg *cfg; + + if (id) { + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), id); + bt_settings_encode_key(key, sizeof(key), "cf", + (bt_addr_le_t *)addr, id_str); + } else { + bt_settings_encode_key(key, sizeof(key), "cf", + (bt_addr_le_t *)addr, NULL); + } + + cfg = find_cf_cfg_by_addr(addr); + if (cfg) { + clear_cf_cfg(cfg); + } + + return settings_delete(key); +#endif /* CONFIG_BT_GATT_CACHING */ + return 0; + +} + +static int sc_clear_by_addr(u8_t id, const bt_addr_le_t *addr) +{ + if (IS_ENABLED(CONFIG_BT_GATT_SERVICE_CHANGED)) { + struct gatt_sc_cfg *cfg; + + cfg = find_sc_cfg(id, (bt_addr_le_t *)addr); + if (cfg) { + sc_clear(cfg); + } + } + return 0; +} + +static void bt_gatt_clear_subscriptions(const bt_addr_le_t *addr) +{ +#if defined(CONFIG_BT_GATT_CLIENT) + struct bt_gatt_subscribe_params *params, *tmp; + sys_snode_t *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) { + if (bt_addr_le_cmp(addr, ¶ms->_peer)) { + prev = ¶ms->node; + continue; + } + params->value = 0U; + gatt_subscription_remove(NULL, prev, params); + } +#endif /* CONFIG_BT_GATT_CLIENT */ +} + +int bt_gatt_clear(u8_t id, const bt_addr_le_t *addr) +{ + int err; + + err = bt_gatt_clear_ccc(id, addr); + if (err < 0) { + return err; + } + + err = sc_clear_by_addr(id, addr); + if (err < 0) { + return err; + } + + err = bt_gatt_clear_cf(id, addr); + if (err < 0) { + return err; + } + + bt_gatt_clear_subscriptions(addr); + + return 0; +} + +#if defined(CONFIG_BT_GATT_SERVICE_CHANGED) +#if defined(BFLB_BLE) +static int sc_set(u8_t id, bt_addr_le_t *addr) +#else +static int sc_set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +#endif +{ + struct gatt_sc_cfg *cfg; + #if !defined(BFLB_BLE) + u8_t id; + bt_addr_le_t addr; + int len, err; + const char *next; + #endif + + #if defined(BFLB_BLE) + int err; + char key[BT_SETTINGS_KEY_MAX]; + + cfg = find_sc_cfg(id, addr); + if (!cfg) { + /* Find and initialize a free sc_cfg entry */ + cfg = find_sc_cfg(BT_ID_DEFAULT, BT_ADDR_LE_ANY); + if (!cfg) { + BT_ERR("Unable to restore SC: no cfg left"); + return -ENOMEM; + } + + cfg->id = id; + bt_addr_le_copy(&cfg->peer, addr); + } + + if(id){ + char id_str[4]; + + u8_to_dec(id_str, sizeof(id_str), id); + bt_settings_encode_key(key, sizeof(key), "sc", + addr, id_str); + }else{ + bt_settings_encode_key(key, sizeof(key), "sc", + addr, NULL); + } + + err = bt_settings_get_bin(key, (u8_t *)cfg, sizeof(*cfg), NULL); + if(err) + memset(cfg, 0, sizeof(*cfg)); + return err; + #else + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + err = bt_settings_decode_key(name, &addr); + if (err) { + BT_ERR("Unable to decode address %s", log_strdup(name)); + return -EINVAL; + } + + settings_name_next(name, &next); + + if (!next) { + id = BT_ID_DEFAULT; + } else { + id = strtol(next, NULL, 10); + } + + cfg = find_sc_cfg(id, &addr); + if (!cfg && len_rd) { + /* Find and initialize a free sc_cfg entry */ + cfg = find_sc_cfg(BT_ID_DEFAULT, BT_ADDR_LE_ANY); + if (!cfg) { + BT_ERR("Unable to restore SC: no cfg left"); + return -ENOMEM; + } + + cfg->id = id; + bt_addr_le_copy(&cfg->peer, &addr); + } + + if (len_rd) { + len = read_cb(cb_arg, &cfg->data, sizeof(cfg->data)); + if (len < 0) { + BT_ERR("Failed to decode value (err %d)", len); + return len; + } + BT_DBG("Read SC: len %d", len); + + BT_DBG("Restored SC for %s", bt_addr_le_str(&addr)); + } else if (cfg) { + /* Clear configuration */ + memset(cfg, 0, sizeof(*cfg)); + + BT_DBG("Removed SC for %s", bt_addr_le_str(&addr)); + } + + return 0; + #endif +} + +static int sc_commit(void) +{ + atomic_clear_bit(gatt_sc.flags, SC_INDICATE_PENDING); + + if (atomic_test_bit(gatt_sc.flags, SC_RANGE_CHANGED)) { + /* Schedule SC indication since the range has changed */ + k_delayed_work_submit(&gatt_sc.work, SC_TIMEOUT); + } + + return 0; +} + +#if !defined(BFLB_BLE) +SETTINGS_STATIC_HANDLER_DEFINE(bt_sc, "bt/sc", NULL, sc_set, sc_commit, NULL); +#endif +#endif /* CONFIG_BT_GATT_SERVICE_CHANGED */ + +#if defined(CONFIG_BT_GATT_CACHING) +static int cf_set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + struct gatt_cf_cfg *cfg; + bt_addr_le_t addr; + int len, err; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + err = bt_settings_decode_key(name, &addr); + if (err) { + BT_ERR("Unable to decode address %s", log_strdup(name)); + return -EINVAL; + } + + cfg = find_cf_cfg_by_addr(&addr); + if (!cfg) { + cfg = find_cf_cfg(NULL); + if (!cfg) { + BT_ERR("Unable to restore CF: no cfg left"); + return 0; + } + } + + if (len_rd) { + len = read_cb(cb_arg, cfg->data, sizeof(cfg->data)); + if (len < 0) { + BT_ERR("Failed to decode value (err %d)", len); + return len; + } + + BT_DBG("Read CF: len %d", len); + } else { + clear_cf_cfg(cfg); + } + + BT_DBG("Restored CF for %s", bt_addr_le_str(&addr)); + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt_cf, "bt/cf", NULL, cf_set, NULL, NULL); + +static u8_t stored_hash[16]; + +static int db_hash_set(const char *name, size_t len_rd, + settings_read_cb read_cb, void *cb_arg) +{ + int len; + + len = read_cb(cb_arg, stored_hash, sizeof(stored_hash)); + if (len < 0) { + BT_ERR("Failed to decode value (err %d)", len); + return len; + } + + BT_HEXDUMP_DBG(stored_hash, sizeof(stored_hash), "Stored Hash: "); + + return 0; +} + +static int db_hash_commit(void) +{ + /* Stop work and generate the hash */ + if (k_delayed_work_remaining_get(&db_hash_work)) { + k_delayed_work_cancel(&db_hash_work); + db_hash_gen(false); + } + + /* Check if hash matches then skip SC update */ + if (!memcmp(stored_hash, db_hash, sizeof(stored_hash))) { + BT_DBG("Database Hash matches"); + k_delayed_work_cancel(&gatt_sc.work); + return 0; + } + + BT_HEXDUMP_DBG(db_hash, sizeof(db_hash), "New Hash: "); + + /** + * GATT database has been modified since last boot, likely due to + * a firmware update or a dynamic service that was not re-registered on + * boot. Indicate Service Changed to all bonded devices for the full + * database range to invalidate client-side cache and force discovery on + * reconnect. + */ + sc_indicate(0x0001, 0xffff); + + /* Hash did not match overwrite with current hash */ + db_hash_store(); + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt_hash, "bt/hash", NULL, db_hash_set, + db_hash_commit, NULL); +#endif /*CONFIG_BT_GATT_CACHING */ +#endif /* CONFIG_BT_SETTINGS */ diff --git a/components/ble/ble_stack/host/gatt_internal.h b/components/ble/ble_stack/host/gatt_internal.h new file mode 100644 index 00000000..4ddd4275 --- /dev/null +++ b/components/ble/ble_stack/host/gatt_internal.h @@ -0,0 +1,60 @@ +/** @file + * @brief Internal API for Generic Attribute Profile handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_GATT_CENTRAL_ADDR_RES_NOT_SUPP 0 +#define BT_GATT_CENTRAL_ADDR_RES_SUPP 1 + +#include + +#define BT_GATT_PERM_READ_MASK (BT_GATT_PERM_READ | \ + BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_READ_AUTHEN) +#define BT_GATT_PERM_WRITE_MASK (BT_GATT_PERM_WRITE | \ + BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_WRITE_AUTHEN) +#define BT_GATT_PERM_ENCRYPT_MASK (BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_WRITE_ENCRYPT) +#define BT_GATT_PERM_AUTHEN_MASK (BT_GATT_PERM_READ_AUTHEN | \ + BT_GATT_PERM_WRITE_AUTHEN) + +void bt_gatt_init(void); +#if defined(BFLB_BLE) +void bt_gatt_deinit(void); +#endif +void bt_gatt_connected(struct bt_conn *conn); +void bt_gatt_encrypt_change(struct bt_conn *conn); +void bt_gatt_disconnected(struct bt_conn *conn); + +bool bt_gatt_change_aware(struct bt_conn *conn, bool req); + +int bt_gatt_store_ccc(u8_t id, const bt_addr_le_t *addr); + +int bt_gatt_clear(u8_t id, const bt_addr_le_t *addr); + +#if defined(BFLB_BLE_MTU_CHANGE_CB) +void bt_gatt_mtu_changed(struct bt_conn *conn, u16_t mtu); +#endif + + +#if defined(CONFIG_BT_GATT_CLIENT) +void bt_gatt_notification(struct bt_conn *conn, u16_t handle, + const void *data, u16_t length); +#else +static inline void bt_gatt_notification(struct bt_conn *conn, u16_t handle, + const void *data, u16_t length) +{ +} +#endif /* CONFIG_BT_GATT_CLIENT */ + +struct bt_gatt_attr; + +/* Check attribute permission */ +u8_t bt_gatt_check_perm(struct bt_conn *conn, const struct bt_gatt_attr *attr, + u8_t mask); diff --git a/components/ble/ble_stack/host/hci_core.c b/components/ble/ble_stack/host/hci_core.c new file mode 100644 index 00000000..87d1004c --- /dev/null +++ b/components/ble/ble_stack/host/hci_core.c @@ -0,0 +1,7814 @@ +/* hci_core.c - HCI core Bluetooth handling */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#include "log.h" + +#include "rpa.h" +#include "keys.h" +#include "monitor.h" +#include "hci_core.h" +#include "hci_ecc.h" +#include "ecc.h" + +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "gatt_internal.h" +#include "smp.h" +#include "crypto.h" +#include "../include/bluetooth/crypto.h" +#include "settings.h" +#if defined(BFLB_BLE) +#include "bl_hci_wrapper.h" +#if defined(BL602) +#include "bl602_hbn.h" +#elif defined(BL702) +#include "bl702_hbn.h" +#elif defined(BL606p) +#include "bl606p_hbn.h" +#endif +#include "work_q.h" +#endif +#if defined(CONFIG_BLE_MULTI_ADV) +#include "multi_adv.h" +#endif /* CONFIG_BLE_MULTI_ADV */ + +/* Peripheral timeout to initialize Connection Parameter Update procedure */ +#define CONN_UPDATE_TIMEOUT K_SECONDS(CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT) +#define RPA_TIMEOUT K_SECONDS(CONFIG_BT_RPA_TIMEOUT) + +#define HCI_CMD_TIMEOUT K_SECONDS(10) + +/* Stacks for the threads */ +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) +static struct k_thread rx_thread_data; +static K_THREAD_STACK_DEFINE(rx_thread_stack, CONFIG_BT_RX_STACK_SIZE); +#endif +#if (!BFLB_BLE_CO_THREAD) +static struct k_thread tx_thread_data; +#endif +#if !defined(BFLB_BLE) +static K_THREAD_STACK_DEFINE(tx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); +#endif + +static void init_work(struct k_work *work); + +struct bt_dev bt_dev = { + .init = _K_WORK_INITIALIZER(init_work), + /* Give cmd_sem allowing to send first HCI_Reset cmd, the only + * exception is if the controller requests to wait for an + * initial Command Complete for NOP. + */ +#if defined(BFLB_BLE) +#if !defined(CONFIG_BT_WAIT_NOP) + .ncmd_sem = _K_SEM_INITIALIZER(bt_dev.ncmd_sem, 1, 1), +#else + .ncmd_sem = _K_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1), +#endif + .cmd_tx_queue = _K_FIFO_INITIALIZER(bt_dev.cmd_tx_queue), +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + .rx_queue = Z_FIFO_INITIALIZER(bt_dev.rx_queue), +#endif +#else //BFLB_BLE +#if !defined(CONFIG_BT_WAIT_NOP) + .ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 1, 1), +#else + .ncmd_sem = Z_SEM_INITIALIZER(bt_dev.ncmd_sem, 0, 1), +#endif + .cmd_tx_queue = Z_FIFO_INITIALIZER(bt_dev.cmd_tx_queue), +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + .rx_queue = Z_FIFO_INITIALIZER(bt_dev.rx_queue), +#endif +#endif +}; + +static bt_ready_cb_t ready_cb; + +static bt_le_scan_cb_t *scan_dev_found_cb; + +u8_t adv_ch_map = 0x7; + +#if defined(CONFIG_BT_HCI_VS_EVT_USER) +static bt_hci_vnd_evt_cb_t *hci_vnd_evt_cb; +#endif /* CONFIG_BT_HCI_VS_EVT_USER */ + +#if defined(CONFIG_BT_ECC) +static u8_t pub_key[64]; +static struct bt_pub_key_cb *pub_key_cb; +static bt_dh_key_cb_t dh_key_cb; +#endif /* CONFIG_BT_ECC */ + +#if defined(CONFIG_BT_BREDR) +static bt_br_discovery_cb_t *discovery_cb; +struct bt_br_discovery_result *discovery_results; +static size_t discovery_results_size; +static size_t discovery_results_count; +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_STACK_PTS) +bt_addr_le_t pts_addr; +volatile u8_t event_flag = 0; +#endif + +#if defined(BFLB_HOST_ASSISTANT) +struct blhast_cb *host_assist_cb; +#endif + +struct cmd_state_set { + atomic_t *target; + int bit; + bool val; +}; + +#if defined(BFLB_RELEASE_CMD_SEM_IF_CONN_DISC) +void hci_release_conn_related_cmd(void); +#endif + +void cmd_state_set_init(struct cmd_state_set *state, atomic_t *target, int bit, + bool val) +{ + state->target = target; + state->bit = bit; + state->val = val; +} + +struct cmd_data { + /** HCI status of the command completion */ + u8_t status; + + /** The command OpCode that the buffer contains */ + u16_t opcode; + + /** The state to update when command completes with success. */ + struct cmd_state_set *state; + + /** Used by bt_hci_cmd_send_sync. */ + struct k_sem *sync; +}; + +struct acl_data { + /** BT_BUF_ACL_IN */ + u8_t type; + + /* Index into the bt_conn storage array */ + u8_t id; + + /** ACL connection handle */ + u16_t handle; +}; + +#if defined(BFLB_BLE) +extern struct k_sem g_poll_sem; +#endif + +static struct cmd_data cmd_data[CONFIG_BT_HCI_CMD_COUNT]; + +#define cmd(buf) (&cmd_data[net_buf_id(buf)]) +#define acl(buf) ((struct acl_data *)net_buf_user_data(buf)) + +/* HCI command buffers. Derive the needed size from BT_BUF_RX_SIZE since + * the same buffer is also used for the response. + */ +#define CMD_BUF_SIZE BT_BUF_RX_SIZE +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_FIXED_DEFINE(hci_cmd_pool, CONFIG_BT_HCI_CMD_COUNT, + CMD_BUF_SIZE, NULL); + +NET_BUF_POOL_FIXED_DEFINE(hci_rx_pool, CONFIG_BT_RX_BUF_COUNT, + BT_BUF_RX_SIZE, NULL); +#if defined(CONFIG_BT_CONN) +/* Dedicated pool for HCI_Number_of_Completed_Packets. This event is always + * consumed synchronously by bt_recv_prio() so a single buffer is enough. + * Having a dedicated pool for it ensures that exhaustion of the RX pool + * cannot block the delivery of this priority event. + */ +NET_BUF_POOL_FIXED_DEFINE(num_complete_pool, 1, BT_BUF_RX_SIZE, NULL); +#endif /* CONFIG_BT_CONN */ + +#if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) +NET_BUF_POOL_FIXED_DEFINE(discardable_pool, CONFIG_BT_DISCARDABLE_BUF_COUNT, + BT_BUF_RX_SIZE, NULL); +#endif /* CONFIG_BT_DISCARDABLE_BUF_COUNT */ +#else +struct net_buf_pool hci_cmd_pool; +struct net_buf_pool hci_rx_pool; +#if defined(CONFIG_BT_CONN) +struct net_buf_pool num_complete_pool; +#endif +#if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) +struct net_buf_pool discardable_pool; +#endif +#endif /*!defined(BFLB_DYNAMIC_ALLOC_MEM)*/ + +extern bool hfp_codec_msbc; + +struct event_handler { + u8_t event; + u8_t min_len; + void (*handler)(struct net_buf *buf); +}; + +#define EVENT_HANDLER(_evt, _handler, _min_len) \ +{ \ + .event = _evt, \ + .handler = _handler, \ + .min_len = _min_len, \ +} + +static inline void handle_event(u8_t event, struct net_buf *buf, + const struct event_handler *handlers, + size_t num_handlers) +{ + size_t i; + + for (i = 0; i < num_handlers; i++) { + const struct event_handler *handler = &handlers[i]; + + if (handler->event != event) { + continue; + } + + if (buf->len < handler->min_len) { + BT_ERR("Too small (%u bytes) event 0x%02x", + buf->len, event); + return; + } + + handler->handler(buf); + return; + } + + BT_WARN("Unhandled event 0x%02x len %u: %s", event, + buf->len, bt_hex(buf->data, buf->len)); +} + +static inline bool is_wl_empty(void) +{ +#if defined(CONFIG_BT_WHITELIST) + return !bt_dev.le.wl_entries; +#else + return true; +#endif /* defined(CONFIG_BT_WHITELIST) */ +} + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +static void report_completed_packet(struct net_buf *buf) +{ + + struct bt_hci_cp_host_num_completed_packets *cp; + u16_t handle = acl(buf)->handle; + struct bt_hci_handle_count *hc; + struct bt_conn *conn; + + net_buf_destroy(buf); + + /* Do nothing if controller to host flow control is not supported */ + if (!BT_CMD_TEST(bt_dev.supported_commands, 10, 5)) { + return; + } + + conn = bt_conn_lookup_id(acl(buf)->id); + if (!conn) { + BT_WARN("Unable to look up conn with id 0x%02x", acl(buf)->id); + return; + } + + if (conn->state != BT_CONN_CONNECTED && + conn->state != BT_CONN_DISCONNECT) { + BT_WARN("Not reporting packet for non-connected conn"); + bt_conn_unref(conn); + return; + } + + bt_conn_unref(conn); + + BT_DBG("Reporting completed packet for handle %u", handle); + + buf = bt_hci_cmd_create(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, + sizeof(*cp) + sizeof(*hc)); + if (!buf) { + BT_ERR("Unable to allocate new HCI command"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->num_handles = sys_cpu_to_le16(1); + + hc = net_buf_add(buf, sizeof(*hc)); + hc->handle = sys_cpu_to_le16(handle); + hc->count = sys_cpu_to_le16(1); + + bt_hci_cmd_send(BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS, buf); +} + +#define ACL_IN_SIZE BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_RX_MTU) +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_DEFINE(acl_in_pool, CONFIG_BT_ACL_RX_COUNT, ACL_IN_SIZE, + sizeof(struct acl_data), report_completed_packet); +#else +struct net_buf_pool acl_in_pool; +#endif +#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */ + +struct net_buf *bt_hci_cmd_create(u16_t opcode, u8_t param_len) +{ + struct bt_hci_cmd_hdr *hdr; + struct net_buf *buf; + + BT_DBG("opcode 0x%04x param_len %u", opcode, param_len); + + buf = net_buf_alloc(&hci_cmd_pool, K_FOREVER); + __ASSERT_NO_MSG(buf); + + BT_DBG("buf %p", buf); + + net_buf_reserve(buf, BT_BUF_RESERVE); + + bt_buf_set_type(buf, BT_BUF_CMD); + + cmd(buf)->opcode = opcode; + cmd(buf)->sync = NULL; + cmd(buf)->state = NULL; + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->opcode = sys_cpu_to_le16(opcode); + hdr->param_len = param_len; + + return buf; +} + +int bt_hci_cmd_send(u16_t opcode, struct net_buf *buf) +{ + if (!buf) { + buf = bt_hci_cmd_create(opcode, 0); + if (!buf) { + return -ENOBUFS; + } + } + + BT_DBG("opcode 0x%04x len %u", opcode, buf->len); + + /* Host Number of Completed Packets can ignore the ncmd value + * and does not generate any cmd complete/status events. + */ + if (opcode == BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS) { + int err; + + err = bt_send(buf); + if (err) { + BT_ERR("Unable to send to driver (err %d)", err); + net_buf_unref(buf); + } + + return err; + } + + net_buf_put(&bt_dev.cmd_tx_queue, buf); +#if defined(BFLB_BLE) + k_sem_give(&g_poll_sem); +#endif + return 0; +} + +#if defined(BFLB_HOST_ASSISTANT) +extern void blhast_bt_reset(void); +uint16_t hci_cmd_to_cnt = 0; +#endif +int bt_hci_cmd_send_sync(u16_t opcode, struct net_buf *buf, + struct net_buf **rsp) +{ + struct k_sem sync_sem; + int err; + + if (!buf) { + buf = bt_hci_cmd_create(opcode, 0); + if (!buf) { + return -ENOBUFS; + } + } + + BT_DBG("buf %p opcode 0x%04x len %u", buf, opcode, buf->len); + + k_sem_init(&sync_sem, 0, 1); + cmd(buf)->sync = &sync_sem; + + #if defined(BFLB_BLE) + /*Assign a initial value to status in order to check if hci cmd timeout*/ + cmd(buf)->status = 0xff; + #endif + + /* Make sure the buffer stays around until the command completes */ + net_buf_ref(buf); + + net_buf_put(&bt_dev.cmd_tx_queue, buf); +#if defined(BFLB_BLE) + k_sem_give(&g_poll_sem); +#endif + err = k_sem_take(&sync_sem, HCI_CMD_TIMEOUT); +#ifdef BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS + k_sem_delete(&sync_sem); +#endif + __ASSERT(err == 0, "k_sem_take failed with err %d", err); + + BT_DBG("opcode 0x%04x status 0x%02x", opcode, cmd(buf)->status); + + if (cmd(buf)->status) { + switch (cmd(buf)->status) { + case BT_HCI_ERR_CONN_LIMIT_EXCEEDED: + err = -ECONNREFUSED; + break; + #if defined(BFLB_BLE) + case 0xff: + err = -ETIME; + BT_ERR("k_sem_take timeout with opcode 0x%04x", opcode); + #if (defined(BL602)|| defined(BL702)) && defined(BFLB_HOST_ASSISTANT) + BT_ERR("Restart and restore bt"); + hci_cmd_to_cnt++; + if (cmd(buf)->state) { + struct cmd_state_set *update = cmd(buf)->state; + atomic_set_bit_to(update->target, update->bit, update->val); + } + blhast_bt_reset(); + #else + BT_ASSERT(err == 0); + #endif + break; + #endif + default: + err = -EIO; + break; + } + + net_buf_unref(buf); + } else { + err = 0; + if (rsp) { + *rsp = buf; + } else { + net_buf_unref(buf); + } + } + + return err; +} + +#if defined(CONFIG_BT_OBSERVER) || defined(CONFIG_BT_CONN) +const bt_addr_le_t *bt_lookup_id_addr(u8_t id, const bt_addr_le_t *addr) +{ + if (IS_ENABLED(CONFIG_BT_SMP)) { + struct bt_keys *keys; + + keys = bt_keys_find_irk(id, addr); + if (keys) { + BT_DBG("Identity %s matched RPA %s", + bt_addr_le_str(&keys->addr), + bt_addr_le_str(addr)); + return &keys->addr; + } + } + + return addr; +} +#endif /* CONFIG_BT_OBSERVER || CONFIG_BT_CONN */ + +static int set_advertise_enable(bool enable) +{ + struct net_buf *buf; + struct cmd_state_set state; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (!buf) { + return -ENOBUFS; + } + + if (enable) { + net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE); + } else { + net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE); + } + + cmd_state_set_init(&state, bt_dev.flags, BT_DEV_ADVERTISING, enable); + cmd(buf)->state = &state; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +static int set_random_address(const bt_addr_t *addr) +{ + struct net_buf *buf; + int err; + + #if defined(CONFIG_BT_STACK_PTS) + BT_PTS("set random address %s", bt_addr_str(addr)); + #else + BT_DBG("%s", bt_addr_str(addr)); + #endif + + /* Do nothing if we already have the right address */ + if (!bt_addr_cmp(addr, &bt_dev.random_addr.a)) { + return 0; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, sizeof(*addr)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, addr, sizeof(*addr)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_RANDOM_ADDRESS, buf, NULL); + if (err) { + return err; + } + + bt_addr_copy(&bt_dev.random_addr.a, addr); + bt_dev.random_addr.type = BT_ADDR_LE_RANDOM; + + return 0; +} + +int bt_addr_from_str(const char *str, bt_addr_t *addr) +{ + int i, j; + u8_t tmp; + + if (strlen(str) != 17U) { + return -EINVAL; + } + + for (i = 5, j = 1; *str != '\0'; str++, j++) { + if (!(j % 3) && (*str != ':')) { + return -EINVAL; + } else if (*str == ':') { + i--; + continue; + } + + addr->val[i] = addr->val[i] << 4; + + if (char2hex(*str, &tmp) < 0) { + return -EINVAL; + } + + addr->val[i] |= tmp; + } + + return 0; +} + +int bt_addr_le_from_str(const char *str, const char *type, bt_addr_le_t *addr) +{ + int err; + + err = bt_addr_from_str(str, &addr->a); + if (err < 0) { + return err; + } + + if (!strcmp(type, "public") || !strcmp(type, "(public)")) { + addr->type = BT_ADDR_LE_PUBLIC; + } else if (!strcmp(type, "random") || !strcmp(type, "(random)")) { + addr->type = BT_ADDR_LE_RANDOM; + } else if (!strcmp(type, "public-id") || !strcmp(type, "(public-id)")) { + addr->type = BT_ADDR_LE_PUBLIC_ID; + } else if (!strcmp(type, "random-id") || !strcmp(type, "(random-id)")) { + addr->type = BT_ADDR_LE_RANDOM_ID; + } else { + return -EINVAL; + } + + return 0; +} + +#if defined(CONFIG_BT_PRIVACY) +/* this function sets new RPA only if current one is no longer valid */ +static int le_set_private_addr(u8_t id) +{ + bt_addr_t rpa; + int err; + + /* check if RPA is valid */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_RPA_VALID)) { + return 0; + } + + err = bt_rpa_create(bt_dev.irk[id], &rpa); + if (!err) { + err = set_random_address(&rpa); + if (!err) { + atomic_set_bit(bt_dev.flags, BT_DEV_RPA_VALID); + } + } + + /* restart timer even if failed to set new RPA */ + k_delayed_work_submit(&bt_dev.rpa_update, RPA_TIMEOUT); + + return err; +} + +static void rpa_timeout(struct k_work *work) +{ + int err_adv = 0, err_scan = 0; + + BT_DBG(""); + + /* Invalidate RPA */ + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + + /* + * we need to update rpa only if advertising is ongoing, with + * BT_DEV_KEEP_ADVERTISING flag is handled in disconnected event + */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + /* make sure new address is used */ + set_advertise_enable(false); + err_adv = le_set_private_addr(bt_dev.adv_id); + set_advertise_enable(true); + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)) { + /* TODO do we need to toggle scan? */ + err_scan = le_set_private_addr(BT_ID_DEFAULT); + } + + /* If both advertising and scanning is active, le_set_private_addr + * will fail. In this case, set back RPA_VALID so that if either of + * advertising or scanning was restarted by application then + * le_set_private_addr in the public API call path will not retry + * set_random_address. This is needed so as to be able to stop and + * restart either of the role by the application after rpa_timeout. + */ + if (err_adv || err_scan) { + atomic_set_bit(bt_dev.flags, BT_DEV_RPA_VALID); + } +} + +#if defined(CONFIG_BT_STACK_PTS) || defined(CONFIG_AUTO_PTS) +static int le_set_non_resolv_private_addr(u8_t id) +{ + bt_addr_t nrpa; + int err; + + err = bt_rand(nrpa.val, sizeof(nrpa.val)); + if (err) { + return err; + } + + nrpa.val[5] &= 0x3f; + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + return set_random_address(&nrpa); +} + +#endif + +#else +static int le_set_private_addr(u8_t id) +{ + bt_addr_t nrpa; + int err; + + err = bt_rand(nrpa.val, sizeof(nrpa.val)); + if (err) { + return err; + } + + nrpa.val[5] &= 0x3f; + + return set_random_address(&nrpa); +} +#endif + +#if defined(CONFIG_BT_OBSERVER) +static int set_le_scan_enable(u8_t enable) +{ + struct bt_hci_cp_le_set_scan_enable *cp; + struct net_buf *buf; + struct cmd_state_set state; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + if (enable == BT_HCI_LE_SCAN_ENABLE) { + cp->filter_dup = atomic_test_bit(bt_dev.flags, + BT_DEV_SCAN_FILTER_DUP); + } else { + cp->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + } + + cp->enable = enable; + + cmd_state_set_init(&state, bt_dev.flags, BT_DEV_SCANNING, + enable == BT_HCI_LE_SCAN_ENABLE); + cmd(buf)->state = &state; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, NULL); + if (err) { + return err; + } + + return 0; +} +#endif /* CONFIG_BT_OBSERVER */ + +#if defined(CONFIG_BT_CONN) +static void hci_acl(struct net_buf *buf) +{ + struct bt_hci_acl_hdr *hdr; + u16_t handle, len; + struct bt_conn *conn; + u8_t flags; + + BT_DBG("buf %p", buf); + + BT_ASSERT(buf->len >= sizeof(*hdr)); + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + len = sys_le16_to_cpu(hdr->len); + handle = sys_le16_to_cpu(hdr->handle); + flags = bt_acl_flags(handle); + + acl(buf)->handle = bt_acl_handle(handle); + acl(buf)->id = BT_CONN_ID_INVALID; + + BT_DBG("handle %u len %u flags %u", acl(buf)->handle, len, flags); + + if (buf->len != len) { + BT_ERR("ACL data length mismatch (%u != %u)", buf->len, len); + net_buf_unref(buf); + return; + } + + conn = bt_conn_lookup_handle(acl(buf)->handle); + if (!conn) { + BT_ERR("Unable to find conn for handle %u", acl(buf)->handle); + net_buf_unref(buf); + return; + } + + acl(buf)->id = bt_conn_index(conn); + + bt_conn_recv(conn, buf, flags); + bt_conn_unref(conn); +} + +static void hci_data_buf_overflow(struct net_buf *buf) +{ + struct bt_hci_evt_data_buf_overflow *evt = (void *)buf->data; + + BT_WARN("Data buffer overflow (link type 0x%02x)", evt->link_type); + // avoid compiler warning if BT_WARN is empty + (void) evt; +} + +static void hci_num_completed_packets(struct net_buf *buf) +{ + struct bt_hci_evt_num_completed_packets *evt = (void *)buf->data; + int i; + + BT_DBG("num_handles %u", evt->num_handles); + + for (i = 0; i < evt->num_handles; i++) { + u16_t handle, count; + struct bt_conn *conn; + unsigned int key; + + handle = sys_le16_to_cpu(evt->h[i].handle); + count = sys_le16_to_cpu(evt->h[i].count); + + BT_DBG("handle %u count %u", handle, count); + + key = irq_lock(); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + irq_unlock(key); + BT_ERR("No connection for handle %u", handle); + continue; + } + + irq_unlock(key); + + while (count--) { + struct bt_conn_tx *tx; + sys_snode_t *node; + + key = irq_lock(); + + if (conn->pending_no_cb) { + conn->pending_no_cb--; + irq_unlock(key); + k_sem_give(bt_conn_get_pkts(conn)); + continue; + } + + node = sys_slist_get(&conn->tx_pending); + irq_unlock(key); + + if (!node) { + BT_ERR("packets count mismatch"); + break; + } + + tx = CONTAINER_OF(node, struct bt_conn_tx, node); + + key = irq_lock(); + conn->pending_no_cb = tx->pending_no_cb; + tx->pending_no_cb = 0U; + sys_slist_append(&conn->tx_complete, &tx->node); + irq_unlock(key); + + k_work_submit(&conn->tx_complete_work); + k_sem_give(bt_conn_get_pkts(conn)); +#if defined(BFLB_BLE) + k_sem_give(&g_poll_sem); +#endif + } + + bt_conn_unref(conn); + } +} + +#if defined(CONFIG_BT_CENTRAL) +#if defined(CONFIG_BT_WHITELIST) +int bt_le_auto_conn(const struct bt_le_conn_param *conn_param) +{ + struct net_buf *buf; + struct cmd_state_set state; + struct bt_hci_cp_le_create_conn *cp; + u8_t own_addr_type; + int err; + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + return err; + } + } + + #if defined(CONFIG_BT_STACK_PTS) + if(conn_param->own_address_type != BT_ADDR_LE_PUBLIC){ + #endif + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + err = le_set_private_addr(BT_ID_DEFAULT); + if (err) { + return err; + } + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + } else { + own_addr_type = BT_ADDR_LE_RANDOM; + } + } else { + const bt_addr_le_t *addr = &bt_dev.id_addr[BT_ID_DEFAULT]; + + /* If Static Random address is used as Identity address we + * need to restore it before creating connection. Otherwise + * NRPA used for active scan could be used for connection. + */ + if (addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&addr->a); + if (err) { + return err; + } + } + + own_addr_type = addr->type; + } + + #if defined(CONFIG_BT_STACK_PTS) + }else{ + own_addr_type = conn_param->own_address_type; + } + #endif + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->filter_policy = BT_HCI_LE_CREATE_CONN_FP_WHITELIST; + cp->own_addr_type = own_addr_type; + + /* User Initiated procedure use fast scan parameters. */ + cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL); + cp->scan_window = sys_cpu_to_le16(BT_GAP_SCAN_FAST_WINDOW); + + cp->conn_interval_min = sys_cpu_to_le16(conn_param->interval_min); + cp->conn_interval_max = sys_cpu_to_le16(conn_param->interval_max); + cp->conn_latency = sys_cpu_to_le16(conn_param->latency); + cp->supervision_timeout = sys_cpu_to_le16(conn_param->timeout); + + cmd_state_set_init(&state, bt_dev.flags, BT_DEV_AUTO_CONN, true); + cmd(buf)->state = &state; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL); +} + +int bt_le_auto_conn_cancel(void) +{ + struct net_buf *buf; + struct cmd_state_set state; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN_CANCEL, 0); + + cmd_state_set_init(&state, bt_dev.flags, BT_DEV_AUTO_CONN, false); + cmd(buf)->state = &state; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN_CANCEL, buf, NULL); +} +#endif /* defined(CONFIG_BT_WHITELIST) */ + +static int hci_le_create_conn(const struct bt_conn *conn) +{ + struct net_buf *buf; + struct bt_hci_cp_le_create_conn *cp; + u8_t own_addr_type; + const bt_addr_le_t *peer_addr; + int err; + +#if defined(CONFIG_BT_STACK_PTS) + if(conn->le.own_adder_type == BT_ADDR_LE_PUBLIC || conn->le.own_adder_type == BT_ADDR_LE_PUBLIC_ID) + { + own_addr_type = conn->le.own_adder_type; + goto start_connect; + } + +#endif + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + err = le_set_private_addr(conn->id); + if (err) { + return err; + } + #if defined(BFLB_BLE) + /*Use random type at the first time*/ + own_addr_type = BT_ADDR_LE_RANDOM; + #if defined(CONFIG_BT_STACK_PTS) + if(conn->le.own_adder_type == BT_ADDR_LE_RANDOM_ID){ + own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + } + #endif + #else + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + } else { + own_addr_type = BT_ADDR_LE_RANDOM; + } + #endif + } else { + /* If Static Random address is used as Identity address we + * need to restore it before creating connection. Otherwise + * NRPA used for active scan could be used for connection. + */ + const bt_addr_le_t *own_addr = &bt_dev.id_addr[conn->id]; + + if (own_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&own_addr->a); + if (err) { + return err; + } + } + + own_addr_type = own_addr->type; + } + +#if defined(CONFIG_BT_STACK_PTS) +start_connect: +#endif + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + /* Interval == window for continuous scanning */ + cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL); + cp->scan_window = cp->scan_interval; + + peer_addr = &conn->le.dst; + +#if defined(CONFIG_BT_SMP) + if (!bt_dev.le.rl_size || bt_dev.le.rl_entries > bt_dev.le.rl_size) { + /* Host resolving is used, use the RPA directly. */ + peer_addr = &conn->le.resp_addr; + } +#endif + + bt_addr_le_copy(&cp->peer_addr, peer_addr); + cp->own_addr_type = own_addr_type; + cp->conn_interval_min = sys_cpu_to_le16(conn->le.interval_min); + cp->conn_interval_max = sys_cpu_to_le16(conn->le.interval_max); + cp->conn_latency = sys_cpu_to_le16(conn->le.latency); + cp->supervision_timeout = sys_cpu_to_le16(conn->le.timeout); + +#if defined(CONFIG_BT_STACK_PTS) + if(event_flag == dir_connect_req) + { + bt_addr_le_copy(&cp->peer_addr,&pts_addr); + + cp->filter_policy = 0; + cp->own_addr_type = BT_ADDR_LE_PUBLIC; + + /* User Initiated procedure use fast scan parameters. */ + cp->scan_interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL); + cp->scan_window = sys_cpu_to_le16(BT_GAP_SCAN_FAST_WINDOW); + } +#endif + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL); +} +#endif /* CONFIG_BT_CENTRAL */ + +static void hci_disconn_complete(struct net_buf *buf) +{ + struct bt_hci_evt_disconn_complete *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + BT_DBG("status 0x%02x handle %u reason 0x%02x", evt->status, handle, + evt->reason); + + if (evt->status) { + return; + } + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to look up conn with handle %u", handle); + goto advertise; + } + + conn->err = evt->reason; + + /* Check stacks usage */ +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + STACK_ANALYZE("rx stack", rx_thread_stack); +#endif +#if !defined(BFLB_BLE) + STACK_ANALYZE("tx stack", tx_thread_stack); +#endif + + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + conn->handle = 0U; + + if (conn->type != BT_CONN_TYPE_LE) { +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_SCO) { + bt_sco_cleanup(conn); + return; + } + /* + * If only for one connection session bond was set, clear keys + * database row for this connection. + */ + if (conn->type == BT_CONN_TYPE_BR && + atomic_test_and_clear_bit(conn->flags, BT_CONN_BR_NOBOND)) { + bt_keys_link_key_clear(conn->br.link_key); + } +#endif + bt_conn_unref(conn); + return; + } + +#if defined(CONFIG_BT_CENTRAL) && !defined(CONFIG_BT_WHITELIST) + if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) { + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + bt_le_scan_update(false); + } +#endif /* defined(CONFIG_BT_CENTRAL) && !defined(CONFIG_BT_WHITELIST) */ + + bt_conn_unref(conn); + +#if defined(BFLB_BLE_PATCH_CLEAN_UP_CONNECT_REF) + atomic_clear(&conn->ref); +#endif + +#if defined(BFLB_RELEASE_CMD_SEM_IF_CONN_DISC) + hci_release_conn_related_cmd(); +#endif + +#if defined(CONFIG_BLE_RECONNECT_TEST) +if (conn->role == BT_CONN_ROLE_MASTER) { + struct bt_le_conn_param param = { + .interval_min = BT_GAP_INIT_CONN_INT_MIN, + .interval_max = BT_GAP_INIT_CONN_INT_MAX, + .latency = 0, + .timeout = 400, + }; + + if(bt_conn_create_le(&conn->le.dst, ¶m)) { + printf("Reconnecting. \n"); + } else { + printf("Reconnect fail. \n"); + } +} +#endif + +advertise: + if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING) && + !atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + le_set_private_addr(bt_dev.adv_id); + } + + set_advertise_enable(true); + } +} + +static int hci_le_read_remote_features(struct bt_conn *conn) +{ + struct bt_hci_cp_le_read_remote_features *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_REMOTE_FEATURES, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + bt_hci_cmd_send(BT_HCI_OP_LE_READ_REMOTE_FEATURES, buf); + + return 0; +} + +/* LE Data Length Change Event is optional so this function just ignore + * error and stack will continue to use default values. + */ +static void hci_le_set_data_len(struct bt_conn *conn) +{ + struct bt_hci_rp_le_read_max_data_len *rp; + struct bt_hci_cp_le_set_data_len *cp; + struct net_buf *buf, *rsp; + u16_t tx_octets, tx_time; + int err; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MAX_DATA_LEN, NULL, &rsp); + if (err) { + BT_ERR("Failed to read DLE max data len"); + return; + } + + rp = (void *)rsp->data; + tx_octets = sys_le16_to_cpu(rp->max_tx_octets); + tx_time = sys_le16_to_cpu(rp->max_tx_time); + net_buf_unref(rsp); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_DATA_LEN, sizeof(*cp)); + if (!buf) { + BT_ERR("Failed to create LE Set Data Length Command"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->tx_octets = sys_cpu_to_le16(tx_octets); + cp->tx_time = sys_cpu_to_le16(tx_time); + err = bt_hci_cmd_send(BT_HCI_OP_LE_SET_DATA_LEN, buf); + if (err) { + BT_ERR("Failed to send LE Set Data Length Command"); + } +} + + +int bt_le_set_data_len(struct bt_conn *conn, u16_t tx_octets, u16_t tx_time) +{ + struct bt_hci_cp_le_set_data_len *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_DATA_LEN, sizeof(*cp)); + if (!buf) { + BT_ERR("bt_le_set_data_len, Failed to create LE Set Data Length Command"); + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->tx_octets = sys_cpu_to_le16(tx_octets); + cp->tx_time = sys_cpu_to_le16(tx_time); + + return bt_hci_cmd_send(BT_HCI_OP_LE_SET_DATA_LEN, buf); +} + + +int hci_le_set_phy(struct bt_conn *conn) +{ + struct bt_hci_cp_le_set_phy *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PHY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->all_phys = 0U; + cp->tx_phys = BT_HCI_LE_PHY_PREFER_2M; + cp->rx_phys = BT_HCI_LE_PHY_PREFER_2M; + cp->phy_opts = BT_HCI_LE_PHY_CODED_ANY; + bt_hci_cmd_send(BT_HCI_OP_LE_SET_PHY, buf); + + return 0; +} + +int hci_le_set_default_phy(struct bt_conn *conn,u8_t default_phy) +{ + struct bt_hci_cp_le_set_default_phy *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_DEFAULT_PHY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->all_phys = 0U; + cp->tx_phys = default_phy; + cp->rx_phys = default_phy; + bt_hci_cmd_send(BT_HCI_OP_LE_SET_DEFAULT_PHY, buf); + + return 0; +} + + +static void slave_update_conn_param(struct bt_conn *conn) +{ + if (!IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + return; + } + + /* don't start timer again on PHY update etc */ + if (atomic_test_bit(conn->flags, BT_CONN_SLAVE_PARAM_UPDATE)) { + return; + } + + /* + * Core 4.2 Vol 3, Part C, 9.3.12.2 + * The Peripheral device should not perform a Connection Parameter + * Update procedure within 5 s after establishing a connection. + */ + k_delayed_work_submit(&conn->update_work, CONN_UPDATE_TIMEOUT); +} + +#if defined(CONFIG_BT_SMP) +static void update_pending_id(struct bt_keys *keys, void *data) +{ + if (keys->flags & BT_KEYS_ID_PENDING_ADD) { + keys->flags &= ~BT_KEYS_ID_PENDING_ADD; + bt_id_add(keys); + return; + } + + if (keys->flags & BT_KEYS_ID_PENDING_DEL) { + keys->flags &= ~BT_KEYS_ID_PENDING_DEL; + bt_id_del(keys); + return; + } +} +#endif + +static struct bt_conn *find_pending_connect(bt_addr_le_t *peer_addr) +{ + struct bt_conn *conn; + + /* + * Make lookup to check if there's a connection object in + * CONNECT or DIR_ADV state associated with passed peer LE address. + */ + conn = bt_conn_lookup_state_le(peer_addr, BT_CONN_CONNECT); + if (conn) { + return conn; + } + + return bt_conn_lookup_state_le(peer_addr, BT_CONN_CONNECT_DIR_ADV); +} + +static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) +{ + u16_t handle = sys_le16_to_cpu(evt->handle); + bt_addr_le_t peer_addr, id_addr; + struct bt_conn *conn; + int err; + + BT_DBG("status 0x%02x handle %u role %u %s", evt->status, handle, + evt->role, bt_addr_le_str(&evt->peer_addr)); + +#if defined(CONFIG_BT_SMP) + if (atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_ID_PENDING)) { + bt_keys_foreach(BT_KEYS_IRK, update_pending_id, NULL); + } +#endif + + if (evt->status) { + /* + * If there was an error we are only interested in pending + * connection. There is no need to check ID address as + * only one connection can be in that state. + * + * Depending on error code address might not be valid anyway. + */ + conn = find_pending_connect(NULL); + if (!conn) { + return; + } + + conn->err = evt->status; + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + /* + * Handle advertising timeout after high duty directed + * advertising. + */ + if (conn->err == BT_HCI_ERR_ADV_TIMEOUT) { + atomic_clear_bit(bt_dev.flags, + BT_DEV_ADVERTISING); + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + + goto done; + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + /* + * Handle cancellation of outgoing connection attempt. + */ + if (conn->err == BT_HCI_ERR_UNKNOWN_CONN_ID) { + /* We notify before checking autoconnect flag + * as application may choose to change it from + * callback. + */ + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + +#if !defined(CONFIG_BT_WHITELIST) + /* Check if device is marked for autoconnect. */ + if (atomic_test_bit(conn->flags, + BT_CONN_AUTO_CONNECT)) { + bt_conn_set_state(conn, + BT_CONN_CONNECT_SCAN); + } +#endif /* !defined(CONFIG_BT_WHITELIST) */ + goto done; + } + } + + BT_WARN("Unexpected status 0x%02x", evt->status); + + bt_conn_unref(conn); + + return; + } + + bt_addr_le_copy(&id_addr, &evt->peer_addr); + + /* Translate "enhanced" identity address type to normal one */ + if (id_addr.type == BT_ADDR_LE_PUBLIC_ID || + id_addr.type == BT_ADDR_LE_RANDOM_ID) { + id_addr.type -= BT_ADDR_LE_PUBLIC_ID; + bt_addr_copy(&peer_addr.a, &evt->peer_rpa); + peer_addr.type = BT_ADDR_LE_RANDOM; + } else { + bt_addr_le_copy(&peer_addr, &evt->peer_addr); + } + + conn = find_pending_connect(&id_addr); + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + evt->role == BT_HCI_ROLE_SLAVE) { + /* + * clear advertising even if we are not able to add connection + * object to keep host in sync with controller state + */ + atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING); + + /* for slave we may need to add new connection */ + if (!conn) { + conn = bt_conn_add_le(bt_dev.adv_id, &id_addr); + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + IS_ENABLED(CONFIG_BT_WHITELIST) && + evt->role == BT_HCI_ROLE_MASTER) { + /* for whitelist initiator me may need to add new connection. */ + if (!conn) { + conn = bt_conn_add_le(BT_ID_DEFAULT, &id_addr); + } + } + + if (!conn) { + BT_ERR("Unable to add new conn for handle %u", handle); + return; + } + + conn->handle = handle; + bt_addr_le_copy(&conn->le.dst, &id_addr); + conn->le.interval = sys_le16_to_cpu(evt->interval); + conn->le.latency = sys_le16_to_cpu(evt->latency); + conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout); + conn->role = evt->role; + conn->err = 0U; + + /* + * Use connection address (instead of identity address) as initiator + * or responder address. Only slave needs to be updated. For master all + * was set during outgoing connection creation. + */ + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + conn->role == BT_HCI_ROLE_SLAVE) { + bt_addr_le_copy(&conn->le.init_addr, &peer_addr); + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + #if defined(BFLB_BLE_PATCH_DHKEY_CHECK_FAILED) + if(memcmp(&evt->local_rpa, BT_ADDR_ANY, sizeof(bt_addr_t))) + bt_addr_copy(&conn->le.resp_addr.a, &evt->local_rpa); + else + bt_addr_copy(&conn->le.resp_addr.a, &bt_dev.random_addr.a); + #else + bt_addr_copy(&conn->le.resp_addr.a, &evt->local_rpa); + #endif + conn->le.resp_addr.type = BT_ADDR_LE_RANDOM; + } else { + bt_addr_le_copy(&conn->le.resp_addr, + &bt_dev.id_addr[conn->id]); + } + + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_and_clear_bit(bt_dev.flags,BT_DEV_ADV_ADDRESS_IS_PUBLIC)) + { + bt_addr_le_copy(&conn->le.resp_addr,&bt_dev.id_addr[conn->id]); + } + #endif + + /* if the controller supports, lets advertise for another + * slave connection. + * check for connectable advertising state is sufficient as + * this is how this le connection complete for slave occurred. + */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING) && + BT_LE_STATES_SLAVE_CONN_ADV(bt_dev.le.states)) { + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + le_set_private_addr(bt_dev.adv_id); + } + + set_advertise_enable(true); + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER) { + + if (IS_ENABLED(CONFIG_BT_WHITELIST) && + atomic_test_bit(bt_dev.flags, BT_DEV_AUTO_CONN)) { + conn->id = BT_ID_DEFAULT; + atomic_clear_bit(bt_dev.flags, BT_DEV_AUTO_CONN); + } + + bt_addr_le_copy(&conn->le.resp_addr, &peer_addr); + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + #if defined(BFLB_BLE_PATCH_DHKEY_CHECK_FAILED) + if(memcmp(&evt->local_rpa, BT_ADDR_ANY, sizeof(bt_addr_t))) + bt_addr_copy(&conn->le.init_addr.a, &evt->local_rpa); + else + bt_addr_copy(&conn->le.init_addr.a, &bt_dev.random_addr.a); + #else + bt_addr_copy(&conn->le.init_addr.a, &evt->local_rpa); + #endif + conn->le.init_addr.type = BT_ADDR_LE_RANDOM; + } else { + bt_addr_le_copy(&conn->le.init_addr, + &bt_dev.id_addr[conn->id]); + } + + #if defined(CONFIG_BT_STACK_PTS) + if(conn->le.own_adder_type == BT_ADDR_LE_PUBLIC_ID) + { + bt_addr_le_copy(&conn->le.init_addr,&bt_dev.id_addr[conn->id]); + } + #endif + } + + + bt_conn_set_state(conn, BT_CONN_CONNECTED); + + /* + * it is possible that connection was disconnected directly from + * connected callback so we must check state before doing connection + * parameters update + */ + if (conn->state != BT_CONN_CONNECTED) { + goto done; + } + + if ((evt->role == BT_HCI_ROLE_MASTER) || + BT_FEAT_LE_SLAVE_FEATURE_XCHG(bt_dev.le.features)) { + err = hci_le_read_remote_features(conn); + if (!err) { + goto done; + } + } + + if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) && + BT_FEAT_LE_PHY_2M(bt_dev.le.features)) { + err = hci_le_set_phy(conn); + if (!err) { + atomic_set_bit(conn->flags, BT_CONN_AUTO_PHY_UPDATE); + goto done; + } + } + + if (IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features)) { + hci_le_set_data_len(conn); + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + conn->role == BT_CONN_ROLE_SLAVE) { + slave_update_conn_param(conn); + } + +done: + bt_conn_unref(conn); + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + bt_le_scan_update(false); + } +} + +static void le_enh_conn_complete(struct net_buf *buf) +{ + enh_conn_complete((void *)buf->data); +} + +static void le_legacy_conn_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_conn_complete *evt = (void *)buf->data; + struct bt_hci_evt_le_enh_conn_complete enh; + const bt_addr_le_t *id_addr; + + BT_DBG("status 0x%02x role %u %s", evt->status, evt->role, + bt_addr_le_str(&evt->peer_addr)); + + enh.status = evt->status; + enh.handle = evt->handle; + enh.role = evt->role; + enh.interval = evt->interval; + enh.latency = evt->latency; + enh.supv_timeout = evt->supv_timeout; + enh.clock_accuracy = evt->clock_accuracy; + + bt_addr_le_copy(&enh.peer_addr, &evt->peer_addr); + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + bt_addr_copy(&enh.local_rpa, &bt_dev.random_addr.a); + } else { + bt_addr_copy(&enh.local_rpa, BT_ADDR_ANY); + } + + if (evt->role == BT_HCI_ROLE_SLAVE) { + id_addr = bt_lookup_id_addr(bt_dev.adv_id, &enh.peer_addr); + } else { + id_addr = bt_lookup_id_addr(BT_ID_DEFAULT, &enh.peer_addr); + } + + if (id_addr != &enh.peer_addr) { + bt_addr_copy(&enh.peer_rpa, &enh.peer_addr.a); + bt_addr_le_copy(&enh.peer_addr, id_addr); + enh.peer_addr.type += BT_ADDR_LE_PUBLIC_ID; + } else { + bt_addr_copy(&enh.peer_rpa, BT_ADDR_ANY); + } + + enh_conn_complete(&enh); +} + +static void le_remote_feat_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_remote_feat_complete *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + return; + } + + if (!evt->status) { + memcpy(conn->le.features, evt->features, + sizeof(conn->le.features)); + } + + if (IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) && + BT_FEAT_LE_PHY_2M(bt_dev.le.features) && + BT_FEAT_LE_PHY_2M(conn->le.features)) { + int err; + + err = hci_le_set_phy(conn); + if (!err) { + atomic_set_bit(conn->flags, BT_CONN_AUTO_PHY_UPDATE); + goto done; + } + } + + if (IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features) && + BT_FEAT_LE_DLE(conn->le.features)) { + hci_le_set_data_len(conn); + } + +#if !defined(CONFIG_BT_STACK_PTS) + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + conn->role == BT_CONN_ROLE_SLAVE) { + slave_update_conn_param(conn); + } +#endif +done: + bt_conn_unref(conn); +} + +#if defined(CONFIG_BT_DATA_LEN_UPDATE) +static void le_data_len_change(struct net_buf *buf) +{ + struct bt_hci_evt_le_data_len_change *evt = (void *)buf->data; + u16_t max_tx_octets = sys_le16_to_cpu(evt->max_tx_octets); + u16_t max_rx_octets = sys_le16_to_cpu(evt->max_rx_octets); + u16_t max_tx_time = sys_le16_to_cpu(evt->max_tx_time); + u16_t max_rx_time = sys_le16_to_cpu(evt->max_rx_time); + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + UNUSED(max_tx_octets); + UNUSED(max_rx_octets); + UNUSED(max_tx_time); + UNUSED(max_rx_time); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + return; + } + + BT_DBG("max. tx: %u (%uus), max. rx: %u (%uus)", max_tx_octets, + max_tx_time, max_rx_octets, max_rx_time); + + /* TODO use those */ + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_DATA_LEN_UPDATE */ + +#if defined(CONFIG_BT_PHY_UPDATE) +static void le_phy_update_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_phy_update_complete *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + return; + } + + BT_DBG("PHY updated: status: 0x%02x, tx: %u, rx: %u", + evt->status, evt->tx_phy, evt->rx_phy); + + if (!IS_ENABLED(CONFIG_BT_AUTO_PHY_UPDATE) || + !atomic_test_and_clear_bit(conn->flags, BT_CONN_AUTO_PHY_UPDATE)) { + goto done; + } + + if (IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features) && + BT_FEAT_LE_DLE(conn->le.features)) { + hci_le_set_data_len(conn); + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + conn->role == BT_CONN_ROLE_SLAVE) { + slave_update_conn_param(conn); + } + +done: + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_PHY_UPDATE */ + +bool bt_le_conn_params_valid(const struct bt_le_conn_param *param) +{ + /* All limits according to BT Core spec 5.0 [Vol 2, Part E, 7.8.12] */ + + if (param->interval_min > param->interval_max || + param->interval_min < 6 || param->interval_max > 3200) { + return false; + } + + if (param->latency > 499) { + return false; + } + + if (param->timeout < 10 || param->timeout > 3200 || + ((param->timeout * 4U) <= + ((1 + param->latency) * param->interval_max))) { + return false; + } + + return true; +} + +static void le_conn_param_neg_reply(u16_t handle, u8_t reason) +{ + struct bt_hci_cp_le_conn_param_req_neg_reply *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, + sizeof(*cp)); + if (!buf) { + BT_ERR("Unable to allocate buffer"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + cp->reason = sys_cpu_to_le16(reason); + + bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, buf); +} + +static int le_conn_param_req_reply(u16_t handle, + const struct bt_le_conn_param *param) +{ + struct bt_hci_cp_le_conn_param_req_reply *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + (void)memset(cp, 0, sizeof(*cp)); + + cp->handle = sys_cpu_to_le16(handle); + cp->interval_min = sys_cpu_to_le16(param->interval_min); + cp->interval_max = sys_cpu_to_le16(param->interval_max); + cp->latency = sys_cpu_to_le16(param->latency); + cp->timeout = sys_cpu_to_le16(param->timeout); + + return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY, buf); +} + +static void le_conn_param_req(struct net_buf *buf) +{ + struct bt_hci_evt_le_conn_param_req *evt = (void *)buf->data; + struct bt_le_conn_param param; + struct bt_conn *conn; + u16_t handle; + + handle = sys_le16_to_cpu(evt->handle); + param.interval_min = sys_le16_to_cpu(evt->interval_min); + param.interval_max = sys_le16_to_cpu(evt->interval_max); + param.latency = sys_le16_to_cpu(evt->latency); + param.timeout = sys_le16_to_cpu(evt->timeout); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + le_conn_param_neg_reply(handle, BT_HCI_ERR_UNKNOWN_CONN_ID); + return; + } + + if (!le_param_req(conn, ¶m)) { + le_conn_param_neg_reply(handle, BT_HCI_ERR_INVALID_LL_PARAM); + } else { + le_conn_param_req_reply(handle, ¶m); + } + + bt_conn_unref(conn); +} + +static void le_conn_update_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_conn_update_complete *evt = (void *)buf->data; + struct bt_conn *conn; + u16_t handle; + + handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("status 0x%02x, handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + return; + } + + if (!evt->status) { + conn->le.interval = sys_le16_to_cpu(evt->interval); + conn->le.latency = sys_le16_to_cpu(evt->latency); + conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout); + notify_le_param_updated(conn); + } else if (evt->status == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE && + conn->role == BT_HCI_ROLE_SLAVE && + !atomic_test_and_set_bit(conn->flags, + BT_CONN_SLAVE_PARAM_L2CAP)) { + /* CPR not supported, let's try L2CAP CPUP instead */ + struct bt_le_conn_param param; + + param.interval_min = conn->le.interval_min; + param.interval_max = conn->le.interval_max; + param.latency = conn->le.pending_latency; + param.timeout = conn->le.pending_timeout; + + bt_l2cap_update_conn_param(conn, ¶m); + } + + bt_conn_unref(conn); +} + +#if defined(CONFIG_BT_CENTRAL) +static void check_pending_conn(const bt_addr_le_t *id_addr, + const bt_addr_le_t *addr, u8_t evtype) +{ + struct bt_conn *conn; + + /* No connections are allowed during explicit scanning */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return; + } + + /* Return if event is not connectable */ + if (evtype != BT_LE_ADV_IND && evtype != BT_LE_ADV_DIRECT_IND) { + return; + } + + conn = bt_conn_lookup_state_le(id_addr, BT_CONN_CONNECT_SCAN); + if (!conn) { + return; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && + set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE)) { + goto failed; + } + + bt_addr_le_copy(&conn->le.resp_addr, addr); + if (hci_le_create_conn(conn)) { + goto failed; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + bt_conn_unref(conn); + return; + +failed: + conn->err = BT_HCI_ERR_UNSPECIFIED; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_conn_unref(conn); + bt_le_scan_update(false); +} +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +static int set_flow_control(void) +{ + struct bt_hci_cp_host_buffer_size *hbs; + struct net_buf *buf; + int err; + + /* Check if host flow control is actually supported */ + if (!BT_CMD_TEST(bt_dev.supported_commands, 10, 5)) { + BT_WARN("Controller to host flow control not supported"); + return 0; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, + sizeof(*hbs)); + if (!buf) { + return -ENOBUFS; + } + + hbs = net_buf_add(buf, sizeof(*hbs)); + (void)memset(hbs, 0, sizeof(*hbs)); + hbs->acl_mtu = sys_cpu_to_le16(CONFIG_BT_L2CAP_RX_MTU + + sizeof(struct bt_l2cap_hdr)); + hbs->acl_pkts = sys_cpu_to_le16(CONFIG_BT_ACL_RX_COUNT); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_HOST_BUFFER_SIZE, buf, NULL); + if (err) { + return err; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_u8(buf, BT_HCI_CTL_TO_HOST_FLOW_ENABLE); + return bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL); +} +#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */ + +static int bt_clear_all_pairings(u8_t id) +{ + bt_conn_disconnect_all(id); + + if (IS_ENABLED(CONFIG_BT_SMP)) { + bt_keys_clear_all(id); + } + + if (IS_ENABLED(CONFIG_BT_BREDR)) { + bt_keys_link_key_clear_addr(NULL); + } + + return 0; +} + +int bt_unpair(u8_t id, const bt_addr_le_t *addr) +{ + struct bt_keys *keys = NULL; + struct bt_conn *conn; + + if (id >= CONFIG_BT_ID_MAX) { + return -EINVAL; + } + + if (!addr || !bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + return bt_clear_all_pairings(id); + } + + conn = bt_conn_lookup_addr_le(id, addr); + if (conn) { + /* Clear the conn->le.keys pointer since we'll invalidate it, + * and don't want any subsequent code (like disconnected + * callbacks) accessing it. + */ + if (conn->type == BT_CONN_TYPE_LE) { + keys = conn->le.keys; + conn->le.keys = NULL; + } + + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + bt_conn_unref(conn); + } + + if (IS_ENABLED(CONFIG_BT_BREDR)) { + /* LE Public may indicate BR/EDR as well */ + if (addr->type == BT_ADDR_LE_PUBLIC) { + bt_keys_link_key_clear_addr(&addr->a); + } + } + + if (IS_ENABLED(CONFIG_BT_SMP)) { + if (!keys) { + keys = bt_keys_find_addr(id, addr); + } + + if (keys) { + bt_keys_clear(keys); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_gatt_clear(id, addr); + } + + return 0; +} + +#endif /* CONFIG_BT_CONN */ + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +static enum bt_security_err security_err_get(u8_t hci_err) +{ + switch (hci_err) { + case BT_HCI_ERR_SUCCESS: + return BT_SECURITY_ERR_SUCCESS; + case BT_HCI_ERR_AUTH_FAIL: + return BT_SECURITY_ERR_AUTH_FAIL; + case BT_HCI_ERR_PIN_OR_KEY_MISSING: + return BT_SECURITY_ERR_PIN_OR_KEY_MISSING; + case BT_HCI_ERR_PAIRING_NOT_SUPPORTED: + return BT_SECURITY_ERR_PAIR_NOT_SUPPORTED; + case BT_HCI_ERR_PAIRING_NOT_ALLOWED: + return BT_SECURITY_ERR_PAIR_NOT_ALLOWED; + case BT_HCI_ERR_INVALID_PARAM: + return BT_SECURITY_ERR_INVALID_PARAM; + default: + return BT_SECURITY_ERR_UNSPECIFIED; + } +} + +static void reset_pairing(struct bt_conn *conn) +{ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + atomic_clear_bit(conn->flags, BT_CONN_BR_PAIRING); + atomic_clear_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR); + atomic_clear_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE); + } +#endif /* CONFIG_BT_BREDR */ + + /* Reset required security level to current operational */ + conn->required_sec_level = conn->sec_level; +} +#endif /* defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) */ + +#if defined(CONFIG_BT_BREDR) +static int reject_conn(const bt_addr_t *bdaddr, u8_t reason) +{ + struct bt_hci_cp_reject_conn_req *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_REJECT_CONN_REQ, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + cp->reason = reason; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_REJECT_CONN_REQ, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +static int accept_sco_conn(const bt_addr_t *bdaddr, struct bt_conn *sco_conn) +{ + struct bt_hci_cp_accept_sync_conn_req *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + cp->pkt_type = sco_conn->sco.pkt_type; + + cp->tx_bandwidth = 0x00001f40; + cp->rx_bandwidth = 0x00001f40; + if (!hfp_codec_msbc) { + cp->max_latency = 0x0007; + cp->retrans_effort = 0x01; + cp->content_format = BT_VOICE_CVSD_16BIT; + BT_DBG("eSCO air coding CVSD!"); + } else { + cp->max_latency = 0x000d; + cp->retrans_effort = 0x02; + cp->content_format = BT_VOICE_MSBC_16BIT; + BT_DBG("eSCO air coding mSBC!"); + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_SYNC_CONN_REQ, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +static int accept_conn(const bt_addr_t *bdaddr) +{ + struct bt_hci_cp_accept_conn_req *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_ACCEPT_CONN_REQ, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + cp->role = BT_HCI_ROLE_SLAVE; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_ACCEPT_CONN_REQ, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +static void bt_esco_conn_req(struct bt_hci_evt_conn_request *evt) +{ + struct bt_conn *sco_conn; + + sco_conn = bt_conn_add_sco(&evt->bdaddr, evt->link_type); + if (!sco_conn) { + reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + if (accept_sco_conn(&evt->bdaddr, sco_conn)) { + BT_ERR("Error accepting connection from %s", + bt_addr_str(&evt->bdaddr)); + reject_conn(&evt->bdaddr, BT_HCI_ERR_UNSPECIFIED); + bt_sco_cleanup(sco_conn); + return; + } + + sco_conn->role = BT_HCI_ROLE_SLAVE; + bt_conn_set_state(sco_conn, BT_CONN_CONNECT); + bt_conn_unref(sco_conn); +} + +static void conn_req(struct net_buf *buf) +{ + struct bt_hci_evt_conn_request *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("conn req from %s, type 0x%02x", bt_addr_str(&evt->bdaddr), + evt->link_type); + + if (evt->link_type != BT_HCI_ACL) { + bt_esco_conn_req(evt); + return; + } + + conn = bt_conn_add_br(&evt->bdaddr); + if (!conn) { + reject_conn(&evt->bdaddr, BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + accept_conn(&evt->bdaddr); + conn->role = BT_HCI_ROLE_SLAVE; + bt_conn_set_state(conn, BT_CONN_CONNECT); + bt_conn_unref(conn); +} + +static bool br_sufficient_key_size(struct bt_conn *conn) +{ + struct bt_hci_cp_read_encryption_key_size *cp; + struct bt_hci_rp_read_encryption_key_size *rp; + struct net_buf *buf, *rsp; + u8_t key_size; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE, + sizeof(*cp)); + if (!buf) { + BT_ERR("Failed to allocate command buffer"); + return false; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE, + buf, &rsp); + if (err) { + BT_ERR("Failed to read encryption key size (err %d)", err); + return false; + } + + if (rsp->len < sizeof(*rp)) { + BT_ERR("Too small command complete for encryption key size"); + net_buf_unref(rsp); + return false; + } + + rp = (void *)rsp->data; + key_size = rp->key_size; + net_buf_unref(rsp); + + BT_DBG("Encryption key size is %u", key_size); + + if (conn->sec_level == BT_SECURITY_L4) { + return key_size == BT_HCI_ENCRYPTION_KEY_SIZE_MAX; + } + + return key_size >= BT_HCI_ENCRYPTION_KEY_SIZE_MIN; +} + +static bool update_sec_level_br(struct bt_conn *conn) +{ + if (!conn->encrypt) { + conn->sec_level = BT_SECURITY_L1; + return true; + } + + if (conn->br.link_key) { + if (conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) { + if (conn->encrypt == 0x02) { + conn->sec_level = BT_SECURITY_L4; + } else { + conn->sec_level = BT_SECURITY_L3; + } + } else { + conn->sec_level = BT_SECURITY_L2; + } + } else { + BT_WARN("No BR/EDR link key found"); + conn->sec_level = BT_SECURITY_L2; + } + + if (!br_sufficient_key_size(conn)) { + BT_ERR("Encryption key size is not sufficient"); + bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + return false; + } + + if (conn->required_sec_level > conn->sec_level) { + BT_ERR("Failed to set required security level"); + bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + return false; + } + + return true; +} + +static void synchronous_conn_complete(struct net_buf *buf) +{ + struct bt_hci_evt_sync_conn_complete *evt = (void *)buf->data; + struct bt_conn *sco_conn; + u16_t handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle, + evt->link_type); + + sco_conn = bt_conn_lookup_addr_sco(&evt->bdaddr); + if (!sco_conn) { + BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + if (evt->status) { + sco_conn->err = evt->status; + bt_conn_set_state(sco_conn, BT_CONN_DISCONNECTED); + bt_conn_unref(sco_conn); + return; + } + + sco_conn->handle = handle; + bt_conn_set_state(sco_conn, BT_CONN_CONNECTED); + bt_conn_unref(sco_conn); +} + +static void conn_complete(struct net_buf *buf) +{ + struct bt_hci_evt_conn_complete *evt = (void *)buf->data; + struct bt_conn *conn; + struct bt_hci_cp_read_remote_features *cp; + u16_t handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("status 0x%02x, handle %u, type 0x%02x", evt->status, handle, + evt->link_type); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + if (evt->status) { + conn->err = evt->status; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_conn_unref(conn); + return; + } + + conn->handle = handle; + conn->err = 0U; + conn->encrypt = evt->encr_enabled; + + if (!update_sec_level_br(conn)) { + bt_conn_unref(conn); + return; + } + + bt_conn_set_state(conn, BT_CONN_CONNECTED); + bt_conn_unref(conn); + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_FEATURES, sizeof(*cp)); + if (!buf) { + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = evt->handle; + + bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL); +} + +static void pin_code_req(struct net_buf *buf) +{ + struct bt_hci_evt_pin_code_req *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + bt_conn_pin_code_req(conn); + bt_conn_unref(conn); +} + +static void link_key_notify(struct net_buf *buf) +{ + struct bt_hci_evt_link_key_notify *evt = (void *)buf->data; + struct bt_conn *conn; + + printf("bredr link key: "); + for(int i = 0; i < 16; i++) + { + printf("0x%02x ", evt->link_key[i]); + } + printf("\n"); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type); + + if (!conn->br.link_key) { + conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr); + } + if (!conn->br.link_key) { + BT_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr)); + bt_conn_unref(conn); + return; + } + + /* clear any old Link Key flags */ + conn->br.link_key->flags = 0U; + + switch (evt->key_type) { + case BT_LK_COMBINATION: + /* + * Setting Combination Link Key as AUTHENTICATED means it was + * successfully generated by 16 digits wide PIN code. + */ + if (atomic_test_and_clear_bit(conn->flags, + BT_CONN_BR_LEGACY_SECURE)) { + conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED; + } + memcpy(conn->br.link_key->val, evt->link_key, 16); + break; + case BT_LK_AUTH_COMBINATION_P192: + conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED; + /* fall through */ + __attribute__ ((fallthrough)); + case BT_LK_UNAUTH_COMBINATION_P192: + /* Mark no-bond so that link-key is removed on disconnection */ + if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { + atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); + } + + memcpy(conn->br.link_key->val, evt->link_key, 16); + break; + case BT_LK_AUTH_COMBINATION_P256: + conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED; + /* fall through */ + __attribute__ ((fallthrough)); + case BT_LK_UNAUTH_COMBINATION_P256: + conn->br.link_key->flags |= BT_LINK_KEY_SC; + + /* Mark no-bond so that link-key is removed on disconnection */ + if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) { + atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND); + } + + memcpy(conn->br.link_key->val, evt->link_key, 16); + break; + default: + BT_WARN("Unsupported Link Key type %u", evt->key_type); + (void)memset(conn->br.link_key->val, 0, + sizeof(conn->br.link_key->val)); + break; + } + + bt_conn_unref(conn); +} + +static void link_key_neg_reply(const bt_addr_t *bdaddr) +{ + struct bt_hci_cp_link_key_neg_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp)); + if (!buf) { + BT_ERR("Out of command buffers"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_NEG_REPLY, buf, NULL); +} + +static void link_key_reply(const bt_addr_t *bdaddr, const u8_t *lk) +{ + struct bt_hci_cp_link_key_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp)); + if (!buf) { + BT_ERR("Out of command buffers"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + memcpy(cp->link_key, lk, 16); + bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_REPLY, buf, NULL); +} + +static void link_key_req(struct net_buf *buf) +{ + struct bt_hci_evt_link_key_req *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("%s", bt_addr_str(&evt->bdaddr)); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + link_key_neg_reply(&evt->bdaddr); + return; + } + + if (!conn->br.link_key) { + conn->br.link_key = bt_keys_find_link_key(&evt->bdaddr); + } + + if (!conn->br.link_key) { + link_key_neg_reply(&evt->bdaddr); + bt_conn_unref(conn); + return; + } + + /* + * Enforce regenerate by controller stronger link key since found one + * in database not covers requested security level. + */ + if (!(conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) && + conn->required_sec_level > BT_SECURITY_L2) { + link_key_neg_reply(&evt->bdaddr); + bt_conn_unref(conn); + return; + } + + link_key_reply(&evt->bdaddr, conn->br.link_key->val); + bt_conn_unref(conn); +} + +static void io_capa_neg_reply(const bt_addr_t *bdaddr, const u8_t reason) +{ + struct bt_hci_cp_io_capability_neg_reply *cp; + struct net_buf *resp_buf; + + resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, + sizeof(*cp)); + if (!resp_buf) { + BT_ERR("Out of command buffers"); + return; + } + + cp = net_buf_add(resp_buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + cp->reason = reason; + bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, resp_buf, NULL); +} + +static void io_capa_resp(struct net_buf *buf) +{ + struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("remote %s, IOcapa 0x%02x, auth 0x%02x", + bt_addr_str(&evt->bdaddr), evt->capability, evt->authentication); + + if (evt->authentication > BT_HCI_GENERAL_BONDING_MITM) { + BT_ERR("Invalid remote authentication requirements"); + io_capa_neg_reply(&evt->bdaddr, + BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL); + return; + } + + if (evt->capability > BT_IO_NO_INPUT_OUTPUT) { + BT_ERR("Invalid remote io capability requirements"); + io_capa_neg_reply(&evt->bdaddr, + BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL); + return; + } + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + conn->br.remote_io_capa = evt->capability; + conn->br.remote_auth = evt->authentication; + atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING); + bt_conn_unref(conn); +} + +static void io_capa_req(struct net_buf *buf) +{ + struct bt_hci_evt_io_capa_req *evt = (void *)buf->data; + struct net_buf *resp_buf; + struct bt_conn *conn; + struct bt_hci_cp_io_capability_reply *cp; + u8_t auth; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY, + sizeof(*cp)); + if (!resp_buf) { + BT_ERR("Out of command buffers"); + bt_conn_unref(conn); + return; + } + + /* + * Set authentication requirements when acting as pairing initiator to + * 'dedicated bond' with MITM protection set if local IO capa + * potentially allows it, and for acceptor, based on local IO capa and + * remote's authentication set. + */ + if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR)) { + if (bt_conn_get_io_capa() != BT_IO_NO_INPUT_OUTPUT) { + auth = BT_HCI_DEDICATED_BONDING_MITM; + } else { + auth = BT_HCI_DEDICATED_BONDING; + } + } else { + auth = bt_conn_ssp_get_auth(conn); + } + + cp = net_buf_add(resp_buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &evt->bdaddr); + cp->capability = bt_conn_get_io_capa(); + cp->authentication = auth; + cp->oob_data = 0U; + bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_REPLY, resp_buf, NULL); + bt_conn_unref(conn); +} + +static void ssp_complete(struct net_buf *buf) +{ + struct bt_hci_evt_ssp_complete *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("status 0x%02x", evt->status); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + bt_conn_ssp_auth_complete(conn, security_err_get(evt->status)); + if (evt->status) { + bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + } + + bt_conn_unref(conn); +} + +static void user_confirm_req(struct net_buf *buf) +{ + struct bt_hci_evt_user_confirm_req *evt = (void *)buf->data; + struct bt_conn *conn; + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey)); + bt_conn_unref(conn); +} + +static void user_passkey_notify(struct net_buf *buf) +{ + struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG(""); + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey)); + bt_conn_unref(conn); +} + +static void user_passkey_req(struct net_buf *buf) +{ + struct bt_hci_evt_user_passkey_req *evt = (void *)buf->data; + struct bt_conn *conn; + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + bt_conn_ssp_auth(conn, 0); + bt_conn_unref(conn); +} + +struct discovery_priv { + u16_t clock_offset; + u8_t pscan_rep_mode; + u8_t resolving; +} __packed; + +static int request_name(const bt_addr_t *addr, u8_t pscan, u16_t offset) +{ + struct bt_hci_cp_remote_name_request *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_REQUEST, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + bt_addr_copy(&cp->bdaddr, addr); + cp->pscan_rep_mode = pscan; + cp->reserved = 0x00; /* reserver, should be set to 0x00 */ + cp->clock_offset = offset; + + return bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_REQUEST, buf, NULL); +} + +#define EIR_SHORT_NAME 0x08 +#define EIR_COMPLETE_NAME 0x09 + +static bool eir_has_name(const u8_t *eir) +{ + int len = 240; + + while (len) { + if (len < 2) { + break; + }; + + /* Look for early termination */ + if (!eir[0]) { + break; + } + + /* Check if field length is correct */ + if (eir[0] > len - 1) { + break; + } + + switch (eir[1]) { + case EIR_SHORT_NAME: + case EIR_COMPLETE_NAME: + if (eir[0] > 1) { + return true; + } + break; + default: + break; + } + + /* Parse next AD Structure */ + len -= eir[0] + 1; + eir += eir[0] + 1; + } + + return false; +} + +static void report_discovery_results(void) +{ + bool resolving_names = false; + int i; + + for (i = 0; i < discovery_results_count; i++) { + struct discovery_priv *priv; + + priv = (struct discovery_priv *)&discovery_results[i]._priv; + + if (eir_has_name(discovery_results[i].eir)) { + continue; + } + + if (request_name(&discovery_results[i].addr, + priv->pscan_rep_mode, priv->clock_offset)) { + continue; + } + + priv->resolving = 1U; + resolving_names = true; + } + + if (resolving_names) { + return; + } + + atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY); + + discovery_cb(discovery_results, discovery_results_count); + + discovery_cb = NULL; + discovery_results = NULL; + discovery_results_size = 0; + discovery_results_count = 0; +} + +static void inquiry_complete(struct net_buf *buf) +{ + struct bt_hci_evt_inquiry_complete *evt = (void *)buf->data; + + if (evt->status) { + BT_ERR("Failed to complete inquiry"); + } + + report_discovery_results(); +} + +static struct bt_br_discovery_result *get_result_slot(const bt_addr_t *addr, + s8_t rssi) +{ + struct bt_br_discovery_result *result = NULL; + size_t i; + + /* check if already present in results */ + for (i = 0; i < discovery_results_count; i++) { + if (!bt_addr_cmp(addr, &discovery_results[i].addr)) { + return &discovery_results[i]; + } + } + + /* Pick a new slot (if available) */ + if (discovery_results_count < discovery_results_size) { + bt_addr_copy(&discovery_results[discovery_results_count].addr, + addr); + return &discovery_results[discovery_results_count++]; + } + + /* ignore if invalid RSSI */ + if (rssi == 0xff) { + return NULL; + } + + /* + * Pick slot with smallest RSSI that is smaller then passed RSSI + * TODO handle TX if present + */ + for (i = 0; i < discovery_results_size; i++) { + if (discovery_results[i].rssi > rssi) { + continue; + } + + if (!result || result->rssi > discovery_results[i].rssi) { + result = &discovery_results[i]; + } + } + + if (result) { + BT_DBG("Reusing slot (old %s rssi %d dBm)", + bt_addr_str(&result->addr), result->rssi); + + bt_addr_copy(&result->addr, addr); + } + + return result; +} + +static void inquiry_result_with_rssi(struct net_buf *buf) +{ + u8_t num_reports = net_buf_pull_u8(buf); + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) { + return; + } + + BT_DBG("number of results: %u", num_reports); + + while (num_reports--) { + struct bt_hci_evt_inquiry_result_with_rssi *evt; + struct bt_br_discovery_result *result; + struct discovery_priv *priv; + + if (buf->len < sizeof(*evt)) { + BT_ERR("Unexpected end to buffer"); + return; + } + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi); + + result = get_result_slot(&evt->addr, evt->rssi); + if (!result) { + return; + } + + priv = (struct discovery_priv *)&result->_priv; + priv->pscan_rep_mode = evt->pscan_rep_mode; + priv->clock_offset = evt->clock_offset; + + memcpy(result->cod, evt->cod, 3); + result->rssi = evt->rssi; + + /* we could reuse slot so make sure EIR is cleared */ + (void)memset(result->eir, 0, sizeof(result->eir)); + } +} + +static void extended_inquiry_result(struct net_buf *buf) +{ + struct bt_hci_evt_extended_inquiry_result *evt = (void *)buf->data; + struct bt_br_discovery_result *result; + struct discovery_priv *priv; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) { + return; + } + + BT_DBG("%s rssi %d dBm", bt_addr_str(&evt->addr), evt->rssi); + + result = get_result_slot(&evt->addr, evt->rssi); + if (!result) { + return; + } + + priv = (struct discovery_priv *)&result->_priv; + priv->pscan_rep_mode = evt->pscan_rep_mode; + priv->clock_offset = evt->clock_offset; + + result->rssi = evt->rssi; + memcpy(result->cod, evt->cod, 3); + memcpy(result->eir, evt->eir, sizeof(result->eir)); +} + +static void remote_name_request_complete(struct net_buf *buf) +{ + struct bt_hci_evt_remote_name_req_complete *evt = (void *)buf->data; + struct bt_br_discovery_result *result; + struct discovery_priv *priv; + int eir_len = 240; + u8_t *eir; + int i; + + result = get_result_slot(&evt->bdaddr, 0xff); + if (!result) { + return; + } + + priv = (struct discovery_priv *)&result->_priv; + priv->resolving = 0U; + + if (evt->status) { + goto check_names; + } + + eir = result->eir; + + while (eir_len) { + if (eir_len < 2) { + break; + }; + + /* Look for early termination */ + if (!eir[0]) { + size_t name_len; + + eir_len -= 2; + + /* name is null terminated */ + name_len = strlen((const char *)evt->name); + + if (name_len > eir_len) { + eir[0] = eir_len + 1; + eir[1] = EIR_SHORT_NAME; + } else { + eir[0] = name_len + 1; + eir[1] = EIR_SHORT_NAME; + } + + memcpy(&eir[2], evt->name, eir[0] - 1); + + break; + } + + /* Check if field length is correct */ + if (eir[0] > eir_len - 1) { + break; + } + + /* next EIR Structure */ + eir_len -= eir[0] + 1; + eir += eir[0] + 1; + } + +check_names: + /* if still waiting for names */ + for (i = 0; i < discovery_results_count; i++) { + struct discovery_priv *priv; + + priv = (struct discovery_priv *)&discovery_results[i]._priv; + + if (priv->resolving) { + return; + } + } + + /* all names resolved, report discovery results */ + atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY); + + discovery_cb(discovery_results, discovery_results_count); + + discovery_cb = NULL; + discovery_results = NULL; + discovery_results_size = 0; + discovery_results_count = 0; +} + +static void link_encr(const u16_t handle) +{ + struct bt_hci_cp_set_conn_encrypt *encr; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr)); + if (!buf) { + BT_ERR("Out of command buffers"); + return; + } + + encr = net_buf_add(buf, sizeof(*encr)); + encr->handle = sys_cpu_to_le16(handle); + encr->encrypt = 0x01; + + bt_hci_cmd_send_sync(BT_HCI_OP_SET_CONN_ENCRYPT, buf, NULL); +} + +static void auth_complete(struct net_buf *buf) +{ + struct bt_hci_evt_auth_complete *evt = (void *)buf->data; + struct bt_conn *conn; + u16_t handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("status 0x%02x, handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Can't find conn for handle %u", handle); + return; + } + + if (evt->status) { + if (conn->state == BT_CONN_CONNECTED) { + /* + * Inform layers above HCI about non-zero authentication + * status to make them able cleanup pending jobs. + */ + bt_l2cap_encrypt_change(conn, evt->status); + } + reset_pairing(conn); + } else { + link_encr(handle); + } + + bt_conn_unref(conn); +} + +static void read_remote_features_complete(struct net_buf *buf) +{ + struct bt_hci_evt_remote_features *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_hci_cp_read_remote_ext_features *cp; + struct bt_conn *conn; + + BT_DBG("status 0x%02x handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Can't find conn for handle %u", handle); + return; + } + + if (evt->status) { + goto done; + } + + memcpy(conn->br.features[0], evt->features, sizeof(evt->features)); + + if (!BT_FEAT_EXT_FEATURES(conn->br.features)) { + goto done; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_REMOTE_EXT_FEATURES, + sizeof(*cp)); + if (!buf) { + goto done; + } + + /* Read remote host features (page 1) */ + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = evt->handle; + cp->page = 0x01; + + bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_EXT_FEATURES, buf, NULL); + +done: + bt_conn_unref(conn); +} + +static void read_remote_ext_features_complete(struct net_buf *buf) +{ + struct bt_hci_evt_remote_ext_features *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + BT_DBG("status 0x%02x handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Can't find conn for handle %u", handle); + return; + } + + if (!evt->status && evt->page == 0x01) { + memcpy(conn->br.features[1], evt->features, + sizeof(conn->br.features[1])); + } + + bt_conn_unref(conn); +} + +static void role_change(struct net_buf *buf) +{ + struct bt_hci_evt_role_change *evt = (void *)buf->data; + struct bt_conn *conn; + + BT_DBG("status 0x%02x role %u addr %s", evt->status, evt->role, + bt_addr_str(&evt->bdaddr)); + + if (evt->status) { + return; + } + + conn = bt_conn_lookup_addr_br(&evt->bdaddr); + if (!conn) { + BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr)); + return; + } + + if (evt->role) { + conn->role = BT_CONN_ROLE_SLAVE; + } else { + conn->role = BT_CONN_ROLE_MASTER; + } + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_SMP) +static int le_set_privacy_mode(const bt_addr_le_t *addr, u8_t mode) +{ + struct bt_hci_cp_le_set_privacy_mode cp; + struct net_buf *buf; + int err; + + /* Check if set privacy mode command is supported */ + if (!BT_CMD_TEST(bt_dev.supported_commands, 39, 2)) { + BT_WARN("Set privacy mode command is not supported"); + return 0; + } + + BT_DBG("addr %s mode 0x%02x", bt_addr_le_str(addr), mode); + + bt_addr_le_copy(&cp.id_addr, addr); + cp.mode = mode; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PRIVACY_MODE, sizeof(cp)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &cp, sizeof(cp)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PRIVACY_MODE, buf, NULL); + if (err) { + return err; + } + + return 0; +} +#if defined(CONFIG_BT_STACK_PTS) +int addr_res_enable(u8_t enable) +#else +static int addr_res_enable(u8_t enable) +#endif +{ + struct net_buf *buf; + + BT_DBG("%s", enable ? "enabled" : "disabled"); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADDR_RES_ENABLE, 1); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_u8(buf, enable); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADDR_RES_ENABLE, + buf, NULL); +} + + +#if defined(CONFIG_BT_STACK_PTS) +int hci_id_add(const bt_addr_le_t *addr, u8_t val[16]) +#else +static int hci_id_add(const bt_addr_le_t *addr, u8_t val[16]) +#endif +{ + struct bt_hci_cp_le_add_dev_to_rl *cp; + struct net_buf *buf; + + BT_DBG("addr %s", bt_addr_le_str(addr)); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ADD_DEV_TO_RL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->peer_id_addr, addr); + memcpy(cp->peer_irk, val, 16); + +#if defined(CONFIG_BT_PRIVACY) + memcpy(cp->local_irk, bt_dev.irk, 16); +#else + (void)memset(cp->local_irk, 0, 16); +#endif + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_RL, buf, NULL); +} + +void bt_id_add(struct bt_keys *keys) +{ + bool adv_enabled; +#if defined(CONFIG_BT_OBSERVER) + bool scan_enabled; +#endif /* CONFIG_BT_OBSERVER */ + struct bt_conn *conn; + int err; + + BT_DBG("addr %s", bt_addr_le_str(&keys->addr)); + + /* Nothing to be done if host-side resolving is used */ + if (!bt_dev.le.rl_size || bt_dev.le.rl_entries > bt_dev.le.rl_size) { + bt_dev.le.rl_entries++; + return; + } + + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT); + if (conn) { + atomic_set_bit(bt_dev.flags, BT_DEV_ID_PENDING); + keys->flags |= BT_KEYS_ID_PENDING_ADD; + bt_conn_unref(conn); + return; + } + + adv_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING); + if (adv_enabled) { + set_advertise_enable(false); + } + +#if defined(CONFIG_BT_OBSERVER) + scan_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING); + if (scan_enabled) { + set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + } +#endif /* CONFIG_BT_OBSERVER */ + + /* If there are any existing entries address resolution will be on */ + if (bt_dev.le.rl_entries) { + err = addr_res_enable(BT_HCI_ADDR_RES_DISABLE); + if (err) { + BT_WARN("Failed to disable address resolution"); + goto done; + } + } + + if (bt_dev.le.rl_entries == bt_dev.le.rl_size) { + BT_WARN("Resolving list size exceeded. Switching to host."); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CLEAR_RL, NULL, NULL); + if (err) { + BT_ERR("Failed to clear resolution list"); + goto done; + } + + bt_dev.le.rl_entries++; + + goto done; + } + + err = hci_id_add(&keys->addr, keys->irk.val); + if (err) { + BT_ERR("Failed to add IRK to controller"); + goto done; + } + + bt_dev.le.rl_entries++; + + /* + * According to Core Spec. 5.0 Vol 1, Part A 5.4.5 Privacy Feature + * + * By default, network privacy mode is used when private addresses are + * resolved and generated by the Controller, so advertising packets from + * peer devices that contain private addresses will only be accepted. + * By changing to the device privacy mode device is only concerned about + * its privacy and will accept advertising packets from peer devices + * that contain their identity address as well as ones that contain + * a private address, even if the peer device has distributed its IRK in + * the past. + */ + err = le_set_privacy_mode(&keys->addr, BT_HCI_LE_PRIVACY_MODE_DEVICE); + if (err) { + BT_ERR("Failed to set privacy mode"); + goto done; + } + +done: + addr_res_enable(BT_HCI_ADDR_RES_ENABLE); + +#if defined(CONFIG_BT_OBSERVER) + if (scan_enabled) { + set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE); + } +#endif /* CONFIG_BT_OBSERVER */ + + if (adv_enabled) { + set_advertise_enable(true); + } +} + +static void keys_add_id(struct bt_keys *keys, void *data) +{ + hci_id_add(&keys->addr, keys->irk.val); +} + +void bt_id_del(struct bt_keys *keys) +{ + struct bt_hci_cp_le_rem_dev_from_rl *cp; + bool adv_enabled; +#if defined(CONFIG_BT_OBSERVER) + bool scan_enabled; +#endif /* CONFIG_BT_OBSERVER */ + struct bt_conn *conn; + struct net_buf *buf; + int err; + + BT_DBG("addr %s", bt_addr_le_str(&keys->addr)); + + if (!bt_dev.le.rl_size || + bt_dev.le.rl_entries > bt_dev.le.rl_size + 1) { + bt_dev.le.rl_entries--; + return; + } + + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT); + if (conn) { + atomic_set_bit(bt_dev.flags, BT_DEV_ID_PENDING); + keys->flags |= BT_KEYS_ID_PENDING_DEL; + bt_conn_unref(conn); + return; + } + + adv_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING); + if (adv_enabled) { + set_advertise_enable(false); + } + +#if defined(CONFIG_BT_OBSERVER) + scan_enabled = atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING); + if (scan_enabled) { + set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + } +#endif /* CONFIG_BT_OBSERVER */ + + err = addr_res_enable(BT_HCI_ADDR_RES_DISABLE); + if (err) { + BT_ERR("Disabling address resolution failed (err %d)", err); + goto done; + } + + /* We checked size + 1 earlier, so here we know we can fit again */ + if (bt_dev.le.rl_entries > bt_dev.le.rl_size) { + bt_dev.le.rl_entries--; + keys->keys &= ~BT_KEYS_IRK; + bt_keys_foreach(BT_KEYS_IRK, keys_add_id, NULL); + goto done; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REM_DEV_FROM_RL, sizeof(*cp)); + if (!buf) { + goto done; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->peer_id_addr, &keys->addr); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_RL, buf, NULL); + if (err) { + BT_ERR("Failed to remove IRK from controller"); + goto done; + } + + bt_dev.le.rl_entries--; + +done: + /* Only re-enable if there are entries to do resolving with */ + if (bt_dev.le.rl_entries) { + addr_res_enable(BT_HCI_ADDR_RES_ENABLE); + } + +#if defined(CONFIG_BT_OBSERVER) + if (scan_enabled) { + set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE); + } +#endif /* CONFIG_BT_OBSERVER */ + + if (adv_enabled) { + set_advertise_enable(true); + } +} + +static void update_sec_level(struct bt_conn *conn) +{ + if (!conn->encrypt) { + conn->sec_level = BT_SECURITY_L1; + return; + } + + if (conn->le.keys && (conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) { + if (conn->le.keys->flags & BT_KEYS_SC && + conn->le.keys->enc_size == BT_SMP_MAX_ENC_KEY_SIZE) { + conn->sec_level = BT_SECURITY_L4; + } else { + conn->sec_level = BT_SECURITY_L3; + } + } else { + conn->sec_level = BT_SECURITY_L2; + } + + if (conn->required_sec_level > conn->sec_level) { + BT_ERR("Failed to set required security level"); + bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + } +} +#endif /* CONFIG_BT_SMP */ + +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +static void hci_encrypt_change(struct net_buf *buf) +{ + struct bt_hci_evt_encrypt_change *evt = (void *)buf->data; + u16_t handle = sys_le16_to_cpu(evt->handle); + struct bt_conn *conn; + + BT_DBG("status 0x%02x handle %u encrypt 0x%02x", evt->status, handle, + evt->encrypt); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to look up conn with handle %u", handle); + return; + } + + if (evt->status) { + reset_pairing(conn); + bt_l2cap_encrypt_change(conn, evt->status); + bt_conn_security_changed(conn, security_err_get(evt->status)); + bt_conn_unref(conn); + return; + } + + conn->encrypt = evt->encrypt; + +#if defined(CONFIG_BT_SMP) + if (conn->type == BT_CONN_TYPE_LE) { + /* + * we update keys properties only on successful encryption to + * avoid losing valid keys if encryption was not successful. + * + * Update keys with last pairing info for proper sec level + * update. This is done only for LE transport, for BR/EDR keys + * are updated on HCI 'Link Key Notification Event' + */ + if (conn->encrypt) { + bt_smp_update_keys(conn); + } + update_sec_level(conn); + } +#endif /* CONFIG_BT_SMP */ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + if (!update_sec_level_br(conn)) { + bt_conn_unref(conn); + return; + } + + if (IS_ENABLED(CONFIG_BT_SMP)) { + /* + * Start SMP over BR/EDR if we are pairing and are + * master on the link + */ + if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING) && + conn->role == BT_CONN_ROLE_MASTER) { + bt_smp_br_send_pairing_req(conn); + } + } + } +#endif /* CONFIG_BT_BREDR */ + reset_pairing(conn); + + bt_l2cap_encrypt_change(conn, evt->status); + bt_conn_security_changed(conn, BT_SECURITY_ERR_SUCCESS); + + bt_conn_unref(conn); +} + +static void hci_encrypt_key_refresh_complete(struct net_buf *buf) +{ + struct bt_hci_evt_encrypt_key_refresh_complete *evt = (void *)buf->data; + struct bt_conn *conn; + u16_t handle; + + handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("status 0x%02x handle %u", evt->status, handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to look up conn with handle %u", handle); + return; + } + + if (evt->status) { + reset_pairing(conn); + bt_l2cap_encrypt_change(conn, evt->status); + bt_conn_security_changed(conn, security_err_get(evt->status)); + bt_conn_unref(conn); + return; + } + + /* + * Update keys with last pairing info for proper sec level update. + * This is done only for LE transport. For BR/EDR transport keys are + * updated on HCI 'Link Key Notification Event', therefore update here + * only security level based on available keys and encryption state. + */ +#if defined(CONFIG_BT_SMP) + if (conn->type == BT_CONN_TYPE_LE) { + bt_smp_update_keys(conn); + update_sec_level(conn); + } +#endif /* CONFIG_BT_SMP */ +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + if (!update_sec_level_br(conn)) { + bt_conn_unref(conn); + return; + } + } +#endif /* CONFIG_BT_BREDR */ + + reset_pairing(conn); + bt_l2cap_encrypt_change(conn, evt->status); + bt_conn_security_changed(conn, BT_SECURITY_ERR_SUCCESS); + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_SMP) +static void le_ltk_neg_reply(u16_t handle) +{ + struct bt_hci_cp_le_ltk_req_neg_reply *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, sizeof(*cp)); + if (!buf) { + BT_ERR("Out of command buffers"); + + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + + bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf); +} + +static void le_ltk_reply(u16_t handle, u8_t *ltk) +{ + struct bt_hci_cp_le_ltk_req_reply *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY, + sizeof(*cp)); + if (!buf) { + BT_ERR("Out of command buffers"); + return; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + memcpy(cp->ltk, ltk, sizeof(cp->ltk)); + + bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf); +} + +static void le_ltk_request(struct net_buf *buf) +{ + struct bt_hci_evt_le_ltk_request *evt = (void *)buf->data; + struct bt_conn *conn; + u16_t handle; + u8_t ltk[16]; + + handle = sys_le16_to_cpu(evt->handle); + + BT_DBG("handle %u", handle); + + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("Unable to lookup conn for handle %u", handle); + return; + } + + if (bt_smp_request_ltk(conn, evt->rand, evt->ediv, ltk)) { + le_ltk_reply(handle, ltk); + } else { + le_ltk_neg_reply(handle); + } + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_SMP */ + +#if defined(CONFIG_BT_ECC) +static void le_pkey_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_p256_public_key_complete *evt = (void *)buf->data; + struct bt_pub_key_cb *cb; + + BT_DBG("status: 0x%02x", evt->status); + + atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); + + if (!evt->status) { + memcpy(pub_key, evt->key, 64); + atomic_set_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); + } + + for (cb = pub_key_cb; cb; cb = cb->_next) { + cb->func(evt->status ? NULL : pub_key); + } + + pub_key_cb = NULL; +} + +static void le_dhkey_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_generate_dhkey_complete *evt = (void *)buf->data; + + BT_DBG("status: 0x%02x", evt->status); + + if (dh_key_cb) { + dh_key_cb(evt->status ? NULL : evt->dhkey); + dh_key_cb = NULL; + } +} +#endif /* CONFIG_BT_ECC */ + +static void hci_reset_complete(struct net_buf *buf) +{ + u8_t status = buf->data[0]; + atomic_t flags; + + BT_DBG("status 0x%02x", status); + + if (status) { + return; + } + + scan_dev_found_cb = NULL; +#if defined(CONFIG_BT_BREDR) + discovery_cb = NULL; + discovery_results = NULL; + discovery_results_size = 0; + discovery_results_count = 0; +#endif /* CONFIG_BT_BREDR */ + + flags = (atomic_get(bt_dev.flags) & BT_DEV_PERSISTENT_FLAGS); + atomic_set(bt_dev.flags, flags); +} + +static void hci_cmd_done(u16_t opcode, u8_t status, struct net_buf *buf) +{ + BT_DBG("opcode 0x%04x status 0x%02x buf %p", opcode, status, buf); + + if (net_buf_pool_get(buf->pool_id) != &hci_cmd_pool) { + BT_WARN("opcode 0x%04x pool id %u pool %p != &hci_cmd_pool %p", + opcode, buf->pool_id, net_buf_pool_get(buf->pool_id), + &hci_cmd_pool); + return; + } + + if (cmd(buf)->opcode != opcode) { + BT_WARN("OpCode 0x%04x completed instead of expected 0x%04x", + opcode, cmd(buf)->opcode); + } + + if (cmd(buf)->state && !status) { + struct cmd_state_set *update = cmd(buf)->state; + + atomic_set_bit_to(update->target, update->bit, update->val); + } + + /* If the command was synchronous wake up bt_hci_cmd_send_sync() */ + if (cmd(buf)->sync) { + cmd(buf)->status = status; + k_sem_give(cmd(buf)->sync); + } +} + +static void hci_cmd_complete(struct net_buf *buf) +{ + struct bt_hci_evt_cmd_complete *evt; + u8_t status, ncmd; + u16_t opcode; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + ncmd = evt->ncmd; + opcode = sys_le16_to_cpu(evt->opcode); + + BT_DBG("opcode 0x%04x", opcode); + + /* All command return parameters have a 1-byte status in the + * beginning, so we can safely make this generalization. + */ + status = buf->data[0]; + + hci_cmd_done(opcode, status, buf); + + /* Allow next command to be sent */ + if (ncmd) { + k_sem_give(&bt_dev.ncmd_sem); + } +} + +static void hci_cmd_status(struct net_buf *buf) +{ + struct bt_hci_evt_cmd_status *evt; + u16_t opcode; + u8_t ncmd; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + opcode = sys_le16_to_cpu(evt->opcode); + ncmd = evt->ncmd; + + BT_DBG("opcode 0x%04x", opcode); + + hci_cmd_done(opcode, evt->status, buf); + + /* Allow next command to be sent */ + if (ncmd) { + k_sem_give(&bt_dev.ncmd_sem); + } +} + +#if defined(CONFIG_BT_OBSERVER) +static int start_le_scan(u8_t scan_type, u16_t interval, u16_t window) +{ + struct bt_hci_cp_le_set_scan_param set_param; + struct net_buf *buf; + int err; + + (void)memset(&set_param, 0, sizeof(set_param)); + + set_param.scan_type = scan_type; + + /* for the rest parameters apply default values according to + * spec 4.2, vol2, part E, 7.8.10 + */ + set_param.interval = sys_cpu_to_le16(interval); + set_param.window = sys_cpu_to_le16(window); + + if (IS_ENABLED(CONFIG_BT_WHITELIST) && + atomic_test_bit(bt_dev.flags, BT_DEV_SCAN_WL)) { + set_param.filter_policy = BT_HCI_LE_SCAN_FP_USE_WHITELIST; + } else { + set_param.filter_policy = BT_HCI_LE_SCAN_FP_NO_WHITELIST; + } + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + err = le_set_private_addr(BT_ID_DEFAULT); + if (err) { + return err; + } + + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + set_param.addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + } else { + set_param.addr_type = BT_ADDR_LE_RANDOM; + } + } else { + set_param.addr_type = bt_dev.id_addr[0].type; + + /* Use NRPA unless identity has been explicitly requested + * (through Kconfig), or if there is no advertising ongoing. + */ + if (!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) && + scan_type == BT_HCI_LE_SCAN_ACTIVE && + !atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + err = le_set_private_addr(BT_ID_DEFAULT); + if (err) { + return err; + } + + set_param.addr_type = BT_ADDR_LE_RANDOM; + } else if (set_param.addr_type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&bt_dev.id_addr[0].a); + if (err) { + return err; + } + } + + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_PARAM, buf, NULL); + + err = set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, + scan_type == BT_HCI_LE_SCAN_ACTIVE); + + return 0; +} + +#if defined(CONFIG_BT_STACK_PTS) +static int start_le_scan_with_isrpa(u8_t scan_type, u16_t interval, u16_t window, u8_t addre_type) +{ + struct bt_hci_cp_le_set_scan_param set_param; + struct net_buf *buf; + int err = 0; + + memset(&set_param, 0, sizeof(set_param)); + + set_param.scan_type = scan_type; + + /* for the rest parameters apply default values according to + * spec 4.2, vol2, part E, 7.8.10 + */ + set_param.interval = sys_cpu_to_le16(interval); + set_param.window = sys_cpu_to_le16(window); + set_param.filter_policy = 0x00; + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + if(addre_type == 1) + err = le_set_private_addr(BT_ID_DEFAULT); + else if(addre_type == 0) + err = le_set_non_resolv_private_addr(BT_ID_DEFAULT); + if (err) { + return err; + } + + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + if(addre_type == 2) + set_param.addr_type = BT_ADDR_LE_PUBLIC; + if(addre_type == 1) + set_param.addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + else if(addre_type == 0) + set_param.addr_type = BT_ADDR_LE_RANDOM; + } else { + set_param.addr_type = BT_ADDR_LE_RANDOM; + } + } else { + set_param.addr_type = bt_dev.id_addr[0].type; + + /* Use NRPA unless identity has been explicitly requested + * (through Kconfig), or if there is no advertising ongoing. + */ + if (!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY) && + scan_type == BT_HCI_LE_SCAN_ACTIVE && + !atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + err = le_set_private_addr(BT_ID_DEFAULT); + if (err) { + return err; + } + + set_param.addr_type = BT_ADDR_LE_RANDOM; + } + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_PARAM, buf, NULL); + + err = set_le_scan_enable(BT_HCI_LE_SCAN_ENABLE); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ACTIVE_SCAN, + scan_type == BT_HCI_LE_SCAN_ACTIVE); + + + return 0; +} + +#endif + +int bt_le_scan_update(bool fast_scan) +{ + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return 0; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + int err; + + err = set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + return err; + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL)) { + u16_t interval, window; + struct bt_conn *conn; + + /* don't restart scan if we have pending connection */ + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT); + if (conn) { + bt_conn_unref(conn); + return 0; + } + + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT_SCAN); + if (!conn) { + return 0; + } + + //atomic_set_bit(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP); + atomic_clear_bit(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP); + + bt_conn_unref(conn); + + if (fast_scan) { + interval = BT_GAP_SCAN_FAST_INTERVAL; + window = BT_GAP_SCAN_FAST_WINDOW; + } else { + interval = CONFIG_BT_BACKGROUND_SCAN_INTERVAL; + window = CONFIG_BT_BACKGROUND_SCAN_WINDOW; + } + + return start_le_scan(BT_HCI_LE_SCAN_PASSIVE, interval, window); + } + + return 0; +} + +void bt_data_parse(struct net_buf_simple *ad, + bool (*func)(struct bt_data *data, void *user_data), + void *user_data) +{ + while (ad->len > 1) { + struct bt_data data; + u8_t len; + + len = net_buf_simple_pull_u8(ad); + if (len == 0U) { + /* Early termination */ + return; + } + + if (len > ad->len) { + BT_WARN("Malformed data"); + return; + } + + data.type = net_buf_simple_pull_u8(ad); + data.data_len = len - 1; + data.data = ad->data; + + if (!func(&data, user_data)) { + return; + } + + net_buf_simple_pull(ad, len - 1); + } +} + +static void le_adv_report(struct net_buf *buf) +{ + u8_t num_reports = net_buf_pull_u8(buf); + struct bt_hci_evt_le_advertising_info *info; + + BT_DBG("Adv number of reports %u", num_reports); + + while (num_reports--) { + bt_addr_le_t id_addr; + s8_t rssi; + + if (buf->len < sizeof(*info)) { + BT_ERR("Unexpected end of buffer"); + break; + } + + info = net_buf_pull_mem(buf, sizeof(*info)); + rssi = info->data[info->length]; + + BT_DBG("%s event %u, len %u, rssi %d dBm", + bt_addr_le_str(&info->addr), + info->evt_type, info->length, rssi); + + if (info->addr.type == BT_ADDR_LE_PUBLIC_ID || + info->addr.type == BT_ADDR_LE_RANDOM_ID) { + bt_addr_le_copy(&id_addr, &info->addr); + id_addr.type -= BT_ADDR_LE_PUBLIC_ID; + } else { + bt_addr_le_copy(&id_addr, + bt_lookup_id_addr(bt_dev.adv_id, + &info->addr)); + } + + if (scan_dev_found_cb) { + struct net_buf_simple_state state; + + net_buf_simple_save(&buf->b, &state); + + buf->len = info->length; + scan_dev_found_cb(&id_addr, rssi, info->evt_type, + &buf->b); + + net_buf_simple_restore(&buf->b, &state); + } + +#if defined(CONFIG_BT_CENTRAL) + check_pending_conn(&id_addr, &info->addr, info->evt_type); +#endif /* CONFIG_BT_CENTRAL */ + + net_buf_pull(buf, info->length + sizeof(rssi)); + } +} +#endif /* CONFIG_BT_OBSERVER */ + +int bt_hci_get_conn_handle(const struct bt_conn *conn, u16_t *conn_handle) +{ + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + *conn_handle = conn->handle; + return 0; +} + +#if defined(CONFIG_BT_HCI_VS_EVT_USER) +int bt_hci_register_vnd_evt_cb(bt_hci_vnd_evt_cb_t cb) +{ + hci_vnd_evt_cb = cb; + return 0; +} +#endif /* CONFIG_BT_HCI_VS_EVT_USER */ + +static void hci_vendor_event(struct net_buf *buf) +{ + bool handled = false; + +#if defined(CONFIG_BT_HCI_VS_EVT_USER) + if (hci_vnd_evt_cb) { + struct net_buf_simple_state state; + + net_buf_simple_save(&buf->b, &state); + + handled = (hci_vnd_evt_cb)(&buf->b); + + net_buf_simple_restore(&buf->b, &state); + } +#endif /* CONFIG_BT_HCI_VS_EVT_USER */ + + if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT) && !handled) { + /* do nothing at present time */ + BT_WARN("Unhandled vendor-specific event: %s", + bt_hex(buf->data, buf->len)); + } +} + +static const struct event_handler meta_events[] = { +#if defined(CONFIG_BT_OBSERVER) + EVENT_HANDLER(BT_HCI_EVT_LE_ADVERTISING_REPORT, le_adv_report, + sizeof(struct bt_hci_evt_le_advertising_report)), +#endif /* CONFIG_BT_OBSERVER */ +#if defined(CONFIG_BT_CONN) + EVENT_HANDLER(BT_HCI_EVT_LE_CONN_COMPLETE, le_legacy_conn_complete, + sizeof(struct bt_hci_evt_le_conn_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_ENH_CONN_COMPLETE, le_enh_conn_complete, + sizeof(struct bt_hci_evt_le_enh_conn_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE, + le_conn_update_complete, + sizeof(struct bt_hci_evt_le_conn_update_complete)), + EVENT_HANDLER(BT_HCI_EV_LE_REMOTE_FEAT_COMPLETE, + le_remote_feat_complete, + sizeof(struct bt_hci_evt_le_remote_feat_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_CONN_PARAM_REQ, le_conn_param_req, + sizeof(struct bt_hci_evt_le_conn_param_req)), +#if defined(CONFIG_BT_DATA_LEN_UPDATE) + EVENT_HANDLER(BT_HCI_EVT_LE_DATA_LEN_CHANGE, le_data_len_change, + sizeof(struct bt_hci_evt_le_data_len_change)), +#endif /* CONFIG_BT_DATA_LEN_UPDATE */ +#if defined(CONFIG_BT_PHY_UPDATE) + EVENT_HANDLER(BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE, + le_phy_update_complete, + sizeof(struct bt_hci_evt_le_phy_update_complete)), +#endif /* CONFIG_BT_PHY_UPDATE */ +#endif /* CONFIG_BT_CONN */ +#if defined(CONFIG_BT_SMP) + EVENT_HANDLER(BT_HCI_EVT_LE_LTK_REQUEST, le_ltk_request, + sizeof(struct bt_hci_evt_le_ltk_request)), +#endif /* CONFIG_BT_SMP */ +#if defined(CONFIG_BT_ECC) + EVENT_HANDLER(BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE, le_pkey_complete, + sizeof(struct bt_hci_evt_le_p256_public_key_complete)), + EVENT_HANDLER(BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE, le_dhkey_complete, + sizeof(struct bt_hci_evt_le_generate_dhkey_complete)), +#endif /* CONFIG_BT_SMP */ +}; + +static void hci_le_meta_event(struct net_buf *buf) +{ + struct bt_hci_evt_le_meta_event *evt; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + + BT_DBG("subevent 0x%02x", evt->subevent); + + handle_event(evt->subevent, buf, meta_events, ARRAY_SIZE(meta_events)); +} + +static const struct event_handler normal_events[] = { + EVENT_HANDLER(BT_HCI_EVT_VENDOR, hci_vendor_event, + sizeof(struct bt_hci_evt_vs)), + EVENT_HANDLER(BT_HCI_EVT_LE_META_EVENT, hci_le_meta_event, + sizeof(struct bt_hci_evt_le_meta_event)), +#if defined(CONFIG_BT_BREDR) + EVENT_HANDLER(BT_HCI_EVT_CONN_REQUEST, conn_req, + sizeof(struct bt_hci_evt_conn_request)), + EVENT_HANDLER(BT_HCI_EVT_CONN_COMPLETE, conn_complete, + sizeof(struct bt_hci_evt_conn_complete)), + EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, pin_code_req, + sizeof(struct bt_hci_evt_pin_code_req)), + EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, link_key_notify, + sizeof(struct bt_hci_evt_link_key_notify)), + EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, link_key_req, + sizeof(struct bt_hci_evt_link_key_req)), + EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, io_capa_resp, + sizeof(struct bt_hci_evt_io_capa_resp)), + EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, io_capa_req, + sizeof(struct bt_hci_evt_io_capa_req)), + EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, ssp_complete, + sizeof(struct bt_hci_evt_ssp_complete)), + EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, user_confirm_req, + sizeof(struct bt_hci_evt_user_confirm_req)), + EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY, user_passkey_notify, + sizeof(struct bt_hci_evt_user_passkey_notify)), + EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, user_passkey_req, + sizeof(struct bt_hci_evt_user_passkey_req)), + EVENT_HANDLER(BT_HCI_EVT_INQUIRY_COMPLETE, inquiry_complete, + sizeof(struct bt_hci_evt_inquiry_complete)), + EVENT_HANDLER(BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI, + inquiry_result_with_rssi, + sizeof(struct bt_hci_evt_inquiry_result_with_rssi)), + EVENT_HANDLER(BT_HCI_EVT_EXTENDED_INQUIRY_RESULT, + extended_inquiry_result, + sizeof(struct bt_hci_evt_extended_inquiry_result)), + EVENT_HANDLER(BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE, + remote_name_request_complete, + sizeof(struct bt_hci_evt_remote_name_req_complete)), + EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, auth_complete, + sizeof(struct bt_hci_evt_auth_complete)), + EVENT_HANDLER(BT_HCI_EVT_REMOTE_FEATURES, + read_remote_features_complete, + sizeof(struct bt_hci_evt_remote_features)), + EVENT_HANDLER(BT_HCI_EVT_REMOTE_EXT_FEATURES, + read_remote_ext_features_complete, + sizeof(struct bt_hci_evt_remote_ext_features)), + EVENT_HANDLER(BT_HCI_EVT_ROLE_CHANGE, role_change, + sizeof(struct bt_hci_evt_role_change)), + EVENT_HANDLER(BT_HCI_EVT_SYNC_CONN_COMPLETE, synchronous_conn_complete, + sizeof(struct bt_hci_evt_sync_conn_complete)), +#endif /* CONFIG_BT_BREDR */ +#if defined(CONFIG_BT_CONN) + EVENT_HANDLER(BT_HCI_EVT_DISCONN_COMPLETE, hci_disconn_complete, + sizeof(struct bt_hci_evt_disconn_complete)), +#endif /* CONFIG_BT_CONN */ +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) + EVENT_HANDLER(BT_HCI_EVT_ENCRYPT_CHANGE, hci_encrypt_change, + sizeof(struct bt_hci_evt_encrypt_change)), + EVENT_HANDLER(BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE, + hci_encrypt_key_refresh_complete, + sizeof(struct bt_hci_evt_encrypt_key_refresh_complete)), +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ +}; + +static void hci_event(struct net_buf *buf) +{ + struct bt_hci_evt_hdr *hdr; + + BT_ASSERT(buf->len >= sizeof(*hdr)); + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_DBG("event 0x%02x", hdr->evt); + BT_ASSERT(!bt_hci_evt_is_prio(hdr->evt)); + + handle_event(hdr->evt, buf, normal_events, ARRAY_SIZE(normal_events)); + + net_buf_unref(buf); +} + +static void send_cmd(void) +{ + struct net_buf *buf; + int err; + + /* Get next command */ + BT_DBG("calling net_buf_get"); + buf = net_buf_get(&bt_dev.cmd_tx_queue, K_NO_WAIT); + BT_ASSERT(buf); + + /* Wait until ncmd > 0 */ + BT_DBG("calling sem_take_wait"); + k_sem_take(&bt_dev.ncmd_sem, K_FOREVER); + + /* Clear out any existing sent command */ + if (bt_dev.sent_cmd) { + BT_ERR("Uncleared pending sent_cmd"); + net_buf_unref(bt_dev.sent_cmd); + bt_dev.sent_cmd = NULL; + } + + bt_dev.sent_cmd = net_buf_ref(buf); + + BT_DBG("Sending command 0x%04x (buf %p) to driver", + cmd(buf)->opcode, buf); + + err = bt_send(buf); + if (err) { + BT_ERR("Unable to send to driver (err %d)", err); + k_sem_give(&bt_dev.ncmd_sem); + hci_cmd_done(cmd(buf)->opcode, BT_HCI_ERR_UNSPECIFIED, + NULL); + net_buf_unref(bt_dev.sent_cmd); + bt_dev.sent_cmd = NULL; + net_buf_unref(buf); + } +} + +static void process_events(struct k_poll_event *ev, int count) +{ + BT_DBG("count %d", count); + + for (; count; ev++, count--) { + BT_DBG("ev->state %u", ev->state); + + switch (ev->state) { + case K_POLL_STATE_SIGNALED: + break; + case K_POLL_STATE_FIFO_DATA_AVAILABLE: + if (ev->tag == BT_EVENT_CMD_TX) { + send_cmd(); + } else if (IS_ENABLED(CONFIG_BT_CONN)) { + struct bt_conn *conn; + + if (ev->tag == BT_EVENT_CONN_TX_QUEUE) { + conn = CONTAINER_OF(ev->fifo, + struct bt_conn, + tx_queue); + bt_conn_process_tx(conn); + } + } + break; + case K_POLL_STATE_NOT_READY: + break; + default: + BT_WARN("Unexpected k_poll event state %u", ev->state); + break; + } + } +} + +#if defined(CONFIG_BT_CONN) +/* command FIFO + conn_change signal + MAX_CONN */ +#define EV_COUNT (2 + CONFIG_BT_MAX_CONN) +#else +/* command FIFO */ +#define EV_COUNT 1 +#endif + +#if defined(BFLB_BLE) +#if (BFLB_BLE_CO_THREAD) +void co_tx_thread() +{ + static struct k_poll_event events[EV_COUNT] = { + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &bt_dev.cmd_tx_queue, + BT_EVENT_CMD_TX), + }; + + if (k_sem_count_get(&g_poll_sem) > 0) { + int ev_count, err; + events[0].state = K_POLL_STATE_NOT_READY; + ev_count = 1; + + if (IS_ENABLED(CONFIG_BT_CONN)) { + ev_count += bt_conn_prepare_events(&events[1]); + } + + err = k_poll(events, ev_count, K_NO_WAIT); + process_events(events, ev_count); + } +} +#endif + +static void hci_tx_thread(void *p1) +#else +static void hci_tx_thread(void *p1, void *p2, void *p3) +#endif +{ + static struct k_poll_event events[EV_COUNT] = { + K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, + &bt_dev.cmd_tx_queue, + BT_EVENT_CMD_TX), + }; + + BT_DBG("Started"); + + while (1) { + int ev_count, err; + + events[0].state = K_POLL_STATE_NOT_READY; + ev_count = 1; + + if (IS_ENABLED(CONFIG_BT_CONN)) { + ev_count += bt_conn_prepare_events(&events[1]); + } + + BT_DBG("Calling k_poll with %d events", ev_count); + + err = k_poll(events, ev_count, K_FOREVER); + BT_ASSERT(err == 0); + + process_events(events, ev_count); + + /* Make sure we don't hog the CPU if there's all the time + * some ready events. + */ + k_yield(); + } +} + + +static void read_local_ver_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_local_version_info *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + bt_dev.hci_version = rp->hci_version; + bt_dev.hci_revision = sys_le16_to_cpu(rp->hci_revision); + bt_dev.lmp_version = rp->lmp_version; + bt_dev.lmp_subversion = sys_le16_to_cpu(rp->lmp_subversion); + bt_dev.manufacturer = sys_le16_to_cpu(rp->manufacturer); +} + +static void read_bdaddr_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_bd_addr *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + if (!bt_addr_cmp(&rp->bdaddr, BT_ADDR_ANY) || + !bt_addr_cmp(&rp->bdaddr, BT_ADDR_NONE)) { + BT_DBG("Controller has no public address"); + return; + } + + bt_addr_copy(&bt_dev.id_addr[0].a, &rp->bdaddr); + bt_dev.id_addr[0].type = BT_ADDR_LE_PUBLIC; + bt_dev.id_count = 1U; +} + +static void read_le_features_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_local_features *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + memcpy(bt_dev.le.features, rp->features, sizeof(bt_dev.le.features)); +} + +#if defined(CONFIG_BT_BREDR) +static void read_buffer_size_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data; + u16_t pkts; + + BT_DBG("status 0x%02x", rp->status); + + bt_dev.br.mtu = sys_le16_to_cpu(rp->acl_max_len); + pkts = sys_le16_to_cpu(rp->acl_max_num); + + BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.br.mtu); + + k_sem_init(&bt_dev.br.pkts, pkts, pkts); +} +#elif defined(CONFIG_BT_CONN) +static void read_buffer_size_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_buffer_size *rp = (void *)buf->data; + u16_t pkts; + + BT_DBG("status 0x%02x", rp->status); + + /* If LE-side has buffers we can ignore the BR/EDR values */ + if (bt_dev.le.mtu) { + return; + } + + bt_dev.le.mtu = sys_le16_to_cpu(rp->acl_max_len); + pkts = sys_le16_to_cpu(rp->acl_max_num); + + BT_DBG("ACL BR/EDR buffers: pkts %u mtu %u", pkts, bt_dev.le.mtu); + + k_sem_init(&bt_dev.le.pkts, pkts, pkts); +} +#endif + +#if defined(CONFIG_BT_CONN) +static void le_read_buffer_size_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_buffer_size *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + bt_dev.le.mtu = sys_le16_to_cpu(rp->le_max_len); + if (!bt_dev.le.mtu) { + return; + } + + BT_DBG("ACL LE buffers: pkts %u mtu %u", rp->le_max_num, bt_dev.le.mtu); + + k_sem_init(&bt_dev.le.pkts, rp->le_max_num, rp->le_max_num); +} +#endif + +static void read_supported_commands_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_supported_commands *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + memcpy(bt_dev.supported_commands, rp->commands, + sizeof(bt_dev.supported_commands)); + + /* + * Report "LE Read Local P-256 Public Key" and "LE Generate DH Key" as + * supported if TinyCrypt ECC is used for emulation. + */ + if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) { + bt_dev.supported_commands[34] |= 0x02; + bt_dev.supported_commands[34] |= 0x04; + } +} + +static void read_local_features_complete(struct net_buf *buf) +{ + struct bt_hci_rp_read_local_features *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + memcpy(bt_dev.features[0], rp->features, sizeof(bt_dev.features[0])); +} + +static void le_read_supp_states_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_supp_states *rp = (void *)buf->data; + + BT_DBG("status 0x%02x", rp->status); + + bt_dev.le.states = sys_get_le64(rp->le_states); +} + +#if defined(CONFIG_BT_SMP) +static void le_read_resolving_list_size_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_rl_size *rp = (void *)buf->data; + + BT_DBG("Resolving List size %u", rp->rl_size); + + bt_dev.le.rl_size = rp->rl_size; +} +#endif /* defined(CONFIG_BT_SMP) */ + +#if defined(CONFIG_BT_WHITELIST) +static void le_read_wl_size_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_wl_size *rp = + (struct bt_hci_rp_le_read_wl_size *)buf->data; + + BT_DBG("Whitelist size %u", rp->wl_size); + + bt_dev.le.wl_size = rp->wl_size; +} +#endif + +static int common_init(void) +{ + struct net_buf *rsp; + int err; + + if (!(bt_dev.drv->quirks & BT_QUIRK_NO_RESET)) { + /* Send HCI_RESET */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp); + if (err) { + return err; + } + hci_reset_complete(rsp); + net_buf_unref(rsp); + } + + /* Read Local Supported Features */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp); + if (err) { + return err; + } + read_local_features_complete(rsp); + net_buf_unref(rsp); + + /* Read Local Version Information */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL, + &rsp); + if (err) { + return err; + } + read_local_ver_complete(rsp); + net_buf_unref(rsp); + + /* Read Bluetooth Address */ + if (!atomic_test_bit(bt_dev.flags, BT_DEV_USER_ID_ADDR)) { + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp); + if (err) { + return err; + } + read_bdaddr_complete(rsp); + net_buf_unref(rsp); + } + + /* Read Local Supported Commands */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_SUPPORTED_COMMANDS, NULL, + &rsp); + if (err) { + return err; + } + read_supported_commands_complete(rsp); + net_buf_unref(rsp); + + if (IS_ENABLED(CONFIG_BT_HOST_CRYPTO)) { + /* Initialize the PRNG so that it is safe to use it later + * on in the initialization process. + */ + err = prng_init(); + if (err) { + return err; + } + } + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + err = set_flow_control(); + if (err) { + return err; + } +#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */ + + return 0; +} + +static int le_set_event_mask(void) +{ + struct bt_hci_cp_le_set_event_mask *cp_mask; + struct net_buf *buf; + u64_t mask = 0U; + + /* Set LE event mask */ + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EVENT_MASK, sizeof(*cp_mask)); + if (!buf) { + return -ENOBUFS; + } + + cp_mask = net_buf_add(buf, sizeof(*cp_mask)); + + mask |= BT_EVT_MASK_LE_ADVERTISING_REPORT; + + if (IS_ENABLED(CONFIG_BT_CONN)) { + if (IS_ENABLED(CONFIG_BT_SMP) && + BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_ENH_CONN_COMPLETE; + } else { + mask |= BT_EVT_MASK_LE_CONN_COMPLETE; + } + + mask |= BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE; + mask |= BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE; + + if (BT_FEAT_LE_CONN_PARAM_REQ_PROC(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_CONN_PARAM_REQ; + } + + if (IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_DATA_LEN_CHANGE; + } + + if (IS_ENABLED(CONFIG_BT_PHY_UPDATE) && + (BT_FEAT_LE_PHY_2M(bt_dev.le.features) || + BT_FEAT_LE_PHY_CODED(bt_dev.le.features))) { + mask |= BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE; + } + } + + if (IS_ENABLED(CONFIG_BT_SMP) && + BT_FEAT_LE_ENCR(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_LTK_REQUEST; + } + + /* + * If "LE Read Local P-256 Public Key" and "LE Generate DH Key" are + * supported we need to enable events generated by those commands. + */ + if (IS_ENABLED(CONFIG_BT_ECC) && + (BT_CMD_TEST(bt_dev.supported_commands, 34, 1)) && + (BT_CMD_TEST(bt_dev.supported_commands, 34, 2))) { + mask |= BT_EVT_MASK_LE_P256_PUBLIC_KEY_COMPLETE; + mask |= BT_EVT_MASK_LE_GENERATE_DHKEY_COMPLETE; + } + + sys_put_le64(mask, cp_mask->events); + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL); +} + +static int le_init(void) +{ + struct bt_hci_cp_write_le_host_supp *cp_le; + struct net_buf *buf, *rsp; + int err; + + /* For now we only support LE capable controllers */ + if (!BT_FEAT_LE(bt_dev.features)) { + BT_ERR("Non-LE capable controller detected!"); + return -ENODEV; + } + + /* Read Low Energy Supported Features */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, + &rsp); + if (err) { + return err; + } + read_le_features_complete(rsp); + net_buf_unref(rsp); + +#if defined(CONFIG_BT_CONN) + /* Read LE Buffer Size */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, + NULL, &rsp); + if (err) { + return err; + } + le_read_buffer_size_complete(rsp); + net_buf_unref(rsp); +#endif + + if (BT_FEAT_BREDR(bt_dev.features)) { + buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, + sizeof(*cp_le)); + if (!buf) { + return -ENOBUFS; + } + + cp_le = net_buf_add(buf, sizeof(*cp_le)); + + /* Explicitly enable LE for dual-mode controllers */ + cp_le->le = 0x01; + cp_le->simul = 0x00; + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf, + NULL); + if (err) { + return err; + } + } + + /* Read LE Supported States */ + if (BT_CMD_LE_STATES(bt_dev.supported_commands)) { + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_SUPP_STATES, NULL, + &rsp); + if (err) { + return err; + } + le_read_supp_states_complete(rsp); + net_buf_unref(rsp); + } + + if (IS_ENABLED(CONFIG_BT_CONN) && + IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) && + BT_FEAT_LE_DLE(bt_dev.le.features)) { + struct bt_hci_cp_le_write_default_data_len *cp; + struct bt_hci_rp_le_read_max_data_len *rp; + u16_t tx_octets, tx_time; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MAX_DATA_LEN, NULL, + &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + tx_octets = sys_le16_to_cpu(rp->max_tx_octets); + tx_time = sys_le16_to_cpu(rp->max_tx_time); + net_buf_unref(rsp); + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->max_tx_octets = sys_cpu_to_le16(tx_octets); + cp->max_tx_time = sys_cpu_to_le16(tx_time); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN, + buf, NULL); + if (err) { + return err; + } + } + +#if defined(CONFIG_BT_SMP) + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { +#if defined(CONFIG_BT_PRIVACY) + struct bt_hci_cp_le_set_rpa_timeout *cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_RPA_TIMEOUT, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->rpa_timeout = sys_cpu_to_le16(CONFIG_BT_RPA_TIMEOUT); + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_RPA_TIMEOUT, buf, + NULL); + if (err) { + return err; + } +#endif /* defined(CONFIG_BT_PRIVACY) */ + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_RL_SIZE, NULL, + &rsp); + if (err) { + return err; + } + le_read_resolving_list_size_complete(rsp); + net_buf_unref(rsp); + } +#endif + +#if defined(CONFIG_BT_WHITELIST) + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_WL_SIZE, NULL, + &rsp); + if (err) { + return err; + } + + le_read_wl_size_complete(rsp); + net_buf_unref(rsp); +#endif /* defined(CONFIG_BT_WHITELIST) */ + + return le_set_event_mask(); +} + +#if defined(CONFIG_BT_BREDR) +static int read_ext_features(void) +{ + int i; + + /* Read Local Supported Extended Features */ + for (i = 1; i < LMP_FEAT_PAGES_COUNT; i++) { + struct bt_hci_cp_read_local_ext_features *cp; + struct bt_hci_rp_read_local_ext_features *rp; + struct net_buf *buf, *rsp; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_LOCAL_EXT_FEATURES, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->page = i; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_EXT_FEATURES, + buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + + memcpy(&bt_dev.features[i], rp->ext_features, + sizeof(bt_dev.features[i])); + + if (rp->max_page <= i) { + net_buf_unref(rsp); + break; + } + + net_buf_unref(rsp); + } + + return 0; +} + +void device_supported_pkt_type(void) +{ + /* Device supported features and sco packet types */ + if (BT_FEAT_HV2_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV2); + } + + if (BT_FEAT_HV3_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_HV3); + } + + if (BT_FEAT_LMP_ESCO_CAPABLE(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV3); + } + + if (BT_FEAT_EV4_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV4); + } + + if (BT_FEAT_EV5_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_EV5); + } + + if (BT_FEAT_2EV3_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV3); + } + + if (BT_FEAT_3EV3_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_3EV3); + } + + if (BT_FEAT_3SLOT_PKT(bt_dev.features)) { + bt_dev.br.esco_pkt_type |= (HCI_PKT_TYPE_ESCO_2EV5 | + HCI_PKT_TYPE_ESCO_3EV5); + } +} + +static int br_init(void) +{ + struct net_buf *buf; + struct bt_hci_cp_write_ssp_mode *ssp_cp; + struct bt_hci_cp_write_inquiry_mode *inq_cp; + struct bt_hci_write_local_name *name_cp; + int err; + + /* Read extended local features */ + if (BT_FEAT_EXT_FEATURES(bt_dev.features)) { + err = read_ext_features(); + if (err) { + return err; + } + } + + /* Add local supported packet types to bt_dev */ + device_supported_pkt_type(); + + /* Get BR/EDR buffer size */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &buf); + if (err) { + return err; + } + + read_buffer_size_complete(buf); + net_buf_unref(buf); + + /* Set SSP mode */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SSP_MODE, sizeof(*ssp_cp)); + if (!buf) { + return -ENOBUFS; + } + + ssp_cp = net_buf_add(buf, sizeof(*ssp_cp)); + ssp_cp->mode = 0x01; + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SSP_MODE, buf, NULL); + if (err) { + return err; + } + + /* Enable Inquiry results with RSSI or extended Inquiry */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_INQUIRY_MODE, sizeof(*inq_cp)); + if (!buf) { + return -ENOBUFS; + } + + inq_cp = net_buf_add(buf, sizeof(*inq_cp)); + inq_cp->mode = 0x02; + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_INQUIRY_MODE, buf, NULL); + if (err) { + return err; + } + + /* Set local name */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp)); + if (!buf) { + return -ENOBUFS; + } + + name_cp = net_buf_add(buf, sizeof(*name_cp)); + strncpy((char *)name_cp->local_name, CONFIG_BT_DEVICE_NAME, + sizeof(name_cp->local_name)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL); + if (err) { + return err; + } + + /* Set page timeout*/ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_PAGE_TIMEOUT, sizeof(u16_t)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_le16(buf, CONFIG_BT_PAGE_TIMEOUT); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_PAGE_TIMEOUT, buf, NULL); + if (err) { + return err; + } + + /* Enable BR/EDR SC if supported */ + if (BT_FEAT_SC(bt_dev.features)) { + struct bt_hci_cp_write_sc_host_supp *sc_cp; + + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP, + sizeof(*sc_cp)); + if (!buf) { + return -ENOBUFS; + } + + sc_cp = net_buf_add(buf, sizeof(*sc_cp)); + sc_cp->sc_support = 0x01; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf, + NULL); + if (err) { + return err; + } + } + + return 0; +} +#else +static int br_init(void) +{ +#if defined(CONFIG_BT_CONN) + struct net_buf *rsp; + int err; + + if (bt_dev.le.mtu) { + return 0; + } + + /* Use BR/EDR buffer size if LE reports zero buffers */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &rsp); + if (err) { + return err; + } + + read_buffer_size_complete(rsp); + net_buf_unref(rsp); +#endif /* CONFIG_BT_CONN */ + + return 0; +} +#endif + +static int set_event_mask(void) +{ + struct bt_hci_cp_set_event_mask *ev; + struct net_buf *buf; + u64_t mask = 0U; + + buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev)); + if (!buf) { + return -ENOBUFS; + } + + ev = net_buf_add(buf, sizeof(*ev)); + + if (IS_ENABLED(CONFIG_BT_BREDR)) { + /* Since we require LE support, we can count on a + * Bluetooth 4.0 feature set + */ + mask |= BT_EVT_MASK_INQUIRY_COMPLETE; + mask |= BT_EVT_MASK_CONN_COMPLETE; + mask |= BT_EVT_MASK_CONN_REQUEST; + mask |= BT_EVT_MASK_AUTH_COMPLETE; + mask |= BT_EVT_MASK_REMOTE_NAME_REQ_COMPLETE; + mask |= BT_EVT_MASK_REMOTE_FEATURES; + mask |= BT_EVT_MASK_ROLE_CHANGE; + mask |= BT_EVT_MASK_PIN_CODE_REQ; + mask |= BT_EVT_MASK_LINK_KEY_REQ; + mask |= BT_EVT_MASK_LINK_KEY_NOTIFY; + mask |= BT_EVT_MASK_INQUIRY_RESULT_WITH_RSSI; + mask |= BT_EVT_MASK_REMOTE_EXT_FEATURES; + mask |= BT_EVT_MASK_SYNC_CONN_COMPLETE; + mask |= BT_EVT_MASK_EXTENDED_INQUIRY_RESULT; + mask |= BT_EVT_MASK_IO_CAPA_REQ; + mask |= BT_EVT_MASK_IO_CAPA_RESP; + mask |= BT_EVT_MASK_USER_CONFIRM_REQ; + mask |= BT_EVT_MASK_USER_PASSKEY_REQ; + mask |= BT_EVT_MASK_SSP_COMPLETE; + mask |= BT_EVT_MASK_USER_PASSKEY_NOTIFY; + } + + mask |= BT_EVT_MASK_HARDWARE_ERROR; + mask |= BT_EVT_MASK_DATA_BUFFER_OVERFLOW; + mask |= BT_EVT_MASK_LE_META_EVENT; + + if (IS_ENABLED(CONFIG_BT_CONN)) { + mask |= BT_EVT_MASK_DISCONN_COMPLETE; + mask |= BT_EVT_MASK_REMOTE_VERSION_INFO; + } + + if (IS_ENABLED(CONFIG_BT_SMP) && + BT_FEAT_LE_ENCR(bt_dev.le.features)) { + mask |= BT_EVT_MASK_ENCRYPT_CHANGE; + mask |= BT_EVT_MASK_ENCRYPT_KEY_REFRESH_COMPLETE; + } + + sys_put_le64(mask, ev->events); + return bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL); +} + +static inline int create_random_addr(bt_addr_le_t *addr) +{ + addr->type = BT_ADDR_LE_RANDOM; + + return bt_rand(addr->a.val, 6); +} + +int bt_addr_le_create_nrpa(bt_addr_le_t *addr) +{ + int err; + + err = create_random_addr(addr); + if (err) { + return err; + } + + BT_ADDR_SET_NRPA(&addr->a); + + return 0; +} + +int bt_addr_le_create_static(bt_addr_le_t *addr) +{ + int err; + + err = create_random_addr(addr); + if (err) { + return err; + } + + BT_ADDR_SET_STATIC(&addr->a); + + return 0; +} + +#if defined(CONFIG_BT_DEBUG) +#if 0 +static const char *ver_str(u8_t ver) +{ + const char * const str[] = { + "1.0b", "1.1", "1.2", "2.0", "2.1", "3.0", "4.0", "4.1", "4.2", + "5.0", "5.1", + }; + + if (ver < ARRAY_SIZE(str)) { + return str[ver]; + } + + return "unknown"; +} +#endif + +static void bt_dev_show_info(void) +{ +#if 0 + int i; + + BT_INFO("Identity%s: %s", bt_dev.id_count > 1 ? "[0]" : "", + bt_addr_le_str(&bt_dev.id_addr[0])); + + for (i = 1; i < bt_dev.id_count; i++) { + BT_INFO("Identity[%d]: %s", + i, bt_addr_le_str(&bt_dev.id_addr[i])); + } + + BT_INFO("HCI: version %s (0x%02x) revision 0x%04x, manufacturer 0x%04x", + ver_str(bt_dev.hci_version), bt_dev.hci_version, + bt_dev.hci_revision, bt_dev.manufacturer); + BT_INFO("LMP: version %s (0x%02x) subver 0x%04x", + ver_str(bt_dev.lmp_version), bt_dev.lmp_version, + bt_dev.lmp_subversion); +#endif +} +#else +static inline void bt_dev_show_info(void) +{ +} +#endif /* CONFIG_BT_DEBUG */ + +#if defined(CONFIG_BT_HCI_VS_EXT) +#if defined(CONFIG_BT_DEBUG) +static const char *vs_hw_platform(u16_t platform) +{ + static const char * const plat_str[] = { + "reserved", "Intel Corporation", "Nordic Semiconductor", + "NXP Semiconductors" }; + + if (platform < ARRAY_SIZE(plat_str)) { + return plat_str[platform]; + } + + return "unknown"; +} + +static const char *vs_hw_variant(u16_t platform, u16_t variant) +{ + static const char * const nordic_str[] = { + "reserved", "nRF51x", "nRF52x", "nRF53x" + }; + + if (platform != BT_HCI_VS_HW_PLAT_NORDIC) { + return "unknown"; + } + + if (variant < ARRAY_SIZE(nordic_str)) { + return nordic_str[variant]; + } + + return "unknown"; +} + +static const char *vs_fw_variant(u8_t variant) +{ + static const char * const var_str[] = { + "Standard Bluetooth controller", + "Vendor specific controller", + "Firmware loader", + "Rescue image", + }; + + if (variant < ARRAY_SIZE(var_str)) { + return var_str[variant]; + } + + return "unknown"; +} +#endif /* CONFIG_BT_DEBUG */ + +static void hci_vs_init(void) +{ + union { + struct bt_hci_rp_vs_read_version_info *info; + struct bt_hci_rp_vs_read_supported_commands *cmds; + struct bt_hci_rp_vs_read_supported_features *feat; + } rp; + struct net_buf *rsp; + int err; + + /* If heuristics is enabled, try to guess HCI VS support by looking + * at the HCI version and identity address. We haven't tried to set + * a static random address yet at this point, so the identity will + * either be zeroes or a valid public address. + */ + if (IS_ENABLED(CONFIG_BT_HCI_VS_EXT_DETECT) && + (bt_dev.hci_version < BT_HCI_VERSION_5_0 || + (!atomic_test_bit(bt_dev.flags, BT_DEV_USER_ID_ADDR) && + bt_addr_le_cmp(&bt_dev.id_addr[0], BT_ADDR_LE_ANY)))) { + BT_WARN("Controller doesn't seem to support Zephyr vendor HCI"); + return; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_VERSION_INFO, NULL, &rsp); + if (err) { + BT_WARN("Vendor HCI extensions not available"); + return; + } + +#if defined(CONFIG_BT_DEBUG) + rp.info = (void *)rsp->data; + BT_INFO("HW Platform: %s (0x%04x)", + vs_hw_platform(sys_le16_to_cpu(rp.info->hw_platform)), + sys_le16_to_cpu(rp.info->hw_platform)); + BT_INFO("HW Variant: %s (0x%04x)", + vs_hw_variant(sys_le16_to_cpu(rp.info->hw_platform), + sys_le16_to_cpu(rp.info->hw_variant)), + sys_le16_to_cpu(rp.info->hw_variant)); + BT_INFO("Firmware: %s (0x%02x) Version %u.%u Build %u", + vs_fw_variant(rp.info->fw_variant), rp.info->fw_variant, + rp.info->fw_version, sys_le16_to_cpu(rp.info->fw_revision), + sys_le32_to_cpu(rp.info->fw_build)); +#endif /* CONFIG_BT_DEBUG */ + + net_buf_unref(rsp); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_SUPPORTED_COMMANDS, + NULL, &rsp); + if (err) { + BT_WARN("Failed to read supported vendor features"); + return; + } + + rp.cmds = (void *)rsp->data; + memcpy(bt_dev.vs_commands, rp.cmds->commands, BT_DEV_VS_CMDS_MAX); + net_buf_unref(rsp); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_SUPPORTED_FEATURES, + NULL, &rsp); + if (err) { + BT_WARN("Failed to read supported vendor commands"); + return; + } + + rp.feat = (void *)rsp->data; + memcpy(bt_dev.vs_features, rp.feat->features, BT_DEV_VS_FEAT_MAX); + net_buf_unref(rsp); +} +#endif /* CONFIG_BT_HCI_VS_EXT */ + +static int host_hci_init(void) +{ + int err; + + err = common_init(); + if (err) { + return err; + } + + err = le_init(); + if (err) { + return err; + } + + if (BT_FEAT_BREDR(bt_dev.features)) { + err = br_init(); + if (err) { + return err; + } + } else if (IS_ENABLED(CONFIG_BT_BREDR)) { + BT_ERR("Non-BR/EDR controller detected"); + return -EIO; + } + + err = set_event_mask(); + if (err) { + return err; + } + +#if defined(CONFIG_BT_HCI_VS_EXT) + hci_vs_init(); +#endif + + if (!IS_ENABLED(CONFIG_BT_SETTINGS) && !bt_dev.id_count) { + BT_DBG("No public address. Trying to set static random."); + err = bt_setup_id_addr(); + if (err) { + BT_ERR("Unable to set identity address"); + return err; + } + } + + return 0; +} + +int bt_send(struct net_buf *buf) +{ + BT_DBG("buf %p len %u type %u", buf, buf->len, bt_buf_get_type(buf)); + + bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); + + if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) { + return bt_hci_ecc_send(buf); + } + + return bt_dev.drv->send(buf); +} + +int bt_recv(struct net_buf *buf) +{ + bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); + + BT_DBG("buf %p len %u", buf, buf->len); + + switch (bt_buf_get_type(buf)) { +#if defined(CONFIG_BT_CONN) + case BT_BUF_ACL_IN: +#if defined(CONFIG_BT_RECV_IS_RX_THREAD) + hci_acl(buf); +#else + net_buf_put(&bt_dev.rx_queue, buf); +#endif + return 0; +#endif /* BT_CONN */ + case BT_BUF_EVT: +#if defined(CONFIG_BT_RECV_IS_RX_THREAD) + hci_event(buf); +#else + net_buf_put(&bt_dev.rx_queue, buf); +#endif + return 0; + default: + BT_ERR("Invalid buf type %u", bt_buf_get_type(buf)); + net_buf_unref(buf); + return -EINVAL; + } +} + +static const struct event_handler prio_events[] = { + EVENT_HANDLER(BT_HCI_EVT_CMD_COMPLETE, hci_cmd_complete, + sizeof(struct bt_hci_evt_cmd_complete)), + EVENT_HANDLER(BT_HCI_EVT_CMD_STATUS, hci_cmd_status, + sizeof(struct bt_hci_evt_cmd_status)), +#if defined(CONFIG_BT_CONN) + EVENT_HANDLER(BT_HCI_EVT_DATA_BUF_OVERFLOW, + hci_data_buf_overflow, + sizeof(struct bt_hci_evt_data_buf_overflow)), + EVENT_HANDLER(BT_HCI_EVT_NUM_COMPLETED_PACKETS, + hci_num_completed_packets, + sizeof(struct bt_hci_evt_num_completed_packets)), +#endif /* CONFIG_BT_CONN */ +}; + +int bt_recv_prio(struct net_buf *buf) +{ + struct bt_hci_evt_hdr *hdr; + + bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len); + + BT_ASSERT(bt_buf_get_type(buf) == BT_BUF_EVT); + BT_ASSERT(buf->len >= sizeof(*hdr)); + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_ASSERT(bt_hci_evt_is_prio(hdr->evt)); + + handle_event(hdr->evt, buf, prio_events, ARRAY_SIZE(prio_events)); + + net_buf_unref(buf); + + return 0; +} + +int bt_hci_driver_register(const struct bt_hci_driver *drv) +{ + if (bt_dev.drv) { + return -EALREADY; + } + + if (!drv->open || !drv->send) { + return -EINVAL; + } + + bt_dev.drv = drv; + + BT_DBG("Registered %s", drv->name ? drv->name : ""); + + bt_monitor_new_index(BT_MONITOR_TYPE_PRIMARY, drv->bus, + BT_ADDR_ANY, drv->name ? drv->name : "bt0"); + + return 0; +} + +#if defined(CONFIG_BT_PRIVACY) +static int irk_init(void) +{ + #if (BFLB_FIXED_IRK) + //use fixed irk + memset(&bt_dev.irk[0], 0x11, 16); + return 0; + #endif +#if defined(BFLB_BLE_PATCH_SETTINGS_LOAD) + u8_t empty_irk[16]; + int err; + /*local irk has been loaded from flash in bt_enable, check if irk is null*/ + memset(empty_irk, 0, 16); + if (memcmp(bt_dev.irk[0], empty_irk, 16) != 0) + return 0; + + err = bt_rand(&bt_dev.irk[0], 16); + + return err; +#else + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + BT_DBG("Expecting settings to handle local IRK"); + } else { + int err; + + err = bt_rand(&bt_dev.irk[0], 16); + if (err) { + return err; + } + + BT_WARN("Using temporary IRK"); + } + + return 0; +#endif +} +#endif /* CONFIG_BT_PRIVACY */ +void bt_finalize_init(void) +{ + atomic_set_bit(bt_dev.flags, BT_DEV_READY); + + if (IS_ENABLED(CONFIG_BT_OBSERVER)) { + bt_le_scan_update(false); + } + + bt_dev_show_info(); +} + +#if defined(BFLB_HOST_ASSISTANT) +extern void blhast_init(struct blhast_cb *cb); +#endif +static int bt_init(void) +{ + int err; + #if defined(CONFIG_BT_STACK_PTS) + u8_t dbg_irk[16]; + #endif +/*Make sure that freertos is running when set info into flash, because Semaphore is used in ef_set_env*/ +#if defined(BFLB_BLE_PATCH_SETTINGS_LOAD) + char empty_name[CONFIG_BT_DEVICE_NAME_MAX]; + memset(empty_name, 0, CONFIG_BT_DEVICE_NAME_MAX); + + if(!memcmp(bt_dev.name, empty_name, CONFIG_BT_DEVICE_NAME_MAX)) + bt_set_name(CONFIG_BT_DEVICE_NAME); +#endif + +#if defined(BFLB_BLE) + err = bl_onchiphci_interface_init(); + if (err) { + return err; + } +#if defined(BFLB_HOST_ASSISTANT) + blhast_init(host_assist_cb); +#endif +#endif + + err = host_hci_init(); + if (err) { + return err; + } + if (IS_ENABLED(CONFIG_BT_CONN)) { + err = bt_conn_init(); + if (err) { + return err; + } + } + +#if defined(CONFIG_BT_PRIVACY) + err = irk_init(); + if (err) { + return err; + } +#if defined(CONFIG_BT_STACK_PTS) + reverse_bytearray(bt_dev.irk[0], dbg_irk, sizeof(dbg_irk)); + BT_PTS("Local IRK %s public identity bdaddr %s", + bt_hex(dbg_irk, 16), bt_addr_str(&(bt_dev.id_addr[0].a))); +#endif + + k_delayed_work_init(&bt_dev.rpa_update, rpa_timeout); +#endif + + +#if defined(CONFIG_BT_SMP) +#if defined(BFLB_BLE_PATCH_SETTINGS_LOAD) + #if defined(CFG_SLEEP) + if(HBN_Get_Status_Flag() == 0) + #endif + { + if(!bt_keys_load()) + keys_commit(); + } +#endif +#endif //CONFIG_BT_SMP + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + if (!bt_dev.id_count) { + BT_INFO("No ID address. App must call settings_load()"); + return 0; + } + + atomic_set_bit(bt_dev.flags, BT_DEV_PRESET_ID); + } + + bt_finalize_init(); + return 0; +} + +static void init_work(struct k_work *work) +{ + int err; + + err = bt_init(); + if (ready_cb) { + ready_cb(err); + } +} + +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) +static void hci_rx_thread(void) +{ + struct net_buf *buf; + + BT_DBG("started"); + + while (1) { + BT_DBG("calling fifo_get_wait"); + buf = net_buf_get(&bt_dev.rx_queue, K_FOREVER); + + BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), + buf->len); + + switch (bt_buf_get_type(buf)) { +#if defined(CONFIG_BT_CONN) + case BT_BUF_ACL_IN: + hci_acl(buf); + break; +#endif /* CONFIG_BT_CONN */ + case BT_BUF_EVT: + hci_event(buf); + break; + default: + BT_ERR("Unknown buf type %u", bt_buf_get_type(buf)); + net_buf_unref(buf); + break; + } + + /* Make sure we don't hog the CPU if the rx_queue never + * gets empty. + */ + k_yield(); + } +} +#endif /* !CONFIG_BT_RECV_IS_RX_THREAD */ + +#if defined(BFLB_DISABLE_BT) +bool queue_inited = false; +#endif + +int bt_enable(bt_ready_cb_t cb) +{ + int err; + + if (!bt_dev.drv) { + BT_ERR("No HCI driver registered"); + return -ENODEV; + } + + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_ENABLE)) { + return -EALREADY; + } + +#if defined(BFLB_BLE) +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + net_buf_init(&hci_cmd_pool, CONFIG_BT_HCI_CMD_COUNT, CMD_BUF_SIZE, NULL); + net_buf_init(&hci_rx_pool, CONFIG_BT_RX_BUF_COUNT, BT_BUF_RX_SIZE, NULL); + #if defined(CONFIG_BT_CONN) + net_buf_init(&num_complete_pool, 1, BT_BUF_RX_SIZE, NULL); + #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + net_buf_init(&acl_in_pool, CONFIG_BT_ACL_RX_COUNT, ACL_IN_SIZE, report_completed_packet); + #endif//CONFIG_BT_HCI_ACL_FLOW_CONTROL + #endif//CONFIG_BT_CONN + #if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) + net_buf_init(&discardable_pool, CONFIG_BT_DISCARDABLE_BUF_COUNT, BT_BUF_RX_SIZE, NULL); + #endif +#endif + + k_work_init(&bt_dev.init, init_work); + k_work_q_start(); +#if !defined(CONFIG_BT_WAIT_NOP) + k_sem_init(&bt_dev.ncmd_sem, 1, 1); +#else + k_sem_init(&bt_dev.ncmd_sem, 0, 1); +#endif + k_fifo_init(&bt_dev.cmd_tx_queue, 20); +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + k_fifo_init(&bt_dev.rx_queue, 20); +#endif + if(queue_inited == false) + { + k_lifo_init(&hci_cmd_pool.free, CONFIG_BT_HCI_CMD_COUNT); + k_lifo_init(&hci_rx_pool.free, CONFIG_BT_RX_BUF_COUNT); + } + + k_sem_init(&g_poll_sem, 0, 1); +#endif + +#if defined(BFLB_BLE_PATCH_SETTINGS_LOAD) + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + #if defined(CFG_SLEEP) + /* When using eflash_loader upprade firmware and softreset, + * HBN_Get_Status_Flag() is 0x594c440b. so comment this line. */ + //if( HBN_Get_Status_Flag() == 0) + #endif + bt_local_info_load(); + } +#else + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + err = bt_settings_init(); + if (err) { + return err; + } + } else { + bt_set_name(CONFIG_BT_DEVICE_NAME); + } +#endif + + ready_cb = cb; + + /* TX thread */ +#if defined(BFLB_BLE) +#if (!BFLB_BLE_CO_THREAD) +k_thread_create(&tx_thread_data, "hci_tx_thread", + CONFIG_BT_HCI_TX_STACK_SIZE, + hci_tx_thread, + CONFIG_BT_HCI_TX_PRIO); +#endif +#else + k_thread_create(&tx_thread_data, tx_thread_stack, + K_THREAD_STACK_SIZEOF(tx_thread_stack), + hci_tx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_HCI_TX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(&tx_thread_data, "BT TX"); +#endif + +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + /* RX thread */ +#if defined(BFLB_BLE) + k_thread_create(&rx_thread_data, "hci_rx_thread", + CONFIG_BT_HCI_RX_STACK_SIZE/*K_THREAD_STACK_SIZEOF(rx_thread_stack)*/, + (k_thread_entry_t)hci_rx_thread, + CONFIG_BT_RX_PRIO); +#else + k_thread_create(&rx_thread_data, rx_thread_stack, + K_THREAD_STACK_SIZEOF(rx_thread_stack), + (k_thread_entry_t)hci_rx_thread, NULL, NULL, NULL, + K_PRIO_COOP(CONFIG_BT_RX_PRIO), + 0, K_NO_WAIT); + k_thread_name_set(&rx_thread_data, "BT RX"); +#endif //BFLB_BLE +#endif + + if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) { + bt_hci_ecc_init(); + } + + err = bt_dev.drv->open(); + if (err) { + BT_ERR("HCI driver open failed (%d)", err); + return err; + } + +#if !defined(BFLB_BLE) + if (!cb) { + return bt_init(); + } +#endif + + #if defined(CONFIG_BLE_MULTI_ADV) + bt_le_multi_adv_thread_init(); + #endif + + k_work_submit(&bt_dev.init); + return 0; +} + +struct bt_ad { + const struct bt_data *data; + size_t len; +}; + +#if defined(BFLB_BLE) +bool le_check_valid_scan(void) +{ + return atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); +} +#endif + + +#if defined(BFLB_DISABLE_BT) +extern struct k_thread recv_thread_data; +extern struct k_thread work_q_thread; +extern struct k_fifo recv_fifo; +extern struct k_fifo free_tx; +extern struct k_work_q g_work_queue_main; +#if defined(CONFIG_BT_SMP) +extern struct k_sem sc_local_pkey_ready; +#endif + +void bt_delete_queue(struct k_fifo * queue_to_del) +{ + struct net_buf *buf = NULL; + buf = net_buf_get(queue_to_del, K_NO_WAIT); + while(buf){ + net_buf_unref(buf); + buf = net_buf_get(queue_to_del, K_NO_WAIT); + } + + k_queue_free(&(queue_to_del->_queue)); +} + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) && (CONFIG_BT_CONN) +extern struct net_buf_pool acl_tx_pool; +extern struct net_buf_pool prep_pool; +#if defined(CONFIG_BT_BREDR) +extern struct net_buf_pool br_sig_pool; +extern struct net_buf_pool sdp_pool; +extern struct net_buf_pool hf_pool; +extern struct net_buf_pool dummy_pool; +#endif +#endif + +int bt_disable_action(void) +{ + #if defined(CONFIG_BT_PRIVACY) + k_delayed_work_del_timer(&bt_dev.rpa_update); + #endif + + bt_gatt_deinit(); + + //delete task + k_thread_delete(&tx_thread_data); + k_thread_delete(&recv_thread_data); + k_thread_delete(&work_q_thread); + + //delete queue, not delete hci_cmd_pool.free/hci_rx_pool.free/acl_tx_pool.free which store released buffers. + bt_delete_queue(&recv_fifo); + bt_delete_queue(&g_work_queue_main.fifo); + bt_delete_queue(&bt_dev.cmd_tx_queue); + + k_queue_free((struct k_queue *)&free_tx); + + //delete sem + k_sem_delete(&bt_dev.ncmd_sem); + k_sem_delete(&g_poll_sem); + #if defined(CONFIG_BT_SMP) + k_sem_delete(&sc_local_pkey_ready); + #endif + k_sem_delete(&bt_dev.le.pkts); + + queue_inited = true; + atomic_clear_bit(bt_dev.flags, BT_DEV_ENABLE); + + #if defined(BFLB_DYNAMIC_ALLOC_MEM) + net_buf_deinit(&hci_cmd_pool); + net_buf_deinit(&hci_rx_pool); + #if defined(CONFIG_BT_CONN) + net_buf_deinit(&acl_tx_pool); + net_buf_deinit(&num_complete_pool); + #if CONFIG_BT_ATT_PREPARE_COUNT > 0 + net_buf_deinit(&prep_pool); + #endif + #if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + net_buf_deinit(&acl_in_pool); + #endif + #if (CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0) + net_buf_deinit(&frag_pool); + #endif + #if defined(CONFIG_BT_BREDR) + net_buf_deinit(&br_sig_pool); + net_buf_deinit(&sdp_pool); + net_buf_deinit(&hf_pool); + net_buf_deinit(&dummy_pool); + #endif + #endif//defined(CONFIG_BT_CONN) + #if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) + net_buf_deinit(&discardable_pool); + #endif + #endif//defined(BFLB_DYNAMIC_ALLOC_MEM) + + #if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) + bl_onchiphci_interface_deinit(); + #endif + + extern void ble_controller_deinit(void); + ble_controller_deinit(); + + return 0; +} + +int bt_disable(void) +{ + if(le_check_valid_conn() || atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN) + || atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)){ + return -1; + } + else + return bt_disable_action(); +} +#endif + +static int set_ad(u16_t hci_op, const struct bt_ad *ad, size_t ad_len) +{ + struct bt_hci_cp_le_set_adv_data *set_data; + struct net_buf *buf; + int c, i; + + buf = bt_hci_cmd_create(hci_op, sizeof(*set_data)); + if (!buf) { + return -ENOBUFS; + } + + set_data = net_buf_add(buf, sizeof(*set_data)); + + (void)memset(set_data, 0, sizeof(*set_data)); + + for (c = 0; c < ad_len; c++) { + const struct bt_data *data = ad[c].data; + + for (i = 0; i < ad[c].len; i++) { + int len = data[i].data_len; + u8_t type = data[i].type; + + /* Check if ad fit in the remaining buffer */ + if (set_data->len + len + 2 > 31) { + len = 31 - (set_data->len + 2); + if (type != BT_DATA_NAME_COMPLETE || !len) { + net_buf_unref(buf); + BT_ERR("Too big advertising data"); + return -EINVAL; + } + type = BT_DATA_NAME_SHORTENED; + } + + set_data->data[set_data->len++] = len + 1; + set_data->data[set_data->len++] = type; + + memcpy(&set_data->data[set_data->len], data[i].data, + len); + set_data->len += len; + } + } + + return bt_hci_cmd_send_sync(hci_op, buf, NULL); +} + +int bt_set_name(const char *name) +{ +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + size_t len = strlen(name); +#if !defined(BFLB_BLE) + int err; +#endif + if (len >= sizeof(bt_dev.name)) { + return -ENOMEM; + } + + if (!strcmp(bt_dev.name, name)) { + return 0; + } + + strncpy(bt_dev.name, name, sizeof(bt_dev.name)); + + /* Update advertising name if in use */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_NAME)) { + struct bt_data data[] = { BT_DATA(BT_DATA_NAME_COMPLETE, name, + strlen(name)) }; + struct bt_ad sd = { data, ARRAY_SIZE(data) }; + + set_ad(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, &sd, 1); + + /* Make sure the new name is set */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + set_advertise_enable(false); + set_advertise_enable(true); + } + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { +#if defined(BFLB_BLE) + #if defined(CFG_SLEEP) + if(HBN_Get_Status_Flag() == 0) + #endif + bt_settings_save_name(); +#else + err = settings_save_one("bt/name", bt_dev.name, len); + if (err) { + BT_WARN("Unable to store name"); + } +#endif + } + + return 0; +#else + return -ENOMEM; +#endif +} + +const char *bt_get_name(void) +{ +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + return bt_dev.name; +#else + return CONFIG_BT_DEVICE_NAME; +#endif +} + +int bt_set_id_addr(const bt_addr_le_t *addr) +{ + bt_addr_le_t non_const_addr; + + if (atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + BT_ERR("Setting identity not allowed after bt_enable()"); + return -EBUSY; + } + + bt_addr_le_copy(&non_const_addr, addr); + + return bt_id_create(&non_const_addr, NULL); +} + +void bt_id_get(bt_addr_le_t *addrs, size_t *count) +{ + size_t to_copy = MIN(*count, bt_dev.id_count); + + memcpy(addrs, bt_dev.id_addr, to_copy * sizeof(bt_addr_le_t)); + *count = to_copy; +} + +static int id_find(const bt_addr_le_t *addr) +{ + u8_t id; + + for (id = 0U; id < bt_dev.id_count; id++) { + if (!bt_addr_le_cmp(addr, &bt_dev.id_addr[id])) { + return id; + } + } + + return -ENOENT; +} + +static void id_create(u8_t id, bt_addr_le_t *addr, u8_t *irk) +{ + if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + bt_addr_le_copy(&bt_dev.id_addr[id], addr); + } else { + bt_addr_le_t new_addr; + + do { + bt_addr_le_create_static(&new_addr); + /* Make sure we didn't generate a duplicate */ + } while (id_find(&new_addr) >= 0); + + bt_addr_le_copy(&bt_dev.id_addr[id], &new_addr); + + if (addr) { + bt_addr_le_copy(addr, &bt_dev.id_addr[id]); + } + } + +#if defined(CONFIG_BT_PRIVACY) + { + u8_t zero_irk[16] = { 0 }; + + if (irk && memcmp(irk, zero_irk, 16)) { + memcpy(&bt_dev.irk[id], irk, 16); + } else { + bt_rand(&bt_dev.irk[id], 16); + if (irk) { + memcpy(irk, &bt_dev.irk[id], 16); + } + } + } +#endif + /* Only store if stack was already initialized. Before initialization + * we don't know the flash content, so it's potentially harmful to + * try to write anything there. + */ + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + bt_settings_save_id(); + } +} + +int bt_id_create(bt_addr_le_t *addr, u8_t *irk) +{ + int new_id; + + if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + if (addr->type != BT_ADDR_LE_RANDOM || + !BT_ADDR_IS_STATIC(&addr->a)) { + BT_ERR("Only static random identity address supported"); + return -EINVAL; + } + + if (id_find(addr) >= 0) { + return -EALREADY; + } + } + + if (!IS_ENABLED(CONFIG_BT_PRIVACY) && irk) { + return -EINVAL; + } + + if (bt_dev.id_count == ARRAY_SIZE(bt_dev.id_addr)) { + return -ENOMEM; + } + + new_id = bt_dev.id_count++; + if (new_id == BT_ID_DEFAULT && + !atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + atomic_set_bit(bt_dev.flags, BT_DEV_USER_ID_ADDR); + } + + id_create(new_id, addr, irk); + + return new_id; +} + +int bt_id_reset(u8_t id, bt_addr_le_t *addr, u8_t *irk) +{ + if (addr && bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { + if (addr->type != BT_ADDR_LE_RANDOM || + !BT_ADDR_IS_STATIC(&addr->a)) { + BT_ERR("Only static random identity address supported"); + return -EINVAL; + } + + if (id_find(addr) >= 0) { + return -EALREADY; + } + } + + if (!IS_ENABLED(CONFIG_BT_PRIVACY) && irk) { + return -EINVAL; + } + + if (id == BT_ID_DEFAULT || id >= bt_dev.id_count) { + return -EINVAL; + } + + if (id == bt_dev.adv_id && atomic_test_bit(bt_dev.flags, + BT_DEV_ADVERTISING)) { + return -EBUSY; + } + + if (IS_ENABLED(CONFIG_BT_CONN) && + bt_addr_le_cmp(&bt_dev.id_addr[id], BT_ADDR_LE_ANY)) { + int err; + + err = bt_unpair(id, NULL); + if (err) { + return err; + } + } + + id_create(id, addr, irk); + + return id; +} + +int bt_id_delete(u8_t id) +{ + if (id == BT_ID_DEFAULT || id >= bt_dev.id_count) { + return -EINVAL; + } + + if (!bt_addr_le_cmp(&bt_dev.id_addr[id], BT_ADDR_LE_ANY)) { + return -EALREADY; + } + + if (id == bt_dev.adv_id && atomic_test_bit(bt_dev.flags, + BT_DEV_ADVERTISING)) { + return -EBUSY; + } + + if (IS_ENABLED(CONFIG_BT_CONN)) { + int err; + + err = bt_unpair(id, NULL); + if (err) { + return err; + } + } + +#if defined(CONFIG_BT_PRIVACY) + (void)memset(bt_dev.irk[id], 0, 16); +#endif + bt_addr_le_copy(&bt_dev.id_addr[id], BT_ADDR_LE_ANY); + + if (id == bt_dev.id_count - 1) { + bt_dev.id_count--; + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + bt_settings_save_id(); + } + + return 0; +} + +#if defined(CONFIG_BT_HCI_VS_EXT) +static uint8_t bt_read_static_addr(bt_addr_le_t *addr) +{ + struct bt_hci_rp_vs_read_static_addrs *rp; + struct net_buf *rsp; + int err, i; + u8_t cnt; + if (!(bt_dev.vs_commands[1] & BIT(0))) { + BT_WARN("Read Static Addresses command not available"); + return 0; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_STATIC_ADDRS, NULL, &rsp); + if (err) { + BT_WARN("Failed to read static addresses"); + return 0; + } + rp = (void *)rsp->data; + cnt = MIN(rp->num_addrs, CONFIG_BT_ID_MAX); + + for (i = 0; i < cnt; i++) { + addr[i].type = BT_ADDR_LE_RANDOM; + bt_addr_copy(&addr[i].a, &rp->a[i].bdaddr); + } + net_buf_unref(rsp); + if (!cnt) { + BT_WARN("No static addresses stored in controller"); + } + return cnt; +} +#elif defined(CONFIG_BT_CTLR) +uint8_t bt_read_static_addr(bt_addr_le_t *addr); +#endif /* CONFIG_BT_HCI_VS_EXT */ + +int bt_setup_id_addr(void) +{ +#if defined(CONFIG_BT_HCI_VS_EXT) || defined(CONFIG_BT_CTLR) + /* Only read the addresses if the user has not already configured one or + * more identities (!bt_dev.id_count). + */ + if (!bt_dev.id_count) { + bt_addr_le_t addrs[CONFIG_BT_ID_MAX]; + + bt_dev.id_count = bt_read_static_addr(addrs); + if (bt_dev.id_count) { + int i; + + for (i = 0; i < bt_dev.id_count; i++) { + id_create(i, &addrs[i], NULL); + } + + return set_random_address(&bt_dev.id_addr[0].a); + } + } +#endif + return bt_id_create(NULL, NULL); +} + +bool bt_addr_le_is_bonded(u8_t id, const bt_addr_le_t *addr) +{ + if (IS_ENABLED(CONFIG_BT_SMP)) { + struct bt_keys *keys = bt_keys_find_addr(id, addr); + + /* if there are any keys stored then device is bonded */ + return keys && keys->keys; + } else { + return false; + } +} + +static bool valid_adv_param(const struct bt_le_adv_param *param, bool dir_adv) +{ + if (param->id >= bt_dev.id_count || + !bt_addr_le_cmp(&bt_dev.id_addr[param->id], BT_ADDR_LE_ANY)) { + return false; + } + + if (!(param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + /* + * BT Core 4.2 [Vol 2, Part E, 7.8.5] + * The Advertising_Interval_Min and Advertising_Interval_Max + * shall not be set to less than 0x00A0 (100 ms) if the + * Advertising_Type is set to ADV_SCAN_IND or ADV_NONCONN_IND. + */ + if (bt_dev.hci_version < BT_HCI_VERSION_5_0 && + param->interval_min < 0x00a0) { + return false; + } + } + + if (is_wl_empty() && + ((param->options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) || + (param->options & BT_LE_ADV_OPT_FILTER_CONN))) { + return false; + } + + + if ((param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) || !dir_adv) { + if (param->interval_min > param->interval_max || + param->interval_min < 0x0020 || + param->interval_max > 0x4000) { + return false; + } + } + + return true; +} + +static inline bool ad_has_name(const struct bt_data *ad, size_t ad_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + if (ad[i].type == BT_DATA_NAME_COMPLETE || + ad[i].type == BT_DATA_NAME_SHORTENED) { + return true; + } + } + + return false; +} + +static int le_adv_update(const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len, + bool connectable, bool use_name) +{ + struct bt_ad d[2] = {}; + struct bt_data data; + int err; + + d[0].data = ad; + d[0].len = ad_len; + + err = set_ad(BT_HCI_OP_LE_SET_ADV_DATA, d, 1); + if (err) { + return err; + } + + d[0].data = sd; + d[0].len = sd_len; + + if (use_name) { + const char *name; + + if (sd) { + /* Cannot use name if name is already set */ + if (ad_has_name(sd, sd_len)) { + return -EINVAL; + } + } + + name = bt_get_name(); + data = (struct bt_data)BT_DATA( + BT_DATA_NAME_COMPLETE, + name, strlen(name)); + + d[1].data = &data; + d[1].len = 1; + } + + /* + * We need to set SCAN_RSP when enabling advertising type that + * allows for Scan Requests. + * + * If any data was not provided but we enable connectable + * undirected advertising sd needs to be cleared from values set + * by previous calls. + * Clearing sd is done by calling set_ad() with NULL data and + * zero len. + * So following condition check is unusual but correct. + */ + if (d[0].data || d[1].data || connectable) { + err = set_ad(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, d, 2); + if (err) { + return err; + } + } + + return 0; +} + +int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + bool connectable, use_name; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return -EAGAIN; + } + + connectable = atomic_test_bit(bt_dev.flags, + BT_DEV_ADVERTISING_CONNECTABLE); + use_name = atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING_NAME); + + return le_adv_update(ad, ad_len, sd, sd_len, connectable, use_name); +} + +int bt_le_adv_start_internal(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len, + const bt_addr_le_t *peer) +{ + struct bt_hci_cp_le_set_adv_param set_param; + const bt_addr_le_t *id_addr; + struct net_buf *buf; + bool dir_adv = (peer != NULL); + int err = 0; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + if (!valid_adv_param(param, dir_adv)) { + return -EINVAL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return -EALREADY; + } + + (void)memset(&set_param, 0, sizeof(set_param)); + + set_param.min_interval = sys_cpu_to_le16(param->interval_min); + set_param.max_interval = sys_cpu_to_le16(param->interval_max); + set_param.channel_map = adv_ch_map; + + if (bt_dev.adv_id != param->id) { + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + } + +#if defined(CONFIG_BT_WHITELIST) + if ((param->options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) && + (param->options & BT_LE_ADV_OPT_FILTER_CONN)) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_BOTH; + } else if (param->options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_SCAN_REQ; + } else if (param->options & BT_LE_ADV_OPT_FILTER_CONN) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_CONN_IND; + } else { +#else + { +#endif /* defined(CONFIG_BT_WHITELIST) */ + set_param.filter_policy = BT_LE_ADV_FP_NO_WHITELIST; + } + + /* Set which local identity address we're advertising with */ + bt_dev.adv_id = param->id; + id_addr = &bt_dev.id_addr[param->id]; + + if (param->options & BT_LE_ADV_OPT_CONNECTABLE) { + if (IS_ENABLED(CONFIG_BT_PRIVACY) && + !(param->options & BT_LE_ADV_OPT_USE_IDENTITY)) { + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_TYPE_RPA) + err = le_set_private_addr(param->id); + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + err = le_set_non_resolv_private_addr(param->id); + #else + err = le_set_private_addr(param->id); + #endif + if (err) { + return err; + } + + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_LE_PUBLIC) + set_param.own_addr_type = BT_ADDR_LE_PUBLIC; + if(param->addr_type == BT_ADDR_TYPE_RPA) + set_param.own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + #else + set_param.own_addr_type = + BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + #endif + } else { + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + } + } else { + /* + * If Static Random address is used as Identity + * address we need to restore it before advertising + * is enabled. Otherwise NRPA used for active scan + * could be used for advertising. + */ + if (id_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&id_addr->a); + if (err) { + return err; + } + } + + set_param.own_addr_type = id_addr->type; + } + + if (dir_adv) { + if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) { + set_param.type = BT_LE_ADV_DIRECT_IND_LOW_DUTY; + } else { + set_param.type = BT_LE_ADV_DIRECT_IND; + } + + bt_addr_le_copy(&set_param.direct_addr, peer); + + if (IS_ENABLED(CONFIG_BT_SMP) && + !IS_ENABLED(CONFIG_BT_PRIVACY) && + BT_FEAT_LE_PRIVACY(bt_dev.le.features) && + (param->options & BT_LE_ADV_OPT_DIR_ADDR_RPA)) { + /* This will not use RPA for our own address + * since we have set zeroed out the local IRK. + */ + set_param.own_addr_type |= + BT_HCI_OWN_ADDR_RPA_MASK; + } + } else { + set_param.type = BT_LE_ADV_IND; + } + } else { + if (param->options & BT_LE_ADV_OPT_USE_IDENTITY) { + if (id_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&id_addr->a); + } + + set_param.own_addr_type = id_addr->type; + } else { + #if defined(BFLB_BLE) && !defined(CONFIG_BT_MESH) + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_TYPE_RPA) + err = le_set_private_addr(param->id); + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + err = le_set_non_resolv_private_addr(param->id); + #else + #if !defined(CONFIG_BT_ADV_WITH_PUBLIC_ADDR) + err = le_set_private_addr(param->id); + #endif + #endif//CONFIG_BT_STACK_PTS + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_LE_PUBLIC) + set_param.own_addr_type = BT_ADDR_LE_PUBLIC; + else + #endif + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + #if defined(CONFIG_BT_ADV_WITH_PUBLIC_ADDR) + set_param.own_addr_type = BT_ADDR_LE_PUBLIC; + #endif + #endif + } + + if (err) { + return err; + } + + if (sd) { + set_param.type = BT_LE_ADV_SCAN_IND; + } else { + set_param.type = BT_LE_ADV_NONCONN_IND; + } + } + + #if defined(CONFIG_BT_STACK_PTS) + if(set_param.own_addr_type == BT_ADDR_LE_PUBLIC) + { + atomic_set_bit(bt_dev.flags, BT_DEV_ADV_ADDRESS_IS_PUBLIC); + } + #endif + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_PARAM, buf, NULL); + if (err) { + return err; + } + + if (!dir_adv) { + err = le_adv_update(ad, ad_len, sd, sd_len, + param->options & BT_LE_ADV_OPT_CONNECTABLE, + param->options & BT_LE_ADV_OPT_USE_NAME); + if (err) { + return err; + } + } + + err = set_advertise_enable(true); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_KEEP_ADVERTISING, + !(param->options & BT_LE_ADV_OPT_ONE_TIME)); + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING_NAME, + param->options & BT_LE_ADV_OPT_USE_NAME); + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE, + param->options & BT_LE_ADV_OPT_CONNECTABLE); + + + #if defined(BFLB_HOST_ASSISTANT) + if(!atomic_test_bit(bt_dev.flags, BT_DEV_ASSIST_RUN) + && host_assist_cb && host_assist_cb->le_adv_cb) + host_assist_cb->le_adv_cb(param, ad, ad_len, sd, sd_len); + #endif + + return 0; +} +#if defined (BFLB_BLE) +int bt_le_read_rssi(u16_t handle,int8_t *rssi) +{ + struct bt_hci_cp_read_rssi *le_rssi; + struct bt_hci_rp_read_rssi *rsp_rssi; + struct net_buf *buf; + struct net_buf *rsp; + int ret; + + buf = bt_hci_cmd_create(BT_HCI_OP_READ_RSSI, sizeof(*le_rssi)); + if (!buf) { + return -ENOBUFS; + } + + le_rssi = net_buf_add(buf, sizeof(*le_rssi)); + memset(le_rssi, 0, sizeof(*le_rssi)); + + le_rssi->handle = handle; + + ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_RSSI,buf,&rsp); + + if (ret) { + return ret; + } + + rsp_rssi = (struct bt_hci_rp_read_rssi *) rsp->data; + *rssi = rsp_rssi->rssi; + + net_buf_unref(rsp); + + return ret; +} + +int set_adv_enable(bool enable) +{ + int err; + err = set_advertise_enable(true); + if (err) { + return err; + } + + return 0; +} + +int set_adv_param(const struct bt_le_adv_param *param) +{ + struct bt_hci_cp_le_set_adv_param set_param; + const bt_addr_le_t *id_addr; + struct net_buf *buf; + int err = 0; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return -EALREADY; + } + + (void)memset(&set_param, 0, sizeof(set_param)); + + set_param.min_interval = sys_cpu_to_le16(param->interval_min); + set_param.max_interval = sys_cpu_to_le16(param->interval_max); + set_param.channel_map = 0x07; + + if (bt_dev.adv_id != param->id) { + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + } + +#if defined(CONFIG_BT_WHITELIST) + if ((param->options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) && + (param->options & BT_LE_ADV_OPT_FILTER_CONN)) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_BOTH; + } else if (param->options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_SCAN_REQ; + } else if (param->options & BT_LE_ADV_OPT_FILTER_CONN) { + set_param.filter_policy = BT_LE_ADV_FP_WHITELIST_CONN_IND; + } else { +#else + { +#endif /* defined(CONFIG_BT_WHITELIST) */ + set_param.filter_policy = BT_LE_ADV_FP_NO_WHITELIST; + } + + /* Set which local identity address we're advertising with */ + bt_dev.adv_id = param->id; + id_addr = &bt_dev.id_addr[param->id]; + + if (param->options & BT_LE_ADV_OPT_CONNECTABLE) { + if (IS_ENABLED(CONFIG_BT_PRIVACY) && + !(param->options & BT_LE_ADV_OPT_USE_IDENTITY)) { + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_TYPE_RPA) + err = le_set_private_addr(param->id); + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + err = le_set_non_resolv_private_addr(param->id); + #else + err = le_set_private_addr(param->id); + #endif + if (err) { + return err; + } + + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_LE_PUBLIC) + set_param.own_addr_type = BT_ADDR_LE_PUBLIC; + if(param->addr_type == BT_ADDR_TYPE_RPA) + set_param.own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + #else + set_param.own_addr_type = + BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + #endif + } else { + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + } + } else { + /* + * If Static Random address is used as Identity + * address we need to restore it before advertising + * is enabled. Otherwise NRPA used for active scan + * could be used for advertising. + */ + if (id_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&id_addr->a); + if (err) { + return err; + } + } + + set_param.own_addr_type = id_addr->type; + } + + set_param.type = BT_LE_ADV_IND; + + } else { + if (param->options & BT_LE_ADV_OPT_USE_IDENTITY) { + if (id_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&id_addr->a); + } + + set_param.own_addr_type = id_addr->type; + } else { + #if defined(BFLB_BLE) && !defined(CONFIG_BT_MESH) + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_TYPE_RPA) + err = le_set_private_addr(param->id); + else if(param->addr_type == BT_ADDR_TYPE_NON_RPA) + err = le_set_non_resolv_private_addr(param->id); + #else + err = le_set_private_addr(param->id); + #endif//CONFIG_BT_STACK_PTS + #if defined(CONFIG_BT_STACK_PTS) + if(param->addr_type == BT_ADDR_LE_PUBLIC) + set_param.own_addr_type = BT_ADDR_LE_PUBLIC; + else + #endif + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + #endif + } + + if (err) { + return err; + } + + set_param.type = BT_LE_ADV_NONCONN_IND; + + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_PARAM, buf, NULL); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_KEEP_ADVERTISING, + !(param->options & BT_LE_ADV_OPT_ONE_TIME)); + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING_NAME, + param->options & BT_LE_ADV_OPT_USE_NAME); + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ADVERTISING_CONNECTABLE, + param->options & BT_LE_ADV_OPT_CONNECTABLE); + + return 0; +} + + +int set_ad_and_rsp_d(u16_t hci_op, u8_t *data, u32_t ad_len) +{ + struct net_buf *buf; + u32_t len; + u8_t size; + + if(BT_HCI_OP_LE_SET_ADV_DATA == hci_op){ + + size = sizeof(struct bt_hci_cp_le_set_adv_data); + + }else if(BT_HCI_OP_LE_SET_SCAN_RSP_DATA == hci_op){ + + size = sizeof(struct bt_hci_cp_le_set_scan_rsp_data); + + }else + return -ENOTSUP; + + buf = bt_hci_cmd_create(hci_op, size); + if (!buf) { + return -ENOBUFS; + } + + if(BT_HCI_OP_LE_SET_ADV_DATA == hci_op){ + + struct bt_hci_cp_le_set_adv_data *set_data = net_buf_add(buf, size); + memset(set_data, 0, size); + set_data->len = ad_len; + + if (set_data->len > 30) { + len = 30 - (set_data->len); + if (!len) { + net_buf_unref(buf); + return -ENOBUFS; + } + } + + memcpy(set_data->data,data,set_data->len); + + }else if(BT_HCI_OP_LE_SET_SCAN_RSP_DATA == hci_op){ + + struct bt_hci_cp_le_set_scan_rsp_data *set_data = net_buf_add(buf, size); + memset(set_data, 0, size); + + set_data->len = ad_len; + + if (set_data->len > 30) { + len = 30 - (set_data->len); + if (!len) { + net_buf_unref(buf); + return -ENOBUFS; + } + } + + memcpy(set_data->data,data,set_data->len); + + }else + return -ENOBUFS; + + return bt_hci_cmd_send_sync(hci_op,buf,NULL); +} + +int set_adv_channel_map(u8_t channel) +{ + int err = 0; + + if(channel >= 1 && channel <= 7) + { + adv_ch_map = channel; + } + else + { + err = -1; + } + + return err; +} + +int bt_get_local_public_address(bt_addr_le_t *adv_addr) +{ + int err = 0; + + bt_addr_le_copy(adv_addr, bt_dev.id_addr); + return err; +} + +int bt_get_local_ramdon_address(bt_addr_le_t *adv_addr) +{ + int err = 0; + + bt_addr_le_copy(adv_addr,&bt_dev.random_addr); + return err; +} +#endif + +int bt_le_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) { + return -EINVAL; + } + + return bt_le_adv_start_internal(param, ad, ad_len, sd, sd_len, NULL); +} + +int bt_le_adv_stop(void) +{ + int err; + + /* Make sure advertising is not re-enabled later even if it's not + * currently enabled (i.e. BT_DEV_ADVERTISING is not set). + */ + atomic_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING); + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return 0; + } + + err = set_advertise_enable(false); + if (err) { + return err; + } + + if (!IS_ENABLED(CONFIG_BT_PRIVACY)) { + /* If active scan is ongoing set NRPA */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) && + atomic_test_bit(bt_dev.flags, BT_DEV_ACTIVE_SCAN)) { + le_set_private_addr(bt_dev.adv_id); + } + } + + return 0; +} + +#if defined(CONFIG_BLE_MULTI_ADV) +static int set_ad_data(u16_t hci_op, const uint8_t *ad_data, int ad_len) +{ + struct bt_hci_cp_le_set_adv_data *set_data; + struct net_buf * buf; + + buf = bt_hci_cmd_create(hci_op, sizeof(*set_data)); + if (!buf) { + return -ENOBUFS; + } + + if (ad_len > 31) + return -EINVAL; + + set_data = net_buf_add(buf, sizeof(*set_data)); + + memset(set_data, 0, sizeof(*set_data)); + memcpy(set_data->data, ad_data, ad_len); + set_data->len = ad_len; + + return bt_hci_cmd_send_sync(hci_op, buf, NULL); +} + +int bt_le_adv_start_instant(const struct bt_le_adv_param *param, + const uint8_t *ad_data, size_t ad_len, + const uint8_t *sd_data, size_t sd_len) +{ + struct bt_hci_cp_le_set_adv_param set_param; + struct net_buf * buf; + const bt_addr_le_t *id_addr; + int err; + + bt_le_adv_stop(); + + if (!valid_adv_param(param, false)) { + return -EINVAL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return -EALREADY; + } + + err = set_ad_data(BT_HCI_OP_LE_SET_ADV_DATA, ad_data, ad_len); + if (err) { + return err; + } + + /* + * We need to set SCAN_RSP when enabling advertising type that allows + * for Scan Requests. + * + * If sd was not provided but we enable connectable undirected + * advertising sd needs to be cleared from values set by previous calls. + * Clearing sd is done by calling set_ad() with NULL data and zero len. + * So following condition check is unusual but correct. + */ + if (sd_len || (param->options & BT_LE_ADV_OPT_CONNECTABLE)) { + err = set_ad_data(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, sd_data, sd_len); + if (err) { + return err; + } + } + + memset(&set_param, 0, sizeof(set_param)); + + set_param.min_interval = sys_cpu_to_le16(param->interval_min); + set_param.max_interval = sys_cpu_to_le16(param->interval_max); + set_param.channel_map = 0x07; + + bt_dev.adv_id = param->id; + id_addr = &bt_dev.id_addr[param->id]; + + if (param->options & BT_LE_ADV_OPT_CONNECTABLE) { + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + err = le_set_private_addr(bt_dev.adv_id); + if (err) { + return err; + } + + if (BT_FEAT_LE_PRIVACY(bt_dev.le.features)) { + set_param.own_addr_type = BT_HCI_OWN_ADDR_RPA_OR_RANDOM; + } else { + set_param.own_addr_type = BT_ADDR_LE_RANDOM; + } + } else { + /* + * If Static Random address is used as Identity + * address we need to restore it before advertising + * is enabled. Otherwise NRPA used for active scan + * could be used for advertising. + */ + if (id_addr->type == BT_ADDR_LE_RANDOM) { + err = set_random_address(&id_addr->a); + if (err) { + return err; + } + } + set_param.own_addr_type = id_addr->type; + } + + set_param.type = BT_LE_ADV_IND; + } else { + + if (sd_len) { + set_param.type = BT_LE_ADV_SCAN_IND; + } else { + set_param.type = BT_LE_ADV_NONCONN_IND; + } + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_PARAM, buf, NULL); + if (err) { + return err; + } + + err = set_advertise_enable(true); + if (err) { + return err; + } + + if (!(param->options & BT_LE_ADV_OPT_ONE_TIME)) { + atomic_set_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING); + } + + return 0; +} +#endif //CONFIG_BLE_MULTI_ADV + +#if defined(CONFIG_BT_OBSERVER) +static bool valid_le_scan_param(const struct bt_le_scan_param *param) +{ + if (param->type != BT_HCI_LE_SCAN_PASSIVE && + param->type != BT_HCI_LE_SCAN_ACTIVE) { + return false; + } + + if (param->filter_dup & + ~(BT_LE_SCAN_FILTER_DUPLICATE | BT_LE_SCAN_FILTER_WHITELIST)) { + return false; + } + + if (is_wl_empty() && + param->filter_dup & BT_LE_SCAN_FILTER_WHITELIST) { + return false; + } + + if (param->interval < 0x0004 || param->interval > 0x4000) { + return false; + } + + if (param->window < 0x0004 || param->window > 0x4000) { + return false; + } + + if (param->window > param->interval) { + return false; + } + + return true; +} + +#if defined(CONFIG_BT_STACK_PTS) +int bt_le_pts_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb, u8_t addre_type) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + /* Check that the parameters have valid values */ + if (!valid_le_scan_param(param)) { + return -EINVAL; + } + + /* Return if active scan is already enabled */ + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP, + param->filter_dup & BT_LE_SCAN_FILTER_DUPLICATE); + +#if defined(CONFIG_BT_WHITELIST) + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_WL, + param->filter_dup & BT_LE_SCAN_FILTER_WHITELIST); +#endif /* defined(CONFIG_BT_WHITELIST) */ + + err = start_le_scan_with_isrpa(param->type, param->interval, param->window, addre_type); + + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + + scan_dev_found_cb = cb; + + return 0; + +} +#endif +int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) + +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + return -EAGAIN; + } + + /* Check that the parameters have valid values */ + if (!valid_le_scan_param(param)) { + return -EINVAL; + } + + /* Return if active scan is already enabled */ + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = set_le_scan_enable(BT_HCI_LE_SCAN_DISABLE); + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_FILTER_DUP, + param->filter_dup & BT_LE_SCAN_FILTER_DUPLICATE); + +#if defined(CONFIG_BT_WHITELIST) + atomic_set_bit_to(bt_dev.flags, BT_DEV_SCAN_WL, + param->filter_dup & BT_LE_SCAN_FILTER_WHITELIST); +#endif /* defined(CONFIG_BT_WHITELIST) */ + + err = start_le_scan(param->type, param->interval, param->window); + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + + scan_dev_found_cb = cb; + + #if defined(BFLB_HOST_ASSISTANT) + if(!atomic_test_bit(bt_dev.flags, BT_DEV_ASSIST_RUN) + && host_assist_cb && host_assist_cb->le_scan_cb) + host_assist_cb->le_scan_cb(param, cb); + #endif + + return 0; +} + +int bt_le_scan_stop(void) +{ + /* Return if active scanning is already disabled */ + if (!atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + scan_dev_found_cb = NULL; + + return bt_le_scan_update(false); +} +#endif /* CONFIG_BT_OBSERVER */ + +#if defined(CONFIG_BT_WHITELIST) +int bt_le_whitelist_add(const bt_addr_le_t *addr) +{ + struct bt_hci_cp_le_add_dev_to_wl *cp; + struct net_buf *buf; + int err; + + if (!(bt_dev.le.wl_entries < bt_dev.le.wl_size)) { + return -ENOMEM; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ADD_DEV_TO_WL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->addr, addr); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ADD_DEV_TO_WL, buf, NULL); + if (err) { + BT_ERR("Failed to add device to whitelist"); + + return err; + } + + bt_dev.le.wl_entries++; + + return 0; +} + +int bt_le_whitelist_rem(const bt_addr_le_t *addr) +{ + struct bt_hci_cp_le_rem_dev_from_wl *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REM_DEV_FROM_WL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_le_copy(&cp->addr, addr); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REM_DEV_FROM_WL, buf, NULL); + if (err) { + BT_ERR("Failed to remove device from whitelist"); + return err; + } + + bt_dev.le.wl_entries--; + return 0; +} + +int bt_le_whitelist_clear(void) +{ + int err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_CLEAR_WL, NULL, NULL); + + if (err) { + BT_ERR("Failed to clear whitelist"); + return err; + } + + bt_dev.le.wl_entries = 0; + return 0; +} +#endif /* defined(CONFIG_BT_WHITELIST) */ + +int bt_le_set_chan_map(u8_t chan_map[5]) +{ + struct bt_hci_cp_le_set_host_chan_classif *cp; + struct net_buf *buf; + + if (!IS_ENABLED(CONFIG_BT_CENTRAL)) { + return -ENOTSUP; + } + + if (!BT_CMD_TEST(bt_dev.supported_commands, 27, 3)) { + BT_WARN("Set Host Channel Classification command is " + "not supported"); + return -ENOTSUP; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF, + sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + memcpy(&cp->ch_map[0], &chan_map[0], 4); + cp->ch_map[4] = chan_map[4] & BIT_MASK(5); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF, + buf, NULL); +} +#if defined(CONFIG_SET_TX_PWR) +int bt_set_tx_pwr(int8_t power) +{ + struct bt_hci_cp_vs_set_tx_pwr set_param; + struct net_buf *buf; + int err; + + if(power < 0 || power > 20) + return BT_HCI_ERR_INVALID_PARAM; + + memset(&set_param, 0, sizeof(set_param)); + + set_param.power = power; + + buf = bt_hci_cmd_create(BT_HCI_OP_VS_SET_TX_PWR, sizeof(set_param)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_mem(buf, &set_param, sizeof(set_param)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_SET_TX_PWR, buf, NULL); + + if (err) { + return err; + } + + return 0; +} +#endif + +int bt_buf_get_rx_avail_cnt(void) +{ + return (k_queue_get_cnt(&hci_rx_pool.free._queue) \ + + hci_rx_pool.uninit_count); +} + +struct net_buf *bt_buf_get_rx(enum bt_buf_type type, s32_t timeout) +{ + struct net_buf *buf; + + __ASSERT(type == BT_BUF_EVT || type == BT_BUF_ACL_IN, + "Invalid buffer type requested"); + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) + if (type == BT_BUF_EVT) { + buf = net_buf_alloc(&hci_rx_pool, timeout); + } else { + buf = net_buf_alloc(&acl_in_pool, timeout); + } +#else + buf = net_buf_alloc(&hci_rx_pool, timeout); +#endif + + if (buf) { + net_buf_reserve(buf, BT_BUF_RESERVE); + bt_buf_set_type(buf, type); + } + + return buf; +} + +struct net_buf *bt_buf_get_cmd_complete(s32_t timeout) +{ + struct net_buf *buf; + unsigned int key; + + key = irq_lock(); + buf = bt_dev.sent_cmd; + bt_dev.sent_cmd = NULL; + irq_unlock(key); + + BT_DBG("sent_cmd %p", buf); + + if (buf) { + bt_buf_set_type(buf, BT_BUF_EVT); + buf->len = 0U; + net_buf_reserve(buf, BT_BUF_RESERVE); + + return buf; + } + + return bt_buf_get_rx(BT_BUF_EVT, timeout); +} + +struct net_buf *bt_buf_get_evt(u8_t evt, bool discardable, s32_t timeout) +{ + switch (evt) { +#if defined(CONFIG_BT_CONN) + case BT_HCI_EVT_NUM_COMPLETED_PACKETS: + { + struct net_buf *buf; + + buf = net_buf_alloc(&num_complete_pool, timeout); + if (buf) { + net_buf_reserve(buf, BT_BUF_RESERVE); + bt_buf_set_type(buf, BT_BUF_EVT); + } + + return buf; + } +#endif /* CONFIG_BT_CONN */ + case BT_HCI_EVT_CMD_COMPLETE: + case BT_HCI_EVT_CMD_STATUS: + return bt_buf_get_cmd_complete(timeout); + default: +#if defined(CONFIG_BT_DISCARDABLE_BUF_COUNT) + if (discardable) { + struct net_buf *buf; + + buf = net_buf_alloc(&discardable_pool, timeout); + if (buf) { + net_buf_reserve(buf, BT_BUF_RESERVE); + bt_buf_set_type(buf, BT_BUF_EVT); + } + + return buf; + } +#endif /* CONFIG_BT_DISCARDABLE_BUF_COUNT */ + + return bt_buf_get_rx(BT_BUF_EVT, timeout); + } +} + +#if defined(CONFIG_BT_BREDR) +static int br_start_inquiry(const struct bt_br_discovery_param *param) +{ + const u8_t iac[3] = { 0x33, 0x8b, 0x9e }; + struct bt_hci_op_inquiry *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_INQUIRY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + cp->length = param->length; + cp->num_rsp = 0xff; /* we limit discovery only by time */ + + memcpy(cp->lap, iac, 3); + if (param->limited) { + cp->lap[0] = 0x00; + } + + return bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY, buf, NULL); +} + +static bool valid_br_discov_param(const struct bt_br_discovery_param *param, + size_t num_results) +{ + if (!num_results || num_results > 255) { + return false; + } + + if (!param->length || param->length > 0x30) { + return false; + } + + return true; +} + +int bt_br_discovery_start(const struct bt_br_discovery_param *param, + struct bt_br_discovery_result *results, size_t cnt, + bt_br_discovery_cb_t cb) +{ + int err; + + BT_DBG(""); + + if (!valid_br_discov_param(param, cnt)) { + return -EINVAL; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) { + return -EALREADY; + } + + err = br_start_inquiry(param); + if (err) { + return err; + } + + atomic_set_bit(bt_dev.flags, BT_DEV_INQUIRY); + + (void)memset(results, 0, sizeof(*results) * cnt); + + discovery_cb = cb; + discovery_results = results; + discovery_results_size = cnt; + discovery_results_count = 0; + + return 0; +} + +int bt_br_discovery_stop(void) +{ + int err; + int i; + + BT_DBG(""); + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_INQUIRY)) { + return -EALREADY; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_INQUIRY_CANCEL, NULL, NULL); + if (err) { + return err; + } + + for (i = 0; i < discovery_results_count; i++) { + struct discovery_priv *priv; + struct bt_hci_cp_remote_name_cancel *cp; + struct net_buf *buf; + + priv = (struct discovery_priv *)&discovery_results[i]._priv; + + if (!priv->resolving) { + continue; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_REMOTE_NAME_CANCEL, + sizeof(*cp)); + if (!buf) { + continue; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, &discovery_results[i].addr); + + bt_hci_cmd_send_sync(BT_HCI_OP_REMOTE_NAME_CANCEL, buf, NULL); + } + + atomic_clear_bit(bt_dev.flags, BT_DEV_INQUIRY); + + discovery_cb = NULL; + discovery_results = NULL; + discovery_results_size = 0; + discovery_results_count = 0; + + return 0; +} + +static int write_scan_enable(u8_t scan) +{ + struct net_buf *buf; + int err; + + BT_DBG("type %u", scan); + + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SCAN_ENABLE, 1); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_u8(buf, scan); + err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SCAN_ENABLE, buf, NULL); + if (err) { + return err; + } + + atomic_set_bit_to(bt_dev.flags, BT_DEV_ISCAN, + (scan & BT_BREDR_SCAN_INQUIRY)); + atomic_set_bit_to(bt_dev.flags, BT_DEV_PSCAN, + (scan & BT_BREDR_SCAN_PAGE)); + + return 0; +} + +int bt_br_set_connectable(bool enable) +{ + if (enable) { + if (atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) { + return -EALREADY; + } else { + return write_scan_enable(BT_BREDR_SCAN_PAGE); + } + } else { + if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) { + return -EALREADY; + } else { + return write_scan_enable(BT_BREDR_SCAN_DISABLED); + } + } +} + +int bt_br_set_discoverable(bool enable) +{ + if (enable) { + if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { + return -EALREADY; + } + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) { + return -EPERM; + } + + return write_scan_enable(BT_BREDR_SCAN_INQUIRY | + BT_BREDR_SCAN_PAGE); + } else { + if (!atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { + return -EALREADY; + } + + return write_scan_enable(BT_BREDR_SCAN_PAGE); + } +} + +int bt_br_write_eir(u8_t rec, u8_t *data) +{ + struct bt_hci_cp_write_ext_inquiry_resp *ext_ir; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_EXT_INQUIRY_RESP, sizeof(*ext_ir)); + if (!buf) { + return -ENOBUFS; + } + + ext_ir = net_buf_add(buf, sizeof(*ext_ir)); + + ext_ir->rec= rec; + memcpy(ext_ir->eir, data, strlen((char *)data)); + + return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_EXT_INQUIRY_RESP, buf, NULL); +} + +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_ECC) +int bt_pub_key_gen(struct bt_pub_key_cb *new_cb) +{ + int err; + + /* + * We check for both "LE Read Local P-256 Public Key" and + * "LE Generate DH Key" support here since both commands are needed for + * ECC support. If "LE Generate DH Key" is not supported then there + * is no point in reading local public key. + */ + if (!BT_CMD_TEST(bt_dev.supported_commands, 34, 1) || + !BT_CMD_TEST(bt_dev.supported_commands, 34, 2)) { + BT_WARN("ECC HCI commands not available"); + return -ENOTSUP; + } + + #if defined(BFLB_BLE_PATCH_AVOID_DUPLI_PUBKEY_CB) + struct bt_pub_key_cb *cb; + struct bt_pub_key_cb *valid_cb; + bool existed = false; + + if(pub_key_cb){ + cb = pub_key_cb; + valid_cb = cb; + while(cb){ + if(new_cb->func == cb->func){ + existed = true; + break; + } + + valid_cb = cb; + cb = cb->_next; + } + + if(!existed){ + valid_cb->_next = new_cb; + } + }else{ + pub_key_cb = new_cb; + } + #else + new_cb->_next = pub_key_cb; + pub_key_cb = new_cb; + #endif + + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { + return 0; + } + + atomic_clear_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_P256_PUBLIC_KEY, NULL, NULL); + if (err) { + BT_ERR("Sending LE P256 Public Key command failed"); + atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); + pub_key_cb = NULL; + return err; + } + + return 0; +} + +const u8_t *bt_pub_key_get(void) +{ + if (atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) { + return pub_key; + } + + return NULL; +} + +int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb) +{ + struct bt_hci_cp_le_generate_dhkey *cp; + struct net_buf *buf; + int err; + + if (dh_key_cb || atomic_test_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { + return -EBUSY; + } + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) { + return -EADDRNOTAVAIL; + } + + dh_key_cb = cb; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_GENERATE_DHKEY, sizeof(*cp)); + if (!buf) { + dh_key_cb = NULL; + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + memcpy(cp->key, remote_pk, sizeof(cp->key)); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_GENERATE_DHKEY, buf, NULL); + if (err) { + dh_key_cb = NULL; + return err; + } + + return 0; +} +#endif /* CONFIG_BT_ECC */ + +#if defined(CONFIG_BT_BREDR) +int bt_br_oob_get_local(struct bt_br_oob *oob) +{ + bt_addr_copy(&oob->addr, &bt_dev.id_addr[0].a); + + return 0; +} +#endif /* CONFIG_BT_BREDR */ + +int bt_le_oob_get_local(u8_t id, struct bt_le_oob *oob) +{ + int err; + + if (id >= CONFIG_BT_ID_MAX) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_PRIVACY)) { + /* Invalidate RPA so a new one is generated */ + atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID); + + err = le_set_private_addr(id); + if (err) { + return err; + } + + bt_addr_le_copy(&oob->addr, &bt_dev.random_addr); + } else { + bt_addr_le_copy(&oob->addr, &bt_dev.id_addr[id]); + } + + + if (IS_ENABLED(CONFIG_BT_SMP)) { + err = bt_smp_le_oob_generate_sc_data(&oob->le_sc_data); + if (err) { + return err; + } + } + + return 0; +} + +#if defined(CONFIG_BT_SMP) +int bt_le_oob_set_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data *oobd_local, + const struct bt_le_oob_sc_data *oobd_remote) +{ + return bt_smp_le_oob_set_sc_data(conn, oobd_local, oobd_remote); +} + +int bt_le_oob_get_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data **oobd_local, + const struct bt_le_oob_sc_data **oobd_remote) +{ + return bt_smp_le_oob_get_sc_data(conn, oobd_local, oobd_remote); +} +#endif + +#if defined(BFLB_RELEASE_CMD_SEM_IF_CONN_DISC) +void hci_release_conn_related_cmd(void) +{ + u16_t opcode; + + (void)opcode; + + if(bt_dev.sent_cmd) + { + opcode = cmd(bt_dev.sent_cmd)->opcode; + switch(opcode) + { + case BT_HCI_OP_LE_SET_DATA_LEN: + case BT_HCI_OP_LE_READ_REMOTE_FEATURES: + case BT_HCI_OP_LE_SET_DEFAULT_PHY: + case BT_HCI_OP_LE_SET_PHY: + case BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY: + case BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY: + case BT_HCI_OP_LE_LTK_REQ_NEG_REPLY: + case BT_HCI_OP_LE_LTK_REQ_REPLY: + { + + k_sem_give(&bt_dev.ncmd_sem); + hci_cmd_done(opcode, BT_HCI_ERR_UNSPECIFIED, bt_dev.sent_cmd); + net_buf_unref(bt_dev.sent_cmd); + bt_dev.sent_cmd = NULL; + } + break; + default: + break; + } + + } +} +#endif + +#if defined(BFLB_HOST_ASSISTANT) +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +int bt_set_flow_control(void) +{ + return set_flow_control(); +} +#endif +int bt_set_event_mask(void) +{ + return set_event_mask(); +} + +int bt_le_set_event_mask(void) +{ + return le_set_event_mask(); +} + +void bt_hci_reset_complete(struct net_buf *buf) +{ + hci_reset_complete(buf); +} + +void bt_register_host_assist_cb(struct blhast_cb *cb) +{ + host_assist_cb = cb; +} +#endif diff --git a/components/ble/ble_stack/host/hci_core.h b/components/ble/ble_stack/host/hci_core.h new file mode 100644 index 00000000..e80b7fdf --- /dev/null +++ b/components/ble/ble_stack/host/hci_core.h @@ -0,0 +1,287 @@ +/* hci_core.h - Bluetooth HCI core access */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* LL connection parameters */ +#define LE_CONN_LATENCY 0x0000 +#define LE_CONN_TIMEOUT 0x002a + +#if defined(CONFIG_BT_BREDR) +#define LMP_FEAT_PAGES_COUNT 3 +#else +#define LMP_FEAT_PAGES_COUNT 1 +#endif + +/* SCO settings */ +#define BT_VOICE_CVSD_16BIT 0x0060 +#define BT_VOICE_MSBC_16BIT 0x0063 + +/* k_poll event tags */ +enum { + BT_EVENT_CMD_TX, + BT_EVENT_CONN_TX_QUEUE, +}; + +/* bt_dev flags: the flags defined here represent BT controller state */ +enum { + BT_DEV_ENABLE, + BT_DEV_READY, + BT_DEV_PRESET_ID, + BT_DEV_USER_ID_ADDR, + BT_DEV_HAS_PUB_KEY, + BT_DEV_PUB_KEY_BUSY, + + BT_DEV_ADVERTISING, + BT_DEV_ADVERTISING_NAME, + BT_DEV_ADVERTISING_CONNECTABLE, + BT_DEV_KEEP_ADVERTISING, + BT_DEV_SCANNING, + BT_DEV_EXPLICIT_SCAN, + BT_DEV_ACTIVE_SCAN, + BT_DEV_SCAN_FILTER_DUP, + BT_DEV_SCAN_WL, + BT_DEV_AUTO_CONN, + + BT_DEV_RPA_VALID, + + BT_DEV_ID_PENDING, + +#if defined(CONFIG_BT_BREDR) + BT_DEV_ISCAN, + BT_DEV_PSCAN, + BT_DEV_INQUIRY, +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_STACK_PTS) + BT_DEV_ADV_ADDRESS_IS_PUBLIC, +#endif + +#if defined(BFLB_HOST_ASSISTANT) + BT_DEV_ASSIST_RUN, +#endif + + + /* Total number of flags - must be at the end of the enum */ + BT_DEV_NUM_FLAGS, +}; + +/* Flags which should not be cleared upon HCI_Reset */ +#define BT_DEV_PERSISTENT_FLAGS (BIT(BT_DEV_ENABLE) | \ + BIT(BT_DEV_PRESET_ID) | \ + BIT(BT_DEV_USER_ID_ADDR)) + +struct bt_dev_le { + /* LE features */ + u8_t features[8]; + /* LE states */ + u64_t states; + +#if defined(CONFIG_BT_CONN) + /* Controller buffer information */ + u16_t mtu; + struct k_sem pkts; +#endif /* CONFIG_BT_CONN */ + +#if defined(CONFIG_BT_SMP) + /* Size of the the controller resolving list */ + u8_t rl_size; + /* Number of entries in the resolving list. rl_entries > rl_size + * means that host-side resolving is used. + */ + u8_t rl_entries; +#endif /* CONFIG_BT_SMP */ + +#if defined(CONFIG_BT_WHITELIST) + /* Size of the controller whitelist. */ + u8_t wl_size; + /* Number of entries in the resolving list. */ + u8_t wl_entries; +#endif /* CONFIG_BT_WHITELIST */ +}; + +#if defined(CONFIG_BT_BREDR) +struct bt_dev_br { + /* Max controller's acceptable ACL packet length */ + u16_t mtu; + struct k_sem pkts; + u16_t esco_pkt_type; +}; +#endif + +/* The theoretical max for these is 8 and 64, but there's no point + * in allocating the full memory if we only support a small subset. + * These values must be updated whenever the host implementation is + * extended beyond the current values. + */ +#define BT_DEV_VS_FEAT_MAX 1 +#define BT_DEV_VS_CMDS_MAX 2 + +/* State tracking for the local Bluetooth controller */ +struct bt_dev { + /* Local Identity Address(es) */ + bt_addr_le_t id_addr[CONFIG_BT_ID_MAX]; + u8_t id_count; + + /* ID Address used for advertising */ + u8_t adv_id; + + /* Current local Random Address */ + bt_addr_le_t random_addr; + + /* Controller version & manufacturer information */ + u8_t hci_version; + u8_t lmp_version; + u16_t hci_revision; + u16_t lmp_subversion; + u16_t manufacturer; + + /* LMP features (pages 0, 1, 2) */ + u8_t features[LMP_FEAT_PAGES_COUNT][8]; + + /* Supported commands */ + u8_t supported_commands[64]; + +#if defined(CONFIG_BT_HCI_VS_EXT) + /* Vendor HCI support */ + u8_t vs_features[BT_DEV_VS_FEAT_MAX]; + u8_t vs_commands[BT_DEV_VS_CMDS_MAX]; +#endif + + struct k_work init; + + ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS); + + /* LE controller specific features */ + struct bt_dev_le le; + +#if defined(CONFIG_BT_BREDR) + /* BR/EDR controller specific features */ + struct bt_dev_br br; +#endif + + /* Number of commands controller can accept */ + struct k_sem ncmd_sem; + + /* Last sent HCI command */ + struct net_buf *sent_cmd; + +#if !defined(CONFIG_BT_RECV_IS_RX_THREAD) + /* Queue for incoming HCI events & ACL data */ + struct k_fifo rx_queue; +#endif + + /* Queue for outgoing HCI commands */ + struct k_fifo cmd_tx_queue; + + /* Registered HCI driver */ + const struct bt_hci_driver *drv; + +#if defined(CONFIG_BT_PRIVACY) + /* Local Identity Resolving Key */ + u8_t irk[CONFIG_BT_ID_MAX][16]; + + /* Work used for RPA rotation */ + struct k_delayed_work rpa_update; +#endif + + /* Local Name */ +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + char name[CONFIG_BT_DEVICE_NAME_MAX + 1]; +#endif +}; + +#if defined (CONFIG_BT_STACK_PTS) +typedef enum __packed{ + dir_connect_req = 0x01, /*Send a direct connection require while the Lower test enters direct mode .*/ + + ad_type_service_uuid = 0x02, + ad_type_local_name = 0x03, + ad_type_flags = 0x04, + ad_type_manu_data = 0x05, + ad_type_tx_power_level = 0x06, + ad_type_service_data = 0x07, + ad_type_appearance = 0x08, + + gatt_discover_chara = 0x09, + gatt_exec_write_req = 0x0a, + gatt_cancel_write_req = 0x0b, + att_read_by_group_type_ind = 0x0c, /* CASE : GATT/SR/GAD/BV-01-C. Indicate PTS sends a GATT discover all primary services request to iut */ + att_find_by_type_value_ind = 0x0d, /* CASE : GATT/SR/GAD/BV-02-C. Indicate PTS sends a request to iut for discover it contains Primary Services by Service UUID */ + att_read_by_type_ind = 0x0e, /* CASE : GATT/SR/GAD/BV-04-C. Indicate PTS sends a request to iut for discover all characteristics of a specified service.*/ + + own_addr_type_random = 0x0f +}event_id; + +#endif + +extern struct bt_dev bt_dev; +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) +extern const struct bt_conn_auth_cb *bt_auth; +#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */ + +bool bt_le_conn_params_valid(const struct bt_le_conn_param *param); + +int bt_le_scan_update(bool fast_scan); + +int bt_le_auto_conn(const struct bt_le_conn_param *conn_param); +int bt_le_auto_conn_cancel(void); + +bool bt_addr_le_is_bonded(u8_t id, const bt_addr_le_t *addr); +const bt_addr_le_t *bt_lookup_id_addr(u8_t id, const bt_addr_le_t *addr); + +int bt_send(struct net_buf *buf); + +/* Don't require everyone to include keys.h */ +struct bt_keys; +void bt_id_add(struct bt_keys *keys); +void bt_id_del(struct bt_keys *keys); + +int bt_setup_id_addr(void); +void bt_finalize_init(void); + +int bt_le_adv_start_internal(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len, + const bt_addr_le_t *peer); +#if defined(CONFIG_BLE_MULTI_ADV) +int bt_le_adv_start_instant(const struct bt_le_adv_param *param, + const uint8_t *ad_data, size_t ad_len, + const uint8_t *sd_data, size_t sd_len); +#endif + +#if defined (BFLB_BLE) + +int bt_le_read_rssi(u16_t handle,int8_t *rssi); +int set_ad_and_rsp_d(u16_t hci_op, u8_t *data, u32_t ad_len); +int set_adv_enable(bool enable); +int set_adv_param(const struct bt_le_adv_param *param); +int set_adv_channel_map(u8_t channel); +int bt_get_local_public_address(bt_addr_le_t *adv_addr); +int bt_get_local_ramdon_address(bt_addr_le_t *adv_addr); +int bt_le_set_data_len(struct bt_conn *conn, u16_t tx_octets, u16_t tx_time); +int hci_le_set_phy(struct bt_conn *conn); +int hci_le_set_default_phy(struct bt_conn *conn,u8_t default_phy); + + +#if defined(CONFIG_SET_TX_PWR) +int bt_set_tx_pwr(int8_t power); +#endif + +#if defined(BFLB_HOST_ASSISTANT) +struct blhast_cb{ + void (*le_scan_cb)(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb); + void (*le_adv_cb)(const struct bt_le_adv_param *param, const struct bt_data *ad, + size_t ad_len, const struct bt_data *sd, size_t sd_len); +}; +int bt_set_flow_control(void); +int bt_set_event_mask(void); +int bt_le_set_event_mask(void); +void bt_hci_reset_complete(struct net_buf *buf); +void bt_register_host_assist_cb(struct blhast_cb *cb); +#endif + +#endif diff --git a/components/ble/ble_stack/host/hci_ecc.c b/components/ble/ble_stack/host/hci_ecc.c new file mode 100644 index 00000000..ac3fa8b1 --- /dev/null +++ b/components/ble/ble_stack/host/hci_ecc.c @@ -0,0 +1,340 @@ +/** + * @file hci_ecc.c + * HCI ECC emulation + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include <../include/bluetooth/crypto.h> + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#include "log.h" + +#include "hci_ecc.h" +#ifdef CONFIG_BT_HCI_RAW +#include +#include "hci_raw_internal.h" +#else +#include "hci_core.h" +#endif + +static struct k_thread ecc_thread_data; +#if !defined(BFLB_BLE) +static BT_STACK_NOINIT(ecc_thread_stack, 1024); +#endif + +/* based on Core Specification 4.2 Vol 3. Part H 2.3.5.6.1 */ +static const u32_t debug_private_key[8] = { + 0xcd3c1abd, 0x5899b8a6, 0xeb40b799, 0x4aff607b, 0xd2103f50, 0x74c9b3e3, + 0xa3c55f38, 0x3f49f6d4 +}; + +#if defined(CONFIG_BT_USE_DEBUG_KEYS) +static const u8_t debug_public_key[64] = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, 0xdb, 0xfd, 0xf4, 0xac, + 0x11, 0x91, 0xf4, 0xef, 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, 0x8b, 0xd2, 0x89, 0x15, + 0xd0, 0x8e, 0x1c, 0x74, 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76, + 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, 0x6d, 0xeb, 0x2a, 0x65, + 0x49, 0x9c, 0x80, 0xdc +}; +#endif + +enum { + PENDING_PUB_KEY, + PENDING_DHKEY, + + /* Total number of flags - must be at the end of the enum */ + NUM_FLAGS, +}; + +static ATOMIC_DEFINE(flags, NUM_FLAGS); + +static K_SEM_DEFINE(cmd_sem, 0, 1); + +static struct { + u8_t private_key[32]; + + union { + u8_t pk[64]; + u8_t dhkey[32]; + }; +} ecc; + +static void send_cmd_status(u16_t opcode, u8_t status) +{ + struct bt_hci_evt_cmd_status *evt; + struct bt_hci_evt_hdr *hdr; + struct net_buf *buf; + + BT_DBG("opcode %x status %x", opcode, status); + + buf = bt_buf_get_evt(BT_HCI_EVT_CMD_STATUS, false, K_FOREVER); + bt_buf_set_type(buf, BT_BUF_EVT); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->evt = BT_HCI_EVT_CMD_STATUS; + hdr->len = sizeof(*evt); + + evt = net_buf_add(buf, sizeof(*evt)); + evt->ncmd = 1U; + evt->opcode = sys_cpu_to_le16(opcode); + evt->status = status; + + bt_recv_prio(buf); +} + +static u8_t generate_keys(void) +{ +#if !defined(CONFIG_BT_USE_DEBUG_KEYS) + do { + int rc; + + rc = uECC_make_key(ecc.pk, ecc.private_key, &curve_secp256r1); + if (rc == TC_CRYPTO_FAIL) { + BT_ERR("Failed to create ECC public/private pair"); + return BT_HCI_ERR_UNSPECIFIED; + } + + /* make sure generated key isn't debug key */ + } while (memcmp(ecc.private_key, debug_private_key, 32) == 0); +#else + sys_memcpy_swap(&ecc.pk, debug_public_key, 32); + sys_memcpy_swap(&ecc.pk[32], &debug_public_key[32], 32); + sys_memcpy_swap(ecc.private_key, debug_private_key, 32); +#endif + return 0; +} + +static void emulate_le_p256_public_key_cmd(void) +{ + struct bt_hci_evt_le_p256_public_key_complete *evt; + struct bt_hci_evt_le_meta_event *meta; + struct bt_hci_evt_hdr *hdr; + struct net_buf *buf; + u8_t status; + + BT_DBG(""); + + status = generate_keys(); + + buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->evt = BT_HCI_EVT_LE_META_EVENT; + hdr->len = sizeof(*meta) + sizeof(*evt); + + meta = net_buf_add(buf, sizeof(*meta)); + meta->subevent = BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE; + + evt = net_buf_add(buf, sizeof(*evt)); + evt->status = status; + + if (status) { + (void)memset(evt->key, 0, sizeof(evt->key)); + } else { + /* Convert X and Y coordinates from big-endian (provided + * by crypto API) to little endian HCI. + */ + sys_memcpy_swap(evt->key, ecc.pk, 32); + sys_memcpy_swap(&evt->key[32], &ecc.pk[32], 32); + } + + atomic_clear_bit(flags, PENDING_PUB_KEY); + + bt_recv(buf); +} + +static void emulate_le_generate_dhkey(void) +{ + struct bt_hci_evt_le_generate_dhkey_complete *evt; + struct bt_hci_evt_le_meta_event *meta; + struct bt_hci_evt_hdr *hdr; + struct net_buf *buf; + int ret; + + ret = uECC_valid_public_key(ecc.pk, &curve_secp256r1); + if (ret < 0) { + BT_ERR("public key is not valid (ret %d)", ret); + ret = TC_CRYPTO_FAIL; + } else { + ret = uECC_shared_secret(ecc.pk, ecc.private_key, ecc.dhkey, + &curve_secp256r1); + } + + buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->evt = BT_HCI_EVT_LE_META_EVENT; + hdr->len = sizeof(*meta) + sizeof(*evt); + + meta = net_buf_add(buf, sizeof(*meta)); + meta->subevent = BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE; + + evt = net_buf_add(buf, sizeof(*evt)); + + if (ret == TC_CRYPTO_FAIL) { + evt->status = BT_HCI_ERR_UNSPECIFIED; + (void)memset(evt->dhkey, 0, sizeof(evt->dhkey)); + } else { + evt->status = 0U; + /* Convert from big-endian (provided by crypto API) to + * little-endian HCI. + */ + sys_memcpy_swap(evt->dhkey, ecc.dhkey, sizeof(ecc.dhkey)); + } + + atomic_clear_bit(flags, PENDING_DHKEY); + + bt_recv(buf); +} + +#if defined(BFLB_BLE) +static void ecc_thread(void *p1) +#else +static void ecc_thread(void *p1, void *p2, void *p3) +#endif +{ + while (true) { + k_sem_take(&cmd_sem, K_FOREVER); + + if (atomic_test_bit(flags, PENDING_PUB_KEY)) { + emulate_le_p256_public_key_cmd(); + } else if (atomic_test_bit(flags, PENDING_DHKEY)) { + emulate_le_generate_dhkey(); + } else { + __ASSERT(0, "Unhandled ECC command"); + } +#if !defined(BFLB_BLE) + STACK_ANALYZE("ecc stack", ecc_thread_stack); +#endif + } +} + +static void clear_ecc_events(struct net_buf *buf) +{ + struct bt_hci_cp_le_set_event_mask *cmd; + + cmd = (void *)(buf->data + sizeof(struct bt_hci_cmd_hdr)); + + /* + * don't enable controller ECC events as those will be generated from + * emulation code + */ + cmd->events[0] &= ~0x80; /* LE Read Local P-256 PKey Compl */ + cmd->events[1] &= ~0x01; /* LE Generate DHKey Compl Event */ +} + +static void le_gen_dhkey(struct net_buf *buf) +{ + struct bt_hci_cp_le_generate_dhkey *cmd; + u8_t status; + + if (atomic_test_bit(flags, PENDING_PUB_KEY)) { + status = BT_HCI_ERR_CMD_DISALLOWED; + goto send_status; + } + + if (buf->len < sizeof(struct bt_hci_cp_le_generate_dhkey)) { + status = BT_HCI_ERR_INVALID_PARAM; + goto send_status; + } + + if (atomic_test_and_set_bit(flags, PENDING_DHKEY)) { + status = BT_HCI_ERR_CMD_DISALLOWED; + goto send_status; + } + + cmd = (void *)buf->data; + /* Convert X and Y coordinates from little-endian HCI to + * big-endian (expected by the crypto API). + */ + sys_memcpy_swap(ecc.pk, cmd->key, 32); + sys_memcpy_swap(&ecc.pk[32], &cmd->key[32], 32); + k_sem_give(&cmd_sem); + status = BT_HCI_ERR_SUCCESS; + +send_status: + net_buf_unref(buf); + send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, status); +} + +static void le_p256_pub_key(struct net_buf *buf) +{ + u8_t status; + + net_buf_unref(buf); + + if (atomic_test_bit(flags, PENDING_DHKEY)) { + status = BT_HCI_ERR_CMD_DISALLOWED; + } else if (atomic_test_and_set_bit(flags, PENDING_PUB_KEY)) { + status = BT_HCI_ERR_CMD_DISALLOWED; + } else { + k_sem_give(&cmd_sem); + status = BT_HCI_ERR_SUCCESS; + } + + send_cmd_status(BT_HCI_OP_LE_P256_PUBLIC_KEY, status); +} + +int bt_hci_ecc_send(struct net_buf *buf) +{ + if (bt_buf_get_type(buf) == BT_BUF_CMD) { + struct bt_hci_cmd_hdr *chdr = (void *)buf->data; + + switch (sys_le16_to_cpu(chdr->opcode)) { + case BT_HCI_OP_LE_P256_PUBLIC_KEY: + net_buf_pull(buf, sizeof(*chdr)); + le_p256_pub_key(buf); + return 0; + case BT_HCI_OP_LE_GENERATE_DHKEY: + net_buf_pull(buf, sizeof(*chdr)); + le_gen_dhkey(buf); + return 0; + case BT_HCI_OP_LE_SET_EVENT_MASK: + clear_ecc_events(buf); + break; + default: + break; + } + } + + return bt_dev.drv->send(buf); +} + +int default_CSPRNG(u8_t *dst, unsigned int len) +{ + return !bt_rand(dst, len); +} + +void bt_hci_ecc_init(void) +{ +#if defined(BFLB_BLE) + k_sem_init(&cmd_sem, 0, 1); + k_thread_create(&ecc_thread_data, "ecc_thread", + CONFIG_BT_HCI_ECC_STACK_SIZE, ecc_thread, + CONFIG_BT_WORK_QUEUE_PRIO); +#else + k_thread_create(&ecc_thread_data, ecc_thread_stack, + K_THREAD_STACK_SIZEOF(ecc_thread_stack), ecc_thread, + NULL, NULL, NULL, K_PRIO_PREEMPT(10), 0, K_NO_WAIT); + k_thread_name_set(&ecc_thread_data, "BT ECC"); +#endif +} diff --git a/components/ble/ble_stack/host/hci_ecc.h b/components/ble/ble_stack/host/hci_ecc.h new file mode 100644 index 00000000..dcf4cf9d --- /dev/null +++ b/components/ble/ble_stack/host/hci_ecc.h @@ -0,0 +1,10 @@ +/* hci_ecc.h - HCI ECC emulation */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_hci_ecc_init(void); +int bt_hci_ecc_send(struct net_buf *buf); diff --git a/components/ble/ble_stack/host/hfp_hf.c b/components/ble/ble_stack/host/hfp_hf.c new file mode 100644 index 00000000..ed7c4016 --- /dev/null +++ b/components/ble/ble_stack/host/hfp_hf.c @@ -0,0 +1,929 @@ +/* hfp_hf.c - Hands free Profile - Handsfree side handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include + +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HFP_HF) +#define LOG_MODULE_NAME bt_hfp_hf +#include "log.h" + +#include +#include +#include + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "rfcomm_internal.h" +#include "at.h" +#include "hfp_internal.h" + +#define MAX_IND_STR_LEN 17 + +struct bt_hfp_hf_cb *bt_hf; +bool hfp_codec_msbc = 0; + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_FIXED_DEFINE(hf_pool, CONFIG_BT_MAX_CONN + 1, + BT_RFCOMM_BUF_SIZE(BT_HF_CLIENT_MAX_PDU), NULL); +#else +struct net_buf_pool hf_pool; +#endif + +static struct bt_hfp_hf bt_hfp_hf_pool[CONFIG_BT_MAX_CONN]; + +static struct bt_sdp_attribute hfp_attrs[] = { + BT_SDP_NEW_SERVICE, + BT_SDP_LIST( + BT_SDP_ATTR_SVCLASS_ID_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 10), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_HANDSFREE_SVCLASS) + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_GENERIC_AUDIO_SVCLASS) + }, + ) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROTO_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 12), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP) + }, + ) + }, + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 5), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_PROTO_RFCOMM) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT8), + BT_SDP_ARRAY_16(BT_RFCOMM_CHAN_HFP_HF) + } + ) + }, + ) + ), + BT_SDP_LIST( + BT_SDP_ATTR_PROFILE_DESC_LIST, + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6), + BT_SDP_DATA_ELEM_LIST( + { + BT_SDP_TYPE_SIZE(BT_SDP_UUID16), + BT_SDP_ARRAY_16(BT_SDP_HANDSFREE_SVCLASS) + }, + { + BT_SDP_TYPE_SIZE(BT_SDP_UINT16), + BT_SDP_ARRAY_16(0x0107) + }, + ) + }, + ) + ), + BT_SDP_SERVICE_NAME("hands-free"), + /* + "SupportedFeatures" attribute bit mapping for the HF + bit 0: EC and/or NR function + bit 1: Call waiting or three-way calling + bit 2: CLI presentation capability + bit 3: Voice recognition activation + bit 4: Remote volume control + bit 5: Wide band speech + bit 6: Enhanced Voice Recognition Status + bit 7: Voice Recognition Text + */ + BT_SDP_SUPPORTED_FEATURES(0x0035), +}; + +static struct bt_sdp_record hfp_rec = BT_SDP_RECORD(hfp_attrs); + +/* The order should follow the enum hfp_hf_ag_indicators */ +static const struct { + char *name; + uint32_t min; + uint32_t max; +} ag_ind[] = { + {"service", 0, 1}, /* HF_SERVICE_IND */ + {"call", 0, 1}, /* HF_CALL_IND */ + {"callsetup", 0, 3}, /* HF_CALL_SETUP_IND */ + {"callheld", 0, 2}, /* HF_CALL_HELD_IND */ + {"signal", 0, 5}, /* HF_SINGNAL_IND */ + {"roam", 0, 1}, /* HF_ROAM_IND */ + {"battchg", 0, 5} /* HF_BATTERY_IND */ +}; + + +static void connected(struct bt_conn *conn) +{ + BT_DBG("HFP HF Connected!"); +} + +static void disconnected(struct bt_conn *conn) +{ + BT_DBG("HFP HF Disconnected!"); +} + +static void service(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Service indicator value: %u", value); +} + +static void call(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Call indicator value: %u", value); +} + +static void call_setup(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Call Setup indicator value: %u", value); +} + +static void call_held(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Call Held indicator value: %u", value); +} + +static void signal(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Signal indicator value: %u", value); +} + +static void roam(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Roaming indicator value: %u", value); +} + +static void battery(struct bt_conn *conn, uint32_t value) +{ + BT_DBG("Battery indicator value: %u", value); +} + +static void ring_cb(struct bt_conn *conn) +{ + BT_DBG("Incoming Call..."); +} + +static struct bt_hfp_hf_cb hf_cb = { + .connected = connected, + .disconnected = disconnected, + .service = service, + .call = call, + .call_setup = call_setup, + .call_held = call_held, + .signal = signal, + .roam = roam, + .battery = battery, + .ring_indication = ring_cb, +}; + + +void hf_slc_error(struct at_client *hf_at) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + BT_ERR("SLC error: disconnecting"); + err = bt_rfcomm_dlc_disconnect(&hf->rfcomm_dlc); + if (err) { + BT_ERR("Rfcomm: Unable to disconnect :%d", -err); + } +} + +int hfp_hf_send_cmd(struct bt_hfp_hf *hf, at_resp_cb_t resp, + at_finish_cb_t finish, const char *format, ...) +{ + struct net_buf *buf; + va_list vargs; + int ret; + + /* register the callbacks */ + at_register(&hf->at, resp, finish); + + buf = bt_rfcomm_create_pdu(&hf_pool); + if (!buf) { + BT_ERR("No Buffers!"); + return -ENOMEM; + } + + va_start(vargs, format); + ret = vsnprintf((char*)buf->data, (net_buf_tailroom(buf) - 1), format, vargs); + if (ret < 0) { + BT_ERR("Unable to format variable arguments"); + return ret; + } + va_end(vargs); + + net_buf_add(buf, ret); + net_buf_add_u8(buf, '\r'); + + ret = bt_rfcomm_dlc_send(&hf->rfcomm_dlc, buf); + if (ret < 0) { + BT_ERR("Rfcomm send error :(%d)", ret); + return ret; + } + + return 0; +} + +int brsf_handle(struct at_client *hf_at) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + uint32_t val; + int ret; + + ret = at_get_number(hf_at, &val); + if (ret < 0) { + BT_ERR("Error getting value"); + return ret; + } + + hf->ag_features = val; + + return 0; +} + +int brsf_resp(struct at_client *hf_at, struct net_buf *buf) +{ + int err; + + BT_DBG(""); + + err = at_parse_cmd_input(hf_at, buf, "BRSF", brsf_handle, + AT_CMD_TYPE_NORMAL); + if (err < 0) { + /* Returning negative value is avoided before SLC connection + * established. + */ + BT_ERR("Error parsing CMD input"); + hf_slc_error(hf_at); + } + + return 0; +} + +static void cind_handle_values(struct at_client *hf_at, uint32_t index, + char *name, uint32_t min, uint32_t max) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int i; + + BT_DBG("index: %u, name: %s, min: %u, max:%u", index, name, min, max); + + for (i = 0; i < ARRAY_SIZE(ag_ind); i++) { + if (strcmp(name, ag_ind[i].name) != 0) { + continue; + } + if (min != ag_ind[i].min || max != ag_ind[i].max) { + BT_ERR("%s indicator min/max value not matching", name); + } + + hf->ind_table[index] = i; + break; + } +} + +int cind_handle(struct at_client *hf_at) +{ + uint32_t index = 0U; + + /* Parsing Example: CIND: ("call",(0,1)) etc.. */ + while (at_has_next_list(hf_at)) { + char name[MAX_IND_STR_LEN]; + uint32_t min, max; + + if (at_open_list(hf_at) < 0) { + BT_ERR("Could not get open list"); + goto error; + } + + if (at_list_get_string(hf_at, name, sizeof(name)) < 0) { + BT_ERR("Could not get string"); + goto error; + } + + if (at_open_list(hf_at) < 0) { + BT_ERR("Could not get open list"); + goto error; + } + + if (at_list_get_range(hf_at, &min, &max) < 0) { + BT_ERR("Could not get range"); + goto error; + } + + if (at_close_list(hf_at) < 0) { + BT_ERR("Could not get close list"); + goto error; + } + + if (at_close_list(hf_at) < 0) { + BT_ERR("Could not get close list"); + goto error; + } + + cind_handle_values(hf_at, index, name, min, max); + index++; + } + + return 0; +error: + BT_ERR("Error on CIND response"); + hf_slc_error(hf_at); + return -EINVAL; +} + +int cind_resp(struct at_client *hf_at, struct net_buf *buf) +{ + int err; + + err = at_parse_cmd_input(hf_at, buf, "CIND", cind_handle, + AT_CMD_TYPE_NORMAL); + if (err < 0) { + BT_ERR("Error parsing CMD input"); + hf_slc_error(hf_at); + } + + return 0; +} + +void ag_indicator_handle_values(struct at_client *hf_at, uint32_t index, + uint32_t value) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn; + + BT_DBG("Index :%u, Value :%u", index, value); + + if (index >= ARRAY_SIZE(ag_ind)) { + BT_ERR("Max only %lu indicators are supported", + ARRAY_SIZE(ag_ind)); + return; + } + + if (value > ag_ind[hf->ind_table[index]].max || + value < ag_ind[hf->ind_table[index]].min) { + BT_ERR("Indicators out of range - value: %u", value); + return; + } + + switch (hf->ind_table[index]) { + case HF_SERVICE_IND: + if (bt_hf->service) { + bt_hf->service(conn, value); + } + break; + case HF_CALL_IND: + if (bt_hf->call) { + bt_hf->call(conn, value); + } + break; + case HF_CALL_SETUP_IND: + if (bt_hf->call_setup) { + bt_hf->call_setup(conn, value); + } + break; + case HF_CALL_HELD_IND: + if (bt_hf->call_held) { + bt_hf->call_held(conn, value); + } + break; + case HF_SINGNAL_IND: + if (bt_hf->signal) { + bt_hf->signal(conn, value); + } + break; + case HF_ROAM_IND: + if (bt_hf->roam) { + bt_hf->roam(conn, value); + } + break; + case HF_BATTERY_IND: + if (bt_hf->battery) { + bt_hf->battery(conn, value); + } + break; + default: + BT_ERR("Unknown AG indicator"); + break; + } +} + +int cind_status_handle(struct at_client *hf_at) +{ + uint32_t index = 0U; + + while (at_has_next_list(hf_at)) { + uint32_t value; + int ret; + + ret = at_get_number(hf_at, &value); + if (ret < 0) { + BT_ERR("could not get the value"); + return ret; + } + + ag_indicator_handle_values(hf_at, index, value); + + index++; + } + + return 0; +} + +int cind_status_resp(struct at_client *hf_at, struct net_buf *buf) +{ + int err; + + err = at_parse_cmd_input(hf_at, buf, "CIND", cind_status_handle, + AT_CMD_TYPE_NORMAL); + if (err < 0) { + BT_ERR("Error parsing CMD input"); + hf_slc_error(hf_at); + } + + return 0; +} + +int ciev_handle(struct at_client *hf_at) +{ + uint32_t index, value; + int ret; + + ret = at_get_number(hf_at, &index); + if (ret < 0) { + BT_ERR("could not get the Index"); + return ret; + } + /* The first element of the list shall have 1 */ + if (!index) { + BT_ERR("Invalid index value '0'"); + return 0; + } + + ret = at_get_number(hf_at, &value); + if (ret < 0) { + BT_ERR("could not get the value"); + return ret; + } + + ag_indicator_handle_values(hf_at, (index - 1), value); + + return 0; +} + +int ring_handle(struct at_client *hf_at) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn; + + if (bt_hf->ring_indication) { + bt_hf->ring_indication(conn); + } + + return 0; +} + +int bcs_handle(struct at_client *hf_at) +{ + uint32_t value; + int ret; + + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + + ret = at_get_number(hf_at, &value); + if (ret < 0) { + BT_ERR("could not get the value"); + return ret; + } + if (value == 1) { + if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+BCS=1") < 0) { + BT_ERR("Error Sending AT+BCS=1"); + } + } + else if (value == 2) { + if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+BCS=2") < 0) { + BT_ERR("Error Sending AT+BCS=2"); + } else { + hfp_codec_msbc = 1; + } + } + else { + BT_WARN("Invail BCS value !"); + } + + return 0; +} + +static const struct unsolicited { + const char *cmd; + enum at_cmd_type type; + int (*func)(struct at_client *hf_at); +} handlers[] = { + { "CIEV", AT_CMD_TYPE_UNSOLICITED, ciev_handle }, + { "RING", AT_CMD_TYPE_OTHER, ring_handle }, + { "BCS", AT_CMD_TYPE_UNSOLICITED, bcs_handle } +}; + +static const struct unsolicited *hfp_hf_unsol_lookup(struct at_client *hf_at) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (!strncmp(hf_at->buf, handlers[i].cmd, + strlen(handlers[i].cmd))) { + return &handlers[i]; + } + } + + return NULL; +} + +int unsolicited_cb(struct at_client *hf_at, struct net_buf *buf) +{ + const struct unsolicited *handler; + + handler = hfp_hf_unsol_lookup(hf_at); + if (!handler) { + BT_ERR("Unhandled unsolicited response"); + return -ENOMSG; + } + + if (!at_parse_cmd_input(hf_at, buf, handler->cmd, handler->func, + handler->type)) { + return 0; + } + + return -ENOMSG; +} + +int cmd_complete(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn; + struct bt_hfp_hf_cmd_complete cmd = { 0 }; + + BT_DBG(""); + + switch (result) { + case AT_RESULT_OK: + cmd.type = HFP_HF_CMD_OK; + break; + case AT_RESULT_ERROR: + cmd.type = HFP_HF_CMD_ERROR; + break; + case AT_RESULT_CME_ERROR: + cmd.type = HFP_HF_CMD_CME_ERROR; + cmd.cme = cme_err; + break; + default: + BT_ERR("Unknown error code"); + cmd.type = HFP_HF_CMD_UNKNOWN_ERROR; + break; + } + + if (bt_hf->cmd_complete_cb) { + bt_hf->cmd_complete_cb(conn, &cmd); + } + + return 0; +} + +int cmee_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + return -EINVAL; + } + + if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+NREC=0") < 0) { + BT_ERR("Error Sending AT+NREC"); + } + + return 0; +} + +static void slc_completed(struct at_client *hf_at) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn; + + if (bt_hf->connected) { + bt_hf->connected(conn); + } + + if (hfp_hf_send_cmd(hf, NULL, cmee_finish, "AT+CMEE=1") < 0) { + BT_ERR("Error Sending AT+CMEE"); + } +} + +int cmer_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + slc_completed(hf_at); + + return 0; +} + +int cind_status_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + at_register_unsolicited(hf_at, unsolicited_cb); + err = hfp_hf_send_cmd(hf, NULL, cmer_finish, "AT+CMER=3,0,0,1"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + +int cind_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + err = hfp_hf_send_cmd(hf, cind_status_resp, cind_status_finish, + "AT+CIND?"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + +int bac_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + err = hfp_hf_send_cmd(hf, cind_resp, cind_finish, "AT+CIND=?"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + +int brsf_finish(struct at_client *hf_at, enum at_result result, + enum at_cme cme_err) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at); + int err; + + if (result != AT_RESULT_OK) { + BT_ERR("SLC Connection ERROR in response"); + hf_slc_error(hf_at); + return -EINVAL; + } + + err = hfp_hf_send_cmd(hf, NULL, bac_finish, "AT+BAC=1,2"); + if (err < 0) { + hf_slc_error(hf_at); + return err; + } + + return 0; +} + +int hf_slc_establish(struct bt_hfp_hf *hf) +{ + int err; + + BT_DBG(""); + + err = hfp_hf_send_cmd(hf, brsf_resp, brsf_finish, "AT+BRSF=%u", + hf->hf_features); + if (err < 0) { + hf_slc_error(&hf->at); + return err; + } + + return 0; +} + +static struct bt_hfp_hf *bt_hfp_hf_lookup_bt_conn(struct bt_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) { + struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i]; + + if (hf->rfcomm_dlc.session->br_chan.chan.conn == conn) { + return hf; + } + } + + return NULL; +} + +int bt_hfp_hf_send_cmd(struct bt_conn *conn, enum bt_hfp_hf_at_cmd cmd) +{ + struct bt_hfp_hf *hf; + int err; + + BT_DBG(""); + + if (!conn) { + BT_ERR("Invalid connection"); + return -ENOTCONN; + } + + hf = bt_hfp_hf_lookup_bt_conn(conn); + if (!hf) { + BT_ERR("No HF connection found"); + return -ENOTCONN; + } + + switch (cmd) { + case BT_HFP_HF_ATA: + err = hfp_hf_send_cmd(hf, NULL, cmd_complete, "ATA"); + if (err < 0) { + BT_ERR("Failed ATA"); + return err; + } + break; + case BT_HFP_HF_AT_CHUP: + err = hfp_hf_send_cmd(hf, NULL, cmd_complete, "AT+CHUP"); + if (err < 0) { + BT_ERR("Failed AT+CHUP"); + return err; + } + break; + default: + BT_ERR("Invalid AT Command"); + return -EINVAL; + } + + return 0; +} + +static void hfp_hf_connected(struct bt_rfcomm_dlc *dlc) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc); + + BT_DBG("hf connected"); + + BT_ASSERT(hf); + hf_slc_establish(hf); +} + +static void hfp_hf_disconnected(struct bt_rfcomm_dlc *dlc) +{ + struct bt_conn *conn = dlc->session->br_chan.chan.conn; + + BT_DBG("hf disconnected!"); + if (bt_hf->disconnected) { + bt_hf->disconnected(conn); + } +} + +static void hfp_hf_recv(struct bt_rfcomm_dlc *dlc, struct net_buf *buf) +{ + struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc); + + if (at_parse_input(&hf->at, buf) < 0) { + BT_ERR("Parsing failed"); + } +} + +static int bt_hfp_hf_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc) +{ + int i; + static struct bt_rfcomm_dlc_ops ops = { + .connected = hfp_hf_connected, + .disconnected = hfp_hf_disconnected, + .recv = hfp_hf_recv, + }; + + BT_DBG("conn %p", conn); + + for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) { + struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i]; + int j; + + if (hf->rfcomm_dlc.session) { + continue; + } + + hf->at.buf = hf->hf_buffer; + hf->at.buf_max_len = HF_MAX_BUF_LEN; + + hf->rfcomm_dlc.ops = &ops; + hf->rfcomm_dlc.mtu = BT_HFP_MAX_MTU; + + *dlc = &hf->rfcomm_dlc; + + /* Set the supported features*/ + hf->hf_features = BT_HFP_HF_SUPPORTED_FEATURES; + + for (j = 0; j < HF_MAX_AG_INDICATORS; j++) { + hf->ind_table[j] = -1; + } + + return 0; + } + + BT_ERR("Unable to establish HF connection (%p)", conn); + + return -ENOMEM; +} + +int bt_hfp_hf_init(void) +{ + int err; + +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + k_lifo_init(&hf_pool.free, CONFIG_BT_MAX_CONN + 1); + net_buf_init(&hf_pool, CONFIG_BT_MAX_CONN + 1, BT_RFCOMM_BUF_SIZE(BT_HF_CLIENT_MAX_PDU), NULL); +#endif + + bt_hf = &hf_cb; + + static struct bt_rfcomm_server chan = { + .channel = BT_RFCOMM_CHAN_HFP_HF, + .accept = bt_hfp_hf_accept, + }; + + bt_rfcomm_server_register(&chan); + + /* Register SDP record */ + err = bt_sdp_register_service(&hfp_rec); + if(err < 0) + { + BT_ERR("HFP regist sdp record failed"); + } + BT_DBG("HFP initialized successfully."); + return err; +} + diff --git a/components/ble/ble_stack/host/hfp_internal.h b/components/ble/ble_stack/host/hfp_internal.h new file mode 100644 index 00000000..aaaa651a --- /dev/null +++ b/components/ble/ble_stack/host/hfp_internal.h @@ -0,0 +1,66 @@ +/** @file + * @brief Internal APIs for Bluetooth Handsfree profile handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_HFP_MAX_MTU 140 +#define BT_HF_CLIENT_MAX_PDU BT_HFP_MAX_MTU + +/* HFP AG Features */ +#define BT_HFP_AG_FEATURE_3WAY_CALL 0x00000001 /* Three-way calling */ +#define BT_HFP_AG_FEATURE_ECNR 0x00000002 /* EC and/or NR function */ +#define BT_HFP_AG_FEATURE_VOICE_RECG 0x00000004 /* Voice recognition */ +#define BT_HFP_AG_INBAND_RING_TONE 0x00000008 /* In-band ring capability */ +#define BT_HFP_AG_VOICE_TAG 0x00000010 /* Attach no. to voice tag */ +#define BT_HFP_AG_FEATURE_REJECT_CALL 0x00000020 /* Ability to reject call */ +#define BT_HFP_AG_FEATURE_ECS 0x00000040 /* Enhanced call status */ +#define BT_HFP_AG_FEATURE_ECC 0x00000080 /* Enhanced call control */ +#define BT_HFP_AG_FEATURE_EXT_ERR 0x00000100 /* Extented error codes */ +#define BT_HFP_AG_FEATURE_CODEC_NEG 0x00000200 /* Codec negotiation */ +#define BT_HFP_AG_FEATURE_HF_IND 0x00000400 /* HF Indicators */ +#define BT_HFP_AG_FEARTURE_ESCO_S4 0x00000800 /* eSCO S4 Settings */ + +/* HFP HF Features */ +#define BT_HFP_HF_FEATURE_ECNR 0x00000001 /* EC and/or NR */ +#define BT_HFP_HF_FEATURE_3WAY_CALL 0x00000002 /* Three-way calling */ +#define BT_HFP_HF_FEATURE_CLI 0x00000004 /* CLI presentation */ +#define BT_HFP_HF_FEATURE_VOICE_RECG 0x00000008 /* Voice recognition */ +#define BT_HFP_HF_FEATURE_VOLUME 0x00000010 /* Remote volume control */ +#define BT_HFP_HF_FEATURE_ECS 0x00000020 /* Enhanced call status */ +#define BT_HFP_HF_FEATURE_ECC 0x00000040 /* Enhanced call control */ +#define BT_HFP_HF_FEATURE_CODEC_NEG 0x00000080 /* CODEC Negotiation */ +#define BT_HFP_HF_FEATURE_HF_IND 0x00000100 /* HF Indicators */ +#define BT_HFP_HF_FEATURE_ESCO_S4 0x00000200 /* eSCO S4 Settings */ + +/* HFP HF Supported features */ +#define BT_HFP_HF_SUPPORTED_FEATURES (BT_HFP_HF_FEATURE_ECNR | \ + BT_HFP_HF_FEATURE_CLI | \ + BT_HFP_HF_FEATURE_VOLUME | \ + BT_HFP_HF_FEATURE_CODEC_NEG) + +#define HF_MAX_BUF_LEN BT_HF_CLIENT_MAX_PDU +#define HF_MAX_AG_INDICATORS 20 + +struct bt_hfp_hf { + struct bt_rfcomm_dlc rfcomm_dlc; + char hf_buffer[HF_MAX_BUF_LEN]; + struct at_client at; + uint32_t hf_features; + uint32_t ag_features; + int8_t ind_table[HF_MAX_AG_INDICATORS]; +}; + +enum hfp_hf_ag_indicators { + HF_SERVICE_IND, + HF_CALL_IND, + HF_CALL_SETUP_IND, + HF_CALL_HELD_IND, + HF_SINGNAL_IND, + HF_ROAM_IND, + HF_BATTERY_IND +}; diff --git a/components/ble/ble_stack/host/iso.c b/components/ble/ble_stack/host/iso.c new file mode 100644 index 00000000..9ea2fa1a --- /dev/null +++ b/components/ble/ble_stack/host/iso.c @@ -0,0 +1,1110 @@ +/* Bluetooth ISO */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "hci_core.h" +#include "conn_internal.h" +#include "iso_internal.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_ISO) +#define LOG_MODULE_NAME bt_iso +#include "log.h" + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_FIXED_DEFINE(iso_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT, + CONFIG_BT_ISO_TX_MTU, NULL); +NET_BUF_POOL_FIXED_DEFINE(iso_rx_pool, CONFIG_BT_ISO_RX_BUF_COUNT, + CONFIG_BT_ISO_RX_MTU, NULL); +#if CONFIG_BT_ISO_TX_FRAG_COUNT > 0 +NET_BUF_POOL_FIXED_DEFINE(iso_frag_pool, CONFIG_BT_ISO_TX_FRAG_COUNT, + CONFIG_BT_ISO_TX_MTU, NULL); +#endif + +#else //defined(BFLB_DYNAMIC_ALLOC_MEM) + +struct net_buf_pool iso_tx_pool; +struct net_buf_pool iso_rx_pool; +#if CONFIG_BT_ISO_TX_FRAG_COUNT > 0 +struct net_buf_pool iso_frag_pool; +#endif + +#endif + + +/* TODO: Allow more than one server? */ +static struct bt_iso_server *iso_server; +struct bt_conn iso_conns[CONFIG_BT_MAX_ISO_CONN]; + + +struct bt_iso_data_path { + /* Data Path direction */ + uint8_t dir; + /* Data Path ID */ + uint8_t pid; + /* Data Path param reference */ + struct bt_iso_chan_path *path; +}; + + +struct net_buf *bt_iso_get_rx(uint32_t timeout) +{ + struct net_buf *buf = net_buf_alloc(&iso_rx_pool, timeout); + + if (buf) { + net_buf_reserve(buf, BT_BUF_RESERVE); + bt_buf_set_type(buf, BT_BUF_ISO_IN); + } + + return buf; +} + +void hci_iso(struct net_buf *buf) +{ + struct bt_hci_iso_hdr *hdr; + uint16_t handle, len; + struct bt_conn *conn; + uint8_t flags; + + BT_DBG("buf %p", buf); + + BT_ASSERT(buf->len >= sizeof(*hdr)); + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + len = sys_le16_to_cpu(hdr->len); + handle = sys_le16_to_cpu(hdr->handle); + flags = bt_iso_flags(handle); + + iso(buf)->handle = bt_iso_handle(handle); + iso(buf)->index = BT_CONN_ID_INVALID; + + BT_DBG("handle %u len %u flags %u", iso(buf)->handle, len, flags); + + if (buf->len != len) { + BT_ERR("ISO data length mismatch (%u != %u)", buf->len, len); + net_buf_unref(buf); + return; + } + + conn = bt_conn_lookup_handle(iso(buf)->handle); + if (!conn) { + BT_ERR("Unable to find conn for handle %u", iso(buf)->handle); + net_buf_unref(buf); + return; + } + + iso(buf)->index = bt_conn_index(conn); + + bt_conn_recv(conn, buf, flags); + bt_conn_unref(conn); +} + +void hci_le_cis_estabilished(struct net_buf *buf) +{ + struct bt_hci_evt_le_cis_established *evt = (void *)buf->data; + uint16_t handle = sys_le16_to_cpu(evt->conn_handle); + struct bt_conn *conn; + + BT_DBG("status %u handle %u", evt->status, handle); + + /* ISO connection handles are already assigned at this point */ + conn = bt_conn_lookup_handle(handle); + if (!conn) { + BT_ERR("No connection found for handle %u", handle); + return; + } + + __ASSERT(conn->type == BT_CONN_TYPE_ISO, "Invalid connection type"); + + if (!evt->status) { + /* TODO: Add CIG sync delay */ + bt_conn_set_state(conn, BT_CONN_CONNECTED); + bt_conn_unref(conn); + return; + } + + conn->err = evt->status; + bt_iso_disconnected(conn); + bt_conn_unref(conn); +} + +int hci_le_reject_cis(uint16_t handle, uint8_t reason) +{ + struct bt_hci_cp_le_reject_cis *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REJECT_CIS, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + cp->reason = reason; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REJECT_CIS, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +int hci_le_accept_cis(uint16_t handle) +{ + struct bt_hci_cp_le_accept_cis *cp; + struct net_buf *buf; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ACCEPT_CIS, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(handle); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ACCEPT_CIS, buf, NULL); + if (err) { + return err; + } + + return 0; +} + +void hci_le_cis_req(struct net_buf *buf) +{ + struct bt_hci_evt_le_cis_req *evt = (void *)buf->data; + uint16_t acl_handle = sys_le16_to_cpu(evt->acl_handle); + uint16_t cis_handle = sys_le16_to_cpu(evt->cis_handle); + struct bt_conn *conn, *iso; + int err; + + BT_DBG("acl_handle %u cis_handle %u cig_id %u cis %u", + acl_handle, cis_handle, evt->cig_id, evt->cis_id); + + /* Lookup existing connection with same handle */ + iso = bt_conn_lookup_handle(cis_handle); + if (iso) { + BT_ERR("Invalid ISO handle %u", cis_handle); + hci_le_reject_cis(cis_handle, BT_HCI_ERR_CONN_LIMIT_EXCEEDED); + bt_conn_unref(iso); + return; + } + + /* Lookup ACL connection to attach */ + conn = bt_conn_lookup_handle(acl_handle); + if (!conn) { + BT_ERR("Invalid ACL handle %u", acl_handle); + hci_le_reject_cis(cis_handle, BT_HCI_ERR_UNKNOWN_CONN_ID); + return; + } + + /* Add ISO connection */ + iso = bt_conn_add_iso(conn); + + bt_conn_unref(conn); + + if (!iso) { + BT_ERR("Could not create and add ISO to conn %u", acl_handle); + hci_le_reject_cis(cis_handle, + BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + /* Request application to accept */ + err = bt_iso_accept(iso); + if (err) { + BT_DBG("App rejected ISO %d", err); + bt_iso_cleanup(iso); + hci_le_reject_cis(cis_handle, + BT_HCI_ERR_INSUFFICIENT_RESOURCES); + return; + } + + iso->handle = cis_handle; + iso->role = BT_HCI_ROLE_SLAVE; + bt_conn_set_state(iso, BT_CONN_CONNECT); + + hci_le_accept_cis(cis_handle); +} + +int hci_le_remove_cig(uint8_t cig_id) +{ + struct bt_hci_cp_le_remove_cig *req; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_CIG, sizeof(*req)); + if (!buf) { + return -ENOBUFS; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->cig_id = cig_id; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_CIG, buf, NULL); +} + +void bt_iso_cleanup(struct bt_conn *conn) +{ + int i; + + bt_conn_unref(conn->iso.acl); + conn->iso.acl = NULL; + + /* Check if conn is last of CIG */ + for (i = 0; i < CONFIG_BT_MAX_ISO_CONN; i++) { + if (conn == &iso_conns[i]) { + continue; + } + + if (atomic_get(&iso_conns[i].ref) && + iso_conns[i].iso.cig_id == conn->iso.cig_id) { + break; + } + } + + if (i == CONFIG_BT_MAX_ISO_CONN) { + hci_le_remove_cig(conn->iso.cig_id); + } + + bt_conn_unref(conn); + +} + +struct bt_conn *iso_new(void) +{ + return iso_conn_new(iso_conns, ARRAY_SIZE(iso_conns)); +} + +struct bt_conn *bt_conn_add_iso(struct bt_conn *acl) +{ + struct bt_conn *conn = iso_new(); + + if (!conn) { + return NULL; + } + + conn->iso.acl = bt_conn_ref(acl); + conn->type = BT_CONN_TYPE_ISO; + sys_slist_init(&conn->channels); + + return conn; +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, + k_timeout_t timeout, + const char *func, int line) +#else +struct net_buf *bt_iso_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, uint32_t timeout) +#endif +{ + if (!pool) { + pool = &iso_tx_pool; + } + + reserve += sizeof(struct bt_hci_iso_data_hdr); + +#if defined(CONFIG_NET_BUF_LOG) + return bt_conn_create_pdu_timeout_debug(pool, reserve, timeout, func, + line); +#else + return bt_conn_create_pdu_timeout(pool, reserve, timeout); +#endif +} + +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_frag_timeout_debug(size_t reserve, + k_timeout_t timeout, + const char *func, int line) +#else +struct net_buf *bt_iso_create_frag_timeout(size_t reserve, uint32_t timeout) +#endif +{ + struct net_buf_pool *pool = NULL; + +#if CONFIG_BT_L2CAP_TX_FRAG_COUNT > 0 + pool = &iso_frag_pool; +#endif + +#if defined(CONFIG_NET_BUF_LOG) + return bt_conn_create_pdu_timeout_debug(pool, reserve, timeout, func, + line); +#else + return bt_conn_create_pdu_timeout(pool, reserve, timeout); +#endif +} + +static struct net_buf *hci_le_set_cig_params(struct bt_iso_create_param *param) +{ + struct bt_hci_cis_params *cis; + struct bt_hci_cp_le_set_cig_params *req; + struct net_buf *buf; + struct net_buf *rsp; + int i, err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_CIG_PARAMS, + sizeof(*req) + sizeof(*cis) * param->num_conns); + if (!buf) { + return NULL; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->cig_id = param->conns[0]->iso.cig_id; + sys_put_le24(param->chans[0]->qos->interval, req->m_interval); + sys_put_le24(param->chans[0]->qos->interval, req->s_interval); + req->sca = param->chans[0]->qos->sca; + req->packing = param->chans[0]->qos->packing; + req->framing = param->chans[0]->qos->framing; + req->m_latency = sys_cpu_to_le16(param->chans[0]->qos->latency); + req->s_latency = sys_cpu_to_le16(param->chans[0]->qos->latency); + req->num_cis = param->num_conns; + + /* Program the cis parameters */ + for (i = 0; i < param->num_conns; i++) { + cis = net_buf_add(buf, sizeof(*cis)); + + memset(cis, 0, sizeof(*cis)); + + cis->cis_id = param->conns[i]->iso.cis_id; + + switch (param->chans[i]->qos->dir) { + case BT_ISO_CHAN_QOS_IN: + cis->m_sdu = param->chans[i]->qos->sdu; + break; + case BT_ISO_CHAN_QOS_OUT: + cis->s_sdu = param->chans[i]->qos->sdu; + break; + case BT_ISO_CHAN_QOS_INOUT: + cis->m_sdu = param->chans[i]->qos->sdu; + cis->s_sdu = param->chans[i]->qos->sdu; + break; + } + + cis->m_phy = param->chans[i]->qos->phy; + cis->s_phy = param->chans[i]->qos->phy; + cis->m_rtn = param->chans[i]->qos->rtn; + cis->s_rtn = param->chans[i]->qos->rtn; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_CIG_PARAMS, buf, &rsp); + if (err) { + return NULL; + } + + return rsp; +} + +int bt_conn_bind_iso(struct bt_iso_create_param *param) +{ + struct bt_conn *conn; + struct net_buf *rsp; + struct bt_hci_rp_le_set_cig_params *cig_rsp; + int i, err; + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (!param->num_conns || param->num_conns > CONFIG_BT_MAX_ISO_CONN) { + return -EINVAL; + } + + /* Assign ISO connections to each LE connection */ + for (i = 0; i < param->num_conns; i++) { + conn = param->conns[i]; + + if (conn->type != BT_CONN_TYPE_LE) { + err = -EINVAL; + goto failed; + } + + conn = bt_conn_add_iso(conn); + if (!conn) { + err = -ENOMEM; + goto failed; + } + + conn->iso.cig_id = param->id; + conn->iso.cis_id = bt_conn_index(conn); + + param->conns[i] = conn; + } + + rsp = hci_le_set_cig_params(param); + if (!rsp) { + err = -EIO; + goto failed; + } + + cig_rsp = (void *)rsp->data; + + if (rsp->len < sizeof(cig_rsp) || + cig_rsp->num_handles != param->num_conns) { + BT_WARN("Unexpected response to hci_le_set_cig_params"); + err = -EIO; + net_buf_unref(rsp); + goto failed; + } + + for (i = 0; i < cig_rsp->num_handles; i++) { + /* Assign the connection handle */ + param->conns[i]->handle = cig_rsp->handle[i]; + } + + net_buf_unref(rsp); + + return 0; + +failed: + for (i = 0; i < param->num_conns; i++) { + conn = param->conns[i]; + + if (conn->type == BT_CONN_TYPE_ISO) { + bt_iso_cleanup(conn); + } + } + + return err; +} + +static int hci_le_create_cis(struct bt_conn **conn, uint8_t num_conns) +{ + struct bt_hci_cis *cis; + struct bt_hci_cp_le_create_cis *req; + struct net_buf *buf; + int i; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CIS, + sizeof(*req) + sizeof(*cis) * num_conns); + if (!buf) { + return -ENOBUFS; + } + + req = net_buf_add(buf, sizeof(*req)); + + memset(req, 0, sizeof(*req)); + + req->num_cis = num_conns; + + /* Program the cis parameters */ + for (i = 0; i < num_conns; i++) { + cis = net_buf_add(buf, sizeof(*cis)); + + memset(cis, 0, sizeof(*cis)); + + cis->cis_handle = sys_cpu_to_le16(conn[i]->handle); + cis->acl_handle = sys_cpu_to_le16(conn[i]->iso.acl->handle); + } + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CIS, buf, NULL); +} + +int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns) +{ + int i, err; + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_MASTER(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (num_conns > CONFIG_BT_MAX_ISO_CONN) { + return -EINVAL; + } + + for (i = 0; i < num_conns; i++) { + if (conns[i]->type != BT_CONN_TYPE_ISO) { + return -EINVAL; + } + } + + err = hci_le_create_cis(conns, num_conns); + if (err) { + return err; + } + + /* Set connection state */ + for (i = 0; i < num_conns; i++) { + bt_conn_set_state(conns[i], BT_CONN_CONNECT); + } + + return 0; +} + +static int hci_le_setup_iso_data_path(struct bt_conn *conn, + struct bt_iso_data_path *path) +{ + struct bt_hci_cp_le_setup_iso_path *cp; + struct bt_hci_rp_le_setup_iso_path *rp; + struct net_buf *buf, *rsp; + uint8_t *cc; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SETUP_ISO_PATH, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->path_dir = path->dir; + cp->path_id = path->pid; + cp->codec_id.coding_format = path->path->format; + cp->codec_id.company_id = sys_cpu_to_le16(path->path->cid); + cp->codec_id.vs_codec_id = sys_cpu_to_le16(path->path->vid); + sys_put_le24(path->path->delay, cp->controller_delay); + cp->codec_config_len = path->path->cc_len; + cc = net_buf_add(buf, cp->codec_config_len); + memcpy(cc, path->path->cc, cp->codec_config_len); + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SETUP_ISO_PATH, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + if (rp->status || (rp->handle != conn->handle)) { + err = -EIO; + } + + net_buf_unref(rsp); + + return err; +} + +static int hci_le_remove_iso_data_path(struct bt_conn *conn, uint8_t dir) +{ + struct bt_hci_cp_le_remove_iso_path *cp; + struct bt_hci_rp_le_remove_iso_path *rp; + struct net_buf *buf, *rsp; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_ISO_PATH, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = conn->handle; + cp->path_dir = dir; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_ISO_PATH, buf, &rsp); + if (err) { + return err; + } + + rp = (void *)rsp->data; + if (rp->status || (rp->handle != conn->handle)) { + err = -EIO; + } + + net_buf_unref(rsp); + + return err; +} + +static void bt_iso_chan_add(struct bt_conn *conn, struct bt_iso_chan *chan) +{ + /* Attach channel to the connection */ + sys_slist_append(&conn->channels, &chan->node); + chan->conn = conn; + + BT_DBG("conn %p chan %p", conn, chan); +} + +int bt_iso_accept(struct bt_conn *conn) +{ + struct bt_iso_chan *chan; + int err; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (!iso_server) { + return -ENOMEM; + } + + err = iso_server->accept(conn, &chan); + if (err < 0) { + BT_ERR("err %d", err); + return err; + } + + bt_iso_chan_add(conn, chan); + bt_iso_chan_set_state(chan, BT_ISO_BOUND); + + return 0; +} + +static int bt_iso_setup_data_path(struct bt_conn *conn) +{ + int err; + struct bt_iso_chan *chan; + struct bt_iso_chan_path path = {}; + struct bt_iso_data_path out_path = { + .dir = BT_HCI_DATAPATH_DIR_CTLR_TO_HOST }; + struct bt_iso_data_path in_path = { + .dir = BT_HCI_DATAPATH_DIR_HOST_TO_CTLR }; + + chan = SYS_SLIST_PEEK_HEAD_CONTAINER(&conn->channels, chan, node); + if (!chan) { + return -EINVAL; + } + + in_path.path = chan->path ? chan->path : &path; + out_path.path = chan->path ? chan->path : &path; + + switch (chan->qos->dir) { + case BT_ISO_CHAN_QOS_IN: + in_path.pid = in_path.path->pid; + out_path.pid = BT_ISO_DATA_PATH_DISABLED; + break; + case BT_ISO_CHAN_QOS_OUT: + in_path.pid = BT_ISO_DATA_PATH_DISABLED; + out_path.pid = out_path.path->pid; + break; + case BT_ISO_CHAN_QOS_INOUT: + in_path.pid = in_path.path->pid; + out_path.pid = out_path.path->pid; + break; + default: + return -EINVAL; + } + + err = hci_le_setup_iso_data_path(conn, &in_path); + if (err) { + return err; + } + + return hci_le_setup_iso_data_path(conn, &out_path); +} + +void bt_iso_connected(struct bt_conn *conn) +{ + struct bt_iso_chan *chan; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (bt_iso_setup_data_path(conn)) { + BT_ERR("Unable to setup data path"); + bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan->ops->connected) { + chan->ops->connected(chan); + } + + bt_iso_chan_set_state(chan, BT_ISO_CONNECTED); + } +} + +static void bt_iso_remove_data_path(struct bt_conn *conn) +{ + BT_DBG("%p", conn); + + /* Remove both directions */ + hci_le_remove_iso_data_path(conn, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST); + hci_le_remove_iso_data_path(conn, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR); +} + +void bt_iso_disconnected(struct bt_conn *conn) +{ + struct bt_iso_chan *chan, *next; + + __ASSERT_NO_MSG(conn->type == BT_CONN_TYPE_ISO); + + BT_DBG("%p", conn); + + if (sys_slist_is_empty(&conn->channels)) { + return; + } + + bt_iso_remove_data_path(conn); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) { + if (chan->ops->disconnected) { + chan->ops->disconnected(chan); + } + + if (chan->conn) { + bt_conn_unref(chan->conn); + chan->conn = NULL; + } + + bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); + } +} + +int bt_iso_server_register(struct bt_iso_server *server) +{ + __ASSERT_NO_MSG(server); + + /* Check if controller is ISO capable */ + if (!BT_FEAT_LE_CIS_SLAVE(bt_dev.le.features)) { + return -ENOTSUP; + } + + if (iso_server) { + return -EADDRINUSE; + } + + if (!server->accept) { + return -EINVAL; + } + + if (server->sec_level > BT_SECURITY_L3) { + return -EINVAL; + } else if (server->sec_level < BT_SECURITY_L1) { + /* Level 0 is only applicable for BR/EDR */ + server->sec_level = BT_SECURITY_L1; + } + + BT_DBG("%p", server); + + iso_server = server; + + return 0; +} + +#if defined(CONFIG_BT_AUDIO_DEBUG_ISO) +const char *bt_iso_chan_state_str(uint8_t state) +{ + switch (state) { + case BT_ISO_DISCONNECTED: + return "disconnected"; + case BT_ISO_BOUND: + return "bound"; + case BT_ISO_CONNECT: + return "connect"; + case BT_ISO_CONNECTED: + return "connected"; + case BT_ISO_DISCONNECT: + return "disconnect"; + default: + return "unknown"; + } +} + +void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state, + const char *func, int line) +{ + BT_DBG("chan %p conn %p %s -> %s", chan, chan->conn, + bt_iso_chan_state_str(chan->state), + bt_iso_chan_state_str(state)); + + /* check transitions validness */ + switch (state) { + case BT_ISO_DISCONNECTED: + /* regardless of old state always allows this state */ + break; + case BT_ISO_BOUND: + if (chan->state != BT_ISO_DISCONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_CONNECT: + if (chan->state != BT_ISO_BOUND) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_CONNECTED: + if (chan->state != BT_ISO_BOUND && + chan->state != BT_ISO_CONNECT) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_ISO_DISCONNECT: + if (chan->state != BT_ISO_CONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + default: + BT_ERR("%s()%d: unknown (%u) state was set", func, line, state); + return; + } + + chan->state = state; +} +#else +void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state) +{ + chan->state = state; +} +#endif /* CONFIG_BT_AUDIO_DEBUG_ISO */ + +void bt_iso_chan_remove(struct bt_conn *conn, struct bt_iso_chan *chan) +{ + struct bt_iso_chan *c; + sys_snode_t *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, c, node) { + if (c == chan) { + sys_slist_remove(&conn->channels, prev, &chan->node); + return; + } + + prev = &chan->node; + } +} + +int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns, + struct bt_iso_chan **chans) +{ + struct bt_iso_create_param param; + int i, err; + static uint8_t id; + + __ASSERT_NO_MSG(conns); + __ASSERT_NO_MSG(num_conns); + __ASSERT_NO_MSG(chans); + + memset(¶m, 0, sizeof(param)); + + param.id = id++; + param.num_conns = num_conns; + param.conns = conns; + param.chans = chans; + + err = bt_conn_bind_iso(¶m); + if (err) { + return err; + } + + /* Bind respective connection to channel */ + for (i = 0; i < num_conns; i++) { + bt_iso_chan_add(conns[i], chans[i]); + bt_iso_chan_set_state(chans[i], BT_ISO_BOUND); + } + + return 0; +} + +int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans) +{ + struct bt_conn *conns[CONFIG_BT_MAX_ISO_CONN]; + int i, err; + + __ASSERT_NO_MSG(chans); + __ASSERT_NO_MSG(num_chans); + + for (i = 0; i < num_chans; i++) { + if (!chans[i]->conn) { + return -ENOTCONN; + } + + conns[i] = chans[i]->conn; + } + + err = bt_conn_connect_iso(conns, num_chans); + if (err) { + return err; + } + + for (i = 0; i < num_chans; i++) { + bt_iso_chan_set_state(chans[i], BT_ISO_CONNECT); + } + + return 0; +} + +int bt_iso_chan_disconnect(struct bt_iso_chan *chan) +{ + __ASSERT_NO_MSG(chan); + + if (!chan->conn) { + return -ENOTCONN; + } + + if (chan->state == BT_ISO_BOUND) { + bt_iso_chan_set_state(chan, BT_ISO_DISCONNECTED); + bt_iso_chan_remove(chan->conn, chan); + bt_conn_unref(chan->conn); + chan->conn = NULL; + return 0; + } + + return bt_conn_disconnect(chan->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); +} + +void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags) +{ + struct bt_hci_iso_data_hdr *hdr; + struct bt_iso_chan *chan; + uint8_t pb, ts; + uint16_t len; + + pb = bt_iso_flags_pb(flags); + ts = bt_iso_flags_ts(flags); + + BT_DBG("handle %u len %u flags 0x%02x pb 0x%02x ts 0x%02x", + conn->handle, buf->len, flags, pb, ts); + + /* When the PB_Flag does not equal 0b00, the fields Time_Stamp, + * Packet_Sequence_Number, Packet_Status_Flag and ISO_SDU_Length + * are omitted from the HCI ISO Data packet. + */ + switch (pb) { + case BT_ISO_START: + case BT_ISO_SINGLE: + /* The ISO_Data_Load field contains either the first fragment + * of an SDU or a complete SDU. + */ + if (ts) { + struct bt_hci_iso_ts_data_hdr *ts_hdr; + + ts_hdr = net_buf_pull_mem(buf, sizeof(*ts_hdr)); + iso(buf)->ts = sys_le32_to_cpu(ts_hdr->ts); + + hdr = &ts_hdr->data; + } else { + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + /* TODO: Generate a timestamp? */ + iso(buf)->ts = 0x00000000; + } + + len = sys_le16_to_cpu(hdr->slen); + flags = bt_iso_pkt_flags(len); + len = bt_iso_pkt_len(len); + + /* TODO: Drop the packet if NOP? */ + + BT_DBG("%s, len %u total %u flags 0x%02x timestamp %u", + pb == BT_ISO_START ? "Start" : "Single", buf->len, len, + flags, iso(buf)->ts); + + if (conn->rx) { + BT_ERR("Unexpected ISO %s fragment", + pb == BT_ISO_START ? "Start" : "Single"); + bt_conn_reset_rx_state(conn); + } + + conn->rx = buf; + conn->rx_len = len - buf->len; + if (conn->rx_len) { + /* if conn->rx_len then package is longer than the + * buf->len and cannot fit in a SINGLE package + */ + if (pb == BT_ISO_SINGLE) { + BT_ERR("Unexpected ISO single fragment"); + bt_conn_reset_rx_state(conn); + } + return; + } + break; + + case BT_ISO_CONT: + /* The ISO_Data_Load field contains a continuation fragment of + * an SDU. + */ + if (!conn->rx) { + BT_ERR("Unexpected ISO continuation fragment"); + net_buf_unref(buf); + return; + } + + BT_DBG("Cont, len %u rx_len %u", buf->len, conn->rx_len); + + if (buf->len > net_buf_tailroom(conn->rx)) { + BT_ERR("Not enough buffer space for ISO data"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + net_buf_add_mem(conn->rx, buf->data, buf->len); + conn->rx_len -= buf->len; + net_buf_unref(buf); + return; + + case BT_ISO_END: + /* The ISO_Data_Load field contains the last fragment of an + * SDU. + */ + BT_DBG("End, len %u rx_len %u", buf->len, conn->rx_len); + + if (!conn->rx) { + BT_ERR("Unexpected ISO end fragment"); + net_buf_unref(buf); + return; + } + + if (buf->len > net_buf_tailroom(conn->rx)) { + BT_ERR("Not enough buffer space for ISO data"); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + net_buf_add_mem(conn->rx, buf->data, buf->len); + conn->rx_len -= buf->len; + net_buf_unref(buf); + + break; + default: + BT_ERR("Unexpected ISO pb flags (0x%02x)", pb); + bt_conn_reset_rx_state(conn); + net_buf_unref(buf); + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan->ops->recv) { + chan->ops->recv(chan, conn->rx); + } + } + + bt_conn_reset_rx_state(conn); +} + +int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf) +{ + struct bt_hci_iso_data_hdr *hdr; + static uint16_t sn; + + __ASSERT_NO_MSG(chan); + __ASSERT_NO_MSG(buf); + + BT_DBG("chan %p len %zu", chan, net_buf_frags_len(buf)); + + if (!chan->conn) { + BT_DBG("Not connected"); + return -ENOTCONN; + } + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->sn = sys_cpu_to_le16(sn++); + hdr->slen = sys_cpu_to_le16(bt_iso_pkt_len_pack(net_buf_frags_len(buf) + - sizeof(*hdr), + BT_ISO_DATA_VALID)); + + return bt_conn_send(chan->conn, buf); +} diff --git a/components/ble/ble_stack/host/iso_internal.h b/components/ble/ble_stack/host/iso_internal.h new file mode 100644 index 00000000..694d8907 --- /dev/null +++ b/components/ble/ble_stack/host/iso_internal.h @@ -0,0 +1,110 @@ +/** @file + * @brief Internal APIs for Bluetooth ISO handling. + */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#define BT_ISO_DATA_PATH_DISABLED 0xFF + +struct iso_data { + /** BT_BUF_ISO_IN */ + uint8_t type; + + /* Index into the bt_conn storage array */ + uint8_t index; + + /** ISO connection handle */ + uint16_t handle; + + /** ISO timestamp */ + uint32_t ts; +}; + +#define iso(buf) ((struct iso_data *)net_buf_user_data(buf)) + +#if defined(CONFIG_BT_MAX_ISO_CONN) +extern struct bt_conn iso_conns[CONFIG_BT_MAX_ISO_CONN]; +#endif + +/* Process ISO buffer */ +void hci_iso(struct net_buf *buf); + +/* Allocates RX buffer */ +struct net_buf *bt_iso_get_rx(uint32_t timeout); + +/* Create new ISO connecting */ +struct bt_conn *iso_new(void); + +/* Process CIS Estabilished event */ +void hci_le_cis_estabilished(struct net_buf *buf); + +/* Process CIS Request event */ +void hci_le_cis_req(struct net_buf *buf); + +/* Notify ISO channels of a new connection */ +int bt_iso_accept(struct bt_conn *conn); + +/* Notify ISO channels of a new connection */ +void bt_iso_connected(struct bt_conn *conn); + +/* Notify ISO channels of a disconnect event */ +void bt_iso_disconnected(struct bt_conn *conn); + +/* Allocate ISO PDU */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_pdu_timeout_debug(struct net_buf_pool *pool, + size_t reserve, + k_timeout_t timeout, + const char *func, int line); +#define bt_iso_create_pdu_timeout(_pool, _reserve, _timeout) \ + bt_iso_create_pdu_timeout_debug(_pool, _reserve, _timeout, \ + __func__, __LINE__) + +#define bt_iso_create_pdu(_pool, _reserve) \ + bt_iso_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \ + __func__, __line__) +#else +struct net_buf *bt_iso_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, uint32_t timeout); + +#define bt_iso_create_pdu(_pool, _reserve) \ + bt_iso_create_pdu_timeout(_pool, _reserve, K_FOREVER) +#endif + +/* Allocate ISO Fragment */ +#if defined(CONFIG_NET_BUF_LOG) +struct net_buf *bt_iso_create_frag_timeout_debug(size_t reserve, + k_timeout_t timeout, + const char *func, int line); + +#define bt_iso_create_frag_timeout(_reserve, _timeout) \ + bt_iso_create_frag_timeout_debug(_reserve, _timeout, \ + __func__, __LINE__) + +#define bt_iso_create_frag(_reserve) \ + bt_iso_create_frag_timeout_debug(_reserve, K_FOREVER, \ + __func__, __LINE__) +#else +struct net_buf *bt_iso_create_frag_timeout(size_t reserve, uint32_t timeout); + +#define bt_iso_create_frag(_reserve) \ + bt_iso_create_frag_timeout(_reserve, K_FOREVER) +#endif + +#if defined(CONFIG_BT_AUDIO_DEBUG_ISO) +void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state, + const char *func, int line); +#define bt_iso_chan_set_state(_chan, _state) \ + bt_iso_chan_set_state_debug(_chan, _state, __func__, __LINE__) +#else +void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state); +#endif /* CONFIG_BT_AUDIO_DEBUG_ISO */ + +/* Process incoming data for a connection */ +void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags); diff --git a/components/ble/ble_stack/host/keys.c b/components/ble/ble_stack/host/keys.c new file mode 100644 index 00000000..daf77dce --- /dev/null +++ b/components/ble/ble_stack/host/keys.c @@ -0,0 +1,507 @@ +/* keys.c - Bluetooth key handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_KEYS) +#include "log.h" + +#include "rpa.h" +#include "gatt_internal.h" +#include "hci_core.h" +#include "smp.h" +#include "settings.h" +#include "keys.h" +#if defined(BFLB_BLE) +#if defined(CONFIG_BT_SETTINGS) +#include "easyflash.h" +#endif +#endif + +static struct bt_keys key_pool[CONFIG_BT_MAX_PAIRED]; + +#define BT_KEYS_STORAGE_LEN_COMPAT (BT_KEYS_STORAGE_LEN - sizeof(uint32_t)) + +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) +static u32_t aging_counter_val; +static struct bt_keys *last_keys_updated; +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + +struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr) +{ + struct bt_keys *keys; + int i; + size_t first_free_slot = ARRAY_SIZE(key_pool); + + BT_DBG("%s", bt_addr_le_str(addr)); + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + keys = &key_pool[i]; + + if (keys->id == id && !bt_addr_le_cmp(&keys->addr, addr)) { + return keys; + } + + if (first_free_slot == ARRAY_SIZE(key_pool) && + (!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY) || + !keys->enc_size)) { + first_free_slot = i; + } + } + +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) + if (first_free_slot == ARRAY_SIZE(key_pool)) { + struct bt_keys *oldest = &key_pool[0]; + + for (i = 1; i < ARRAY_SIZE(key_pool); i++) { + struct bt_keys *current = &key_pool[i]; + + if (current->aging_counter < oldest->aging_counter) { + oldest = current; + } + } + + bt_unpair(oldest->id, &oldest->addr); + if (!bt_addr_le_cmp(&oldest->addr, BT_ADDR_LE_ANY)) { + first_free_slot = oldest - &key_pool[0]; + } + } + +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + if (first_free_slot < ARRAY_SIZE(key_pool)) { + keys = &key_pool[first_free_slot]; + keys->id = id; + bt_addr_le_copy(&keys->addr, addr); +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) + keys->aging_counter = ++aging_counter_val; + last_keys_updated = keys; +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + BT_DBG("created %p for %s", keys, bt_addr_le_str(addr)); + return keys; + } + + BT_DBG("unable to create keys for %s", bt_addr_le_str(addr)); + + return NULL; +} + +void bt_foreach_bond(u8_t id, void (*func)(const struct bt_bond_info *info, + void *user_data), + void *user_data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + struct bt_keys *keys = &key_pool[i]; + + if (keys->keys && keys->id == id) { + struct bt_bond_info info; + + bt_addr_le_copy(&info.addr, &keys->addr); + func(&info, user_data); + } + } +} + +void bt_keys_foreach(int type, void (*func)(struct bt_keys *keys, void *data), + void *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + if ((key_pool[i].keys & type)) { + func(&key_pool[i], data); + } + } +} + +struct bt_keys *bt_keys_find(int type, u8_t id, const bt_addr_le_t *addr) +{ + int i; + + BT_DBG("type %d %s", type, bt_addr_le_str(addr)); + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + if ((key_pool[i].keys & type) && key_pool[i].id == id && + !bt_addr_le_cmp(&key_pool[i].addr, addr)) { + return &key_pool[i]; + } + } + + return NULL; +} + +struct bt_keys *bt_keys_get_type(int type, u8_t id, const bt_addr_le_t *addr) +{ + struct bt_keys *keys; + + BT_DBG("type %d %s", type, bt_addr_le_str(addr)); + + keys = bt_keys_find(type, id, addr); + if (keys) { + return keys; + } + + keys = bt_keys_get_addr(id, addr); + if (!keys) { + return NULL; + } + + bt_keys_add_type(keys, type); + + return keys; +} + +struct bt_keys *bt_keys_find_irk(u8_t id, const bt_addr_le_t *addr) +{ + int i; + + BT_DBG("%s", bt_addr_le_str(addr)); + + if (!bt_addr_le_is_rpa(addr)) { + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + if (!(key_pool[i].keys & BT_KEYS_IRK)) { + continue; + } + + if (key_pool[i].id == id && + !bt_addr_cmp(&addr->a, &key_pool[i].irk.rpa)) { + BT_DBG("cached RPA %s for %s", + bt_addr_str(&key_pool[i].irk.rpa), + bt_addr_le_str(&key_pool[i].addr)); + return &key_pool[i]; + } + } + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + if (!(key_pool[i].keys & BT_KEYS_IRK)) { + continue; + } + + if (key_pool[i].id != id) { + continue; + } + + if (bt_rpa_irk_matches(key_pool[i].irk.val, &addr->a)) { + BT_DBG("RPA %s matches %s", + bt_addr_str(&key_pool[i].irk.rpa), + bt_addr_le_str(&key_pool[i].addr)); + + bt_addr_copy(&key_pool[i].irk.rpa, &addr->a); + + return &key_pool[i]; + } + } + + BT_DBG("No IRK for %s", bt_addr_le_str(addr)); + + return NULL; +} + +struct bt_keys *bt_keys_find_addr(u8_t id, const bt_addr_le_t *addr) +{ + int i; + + BT_DBG("%s", bt_addr_le_str(addr)); + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + if (key_pool[i].id == id && + !bt_addr_le_cmp(&key_pool[i].addr, addr)) { + return &key_pool[i]; + } + } + + return NULL; +} + +#if defined(CONFIG_BLE_AT_CMD) + bt_addr_le_t *bt_get_keys_address(u8_t id) +{ + bt_addr_le_t addr; + + memset(&addr,0,sizeof(bt_addr_le_t)); + if(id < ARRAY_SIZE(key_pool)){ + if (bt_addr_le_cmp(&key_pool[id].addr, &addr)) { + return &key_pool[id].addr; + } + } + + return NULL; +} +#endif + +void bt_keys_add_type(struct bt_keys *keys, int type) +{ + keys->keys |= type; +} + +void bt_keys_clear(struct bt_keys *keys) +{ +#if defined(BFLB_BLE) + if (keys->keys & BT_KEYS_IRK) { + bt_id_del(keys); + } + + memset(keys, 0, sizeof(*keys)); + + #if defined (CONFIG_BT_SETTINGS) + ef_del_env(NV_KEY_POOL); + #endif +#else + BT_DBG("%s (keys 0x%04x)", bt_addr_le_str(&keys->addr), keys->keys); + + if (keys->keys & BT_KEYS_IRK) { + bt_id_del(keys); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + char key[BT_SETTINGS_KEY_MAX]; + + /* Delete stored keys from flash */ + if (keys->id) { + char id[4]; + + u8_to_dec(id, sizeof(id), keys->id); + bt_settings_encode_key(key, sizeof(key), "keys", + &keys->addr, id); + } else { + bt_settings_encode_key(key, sizeof(key), "keys", + &keys->addr, NULL); + } + + BT_DBG("Deleting key %s", log_strdup(key)); + settings_delete(key); + } + + (void)memset(keys, 0, sizeof(*keys)); + #endif +} + +static void keys_clear_id(struct bt_keys *keys, void *data) +{ + u8_t *id = data; + + if (*id == keys->id) { + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_gatt_clear(*id, &keys->addr); + } + + bt_keys_clear(keys); + } +} + +void bt_keys_clear_all(u8_t id) +{ + bt_keys_foreach(BT_KEYS_ALL, keys_clear_id, &id); +} + +#if defined(CONFIG_BT_SETTINGS) +int bt_keys_store(struct bt_keys *keys) +{ +#if defined(BFLB_BLE) + int err; + err = bt_settings_set_bin(NV_KEY_POOL, (const u8_t *)&key_pool[0], sizeof(key_pool)); + return err; +#else + char val[BT_SETTINGS_SIZE(BT_KEYS_STORAGE_LEN)]; + char key[BT_SETTINGS_KEY_MAX]; + char *str; + int err; + + str = settings_str_from_bytes(keys->storage_start, BT_KEYS_STORAGE_LEN, + val, sizeof(val)); + if (!str) { + BT_ERR("Unable to encode bt_keys as value"); + return -EINVAL; + } + + if (keys->id) { + char id[4]; + + u8_to_dec(id, sizeof(id), keys->id); + bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr, + id); + } else { + bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr, + NULL); + } + + err = settings_save_one(key, keys->storage_start, BT_KEYS_STORAGE_LEN); + if (err) { + BT_ERR("Failed to save keys (err %d)", err); + return err; + } + + BT_DBG("Stored keys for %s (%s)", bt_addr_le_str(&keys->addr), + log_strdup(key)); + + return 0; + #endif //BFLB_BLE +} + +#if !defined(BFLB_BLE) +static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + struct bt_keys *keys; + bt_addr_le_t addr; + u8_t id; + size_t len; + int err; + char val[BT_KEYS_STORAGE_LEN]; + const char *next; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + len = read_cb(cb_arg, val, sizeof(val)); + if (len < 0) { + BT_ERR("Failed to read value (err %zu)", len); + return -EINVAL; + } + + BT_DBG("name %s val %s", log_strdup(name), + (len) ? bt_hex(val, sizeof(val)) : "(null)"); + + err = bt_settings_decode_key(name, &addr); + if (err) { + BT_ERR("Unable to decode address %s", name); + return -EINVAL; + } + + settings_name_next(name, &next); + + if (!next) { + id = BT_ID_DEFAULT; + } else { + id = strtol(next, NULL, 10); + } + + if (!len) { + keys = bt_keys_find(BT_KEYS_ALL, id, &addr); + if (keys) { + (void)memset(keys, 0, sizeof(*keys)); + BT_DBG("Cleared keys for %s", bt_addr_le_str(&addr)); + } else { + BT_WARN("Unable to find deleted keys for %s", + bt_addr_le_str(&addr)); + } + + return 0; + } + + keys = bt_keys_get_addr(id, &addr); + if (!keys) { + BT_ERR("Failed to allocate keys for %s", bt_addr_le_str(&addr)); + return -ENOMEM; + } + if (len != BT_KEYS_STORAGE_LEN) { + do { + /* Load shorter structure for compatibility with old + * records format with no counter. + */ + if (IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) && + len == BT_KEYS_STORAGE_LEN_COMPAT) { + BT_WARN("Keys for %s have no aging counter", + bt_addr_le_str(&addr)); + memcpy(keys->storage_start, val, len); + continue; + } + + BT_ERR("Invalid key length %zu != %zu", len, + BT_KEYS_STORAGE_LEN); + bt_keys_clear(keys); + + return -EINVAL; + } while (0); + } else { + memcpy(keys->storage_start, val, len); + } + + BT_DBG("Successfully restored keys for %s", bt_addr_le_str(&addr)); +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) + if (aging_counter_val < keys->aging_counter) { + aging_counter_val = keys->aging_counter; + } +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + return 0; +} +#endif //!(BFLB_BLE) + +static void id_add(struct bt_keys *keys, void *user_data) +{ + bt_id_add(keys); +} + +#if defined(BFLB_BLE) +int keys_commit(void) +#else +static int keys_commit(void) +#endif +{ + BT_DBG(""); + + /* We do this in commit() rather than add() since add() may get + * called multiple times for the same address, especially if + * the keys were already removed. + */ + bt_keys_foreach(BT_KEYS_IRK, id_add, NULL); + + return 0; +} + +//SETTINGS_STATIC_HANDLER_DEFINE(bt_keys, "bt/keys", NULL, keys_set, keys_commit, +// NULL); + +#if defined(BFLB_BLE) +int bt_keys_load(void) +{ + return bt_settings_get_bin(NV_KEY_POOL, (u8_t *)&key_pool[0], sizeof(key_pool), NULL); +} +#endif + +#endif /* CONFIG_BT_SETTINGS */ + +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) +void bt_keys_update_usage(u8_t id, const bt_addr_le_t *addr) +{ + struct bt_keys *keys = bt_keys_find_addr(id, addr); + + if (!keys) { + return; + } + + if (last_keys_updated == keys) { + return; + } + + keys->aging_counter = ++aging_counter_val; + last_keys_updated = keys; + + BT_DBG("Aging counter for %s is set to %u", bt_addr_le_str(addr), + keys->aging_counter); + + if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) { + bt_keys_store(keys); + } +} + +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ diff --git a/components/ble/ble_stack/host/keys.h b/components/ble/ble_stack/host/keys.h new file mode 100644 index 00000000..7b11f146 --- /dev/null +++ b/components/ble/ble_stack/host/keys.h @@ -0,0 +1,123 @@ +/* keys.h - Bluetooth key handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +enum { + BT_KEYS_SLAVE_LTK = BIT(0), + BT_KEYS_IRK = BIT(1), + BT_KEYS_LTK = BIT(2), + BT_KEYS_LOCAL_CSRK = BIT(3), + BT_KEYS_REMOTE_CSRK = BIT(4), + BT_KEYS_LTK_P256 = BIT(5), + + BT_KEYS_ALL = (BT_KEYS_SLAVE_LTK | BT_KEYS_IRK | \ + BT_KEYS_LTK | BT_KEYS_LOCAL_CSRK | \ + BT_KEYS_REMOTE_CSRK | BT_KEYS_LTK_P256), +}; + +enum { + BT_KEYS_AUTHENTICATED = BIT(0), + BT_KEYS_DEBUG = BIT(1), + BT_KEYS_ID_PENDING_ADD = BIT(2), + BT_KEYS_ID_PENDING_DEL = BIT(3), + BT_KEYS_SC = BIT(4), +}; + +struct bt_ltk { + u8_t rand[8]; + u8_t ediv[2]; + u8_t val[16]; +}; + +struct bt_irk { + u8_t val[16]; + bt_addr_t rpa; +}; + +struct bt_csrk { + u8_t val[16]; + u32_t cnt; +}; + +struct bt_keys { + u8_t id; + bt_addr_le_t addr; +#if !defined(BFLB_BLE) + u8_t storage_start[0]; +#endif + u8_t enc_size; + u8_t flags; + u16_t keys; + struct bt_ltk ltk; + struct bt_irk irk; +#if defined(CONFIG_BT_SIGNING) + struct bt_csrk local_csrk; + struct bt_csrk remote_csrk; +#endif /* BT_SIGNING */ +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + struct bt_ltk slave_ltk; +#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */ +#if (defined(CONFIG_BT_KEYS_OVERWRITE_OLDEST)) + u32_t aging_counter; +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ +}; + +#if !defined(BFLB_BLE) +#define BT_KEYS_STORAGE_LEN (sizeof(struct bt_keys) - \ + offsetof(struct bt_keys, storage_start)) +#endif + +void bt_keys_foreach(int type, void (*func)(struct bt_keys *keys, void *data), + void *data); + +struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr); +struct bt_keys *bt_keys_get_type(int type, u8_t id, const bt_addr_le_t *addr); +struct bt_keys *bt_keys_find(int type, u8_t id, const bt_addr_le_t *addr); +struct bt_keys *bt_keys_find_irk(u8_t id, const bt_addr_le_t *addr); +struct bt_keys *bt_keys_find_addr(u8_t id, const bt_addr_le_t *addr); +#if defined(CONFIG_BLE_AT_CMD) +bt_addr_le_t *bt_get_keys_address(u8_t id); +#endif + +void bt_keys_add_type(struct bt_keys *keys, int type); +void bt_keys_clear(struct bt_keys *keys); +void bt_keys_clear_all(u8_t id); +#if defined(BFLB_BLE) +int keys_commit(void); +int bt_keys_load(void); +#endif + +#if defined(CONFIG_BT_SETTINGS) +int bt_keys_store(struct bt_keys *keys); +#else +static inline int bt_keys_store(struct bt_keys *keys) +{ + return 0; +} +#endif + +enum { + BT_LINK_KEY_AUTHENTICATED = BIT(0), + BT_LINK_KEY_DEBUG = BIT(1), + BT_LINK_KEY_SC = BIT(2), +}; + +struct bt_keys_link_key { + bt_addr_t addr; + u8_t flags; + u8_t val[16]; +}; + +struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr); +struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr); +void bt_keys_link_key_clear(struct bt_keys_link_key *link_key); +void bt_keys_link_key_clear_addr(const bt_addr_t *addr); + +/* This function is used to signal that the key has been used for paring */ +/* It updates the aging counter and saves it to flash if configuration option */ +/* BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING is enabled */ +void bt_keys_update_usage(u8_t id, const bt_addr_le_t *addr); diff --git a/components/ble/ble_stack/host/keys_br.c b/components/ble/ble_stack/host/keys_br.c new file mode 100644 index 00000000..e2548007 --- /dev/null +++ b/components/ble/ble_stack/host/keys_br.c @@ -0,0 +1,241 @@ +/* keys_br.c - Bluetooth BR/EDR key handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_KEYS) +#define LOG_MODULE_NAME bt_keys_br +#include "log.h" + +#include "hci_core.h" +#include "settings.h" +#include "keys.h" + +static struct bt_keys_link_key key_pool[CONFIG_BT_MAX_PAIRED]; + +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) +static uint32_t aging_counter_val; +static struct bt_keys_link_key *last_keys_updated; +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + +struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr) +{ + struct bt_keys_link_key *key; + int i; + + BT_DBG("%s", bt_addr_str(addr)); + + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + key = &key_pool[i]; + + if (!bt_addr_cmp(&key->addr, addr)) { + return key; + } + } + + return NULL; +} + +struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr) +{ + struct bt_keys_link_key *key; + + key = bt_keys_find_link_key(addr); + if (key) { + return key; + } + + key = bt_keys_find_link_key(BT_ADDR_ANY); +#if 0//IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) //MBHJ + if (!key) { + int i; + + key = &key_pool[0]; + for (i = 1; i < ARRAY_SIZE(key_pool); i++) { + struct bt_keys_link_key *current = &key_pool[i]; + + if (current->aging_counter < key->aging_counter) { + key = current; + } + } + + if (key) { + bt_keys_link_key_clear(key); + } + } +#endif + + if (key) { + bt_addr_copy(&key->addr, addr); +#if 0 //IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) //MBHJ + key->aging_counter = ++aging_counter_val; + last_keys_updated = key; +#endif + BT_DBG("created %p for %s", key, bt_addr_str(addr)); + return key; + } + + BT_DBG("unable to create keys for %s", bt_addr_str(addr)); + + return NULL; +} + +void bt_keys_link_key_clear(struct bt_keys_link_key *link_key) +{ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + char key[BT_SETTINGS_KEY_MAX]; + bt_addr_le_t le_addr; + + le_addr.type = BT_ADDR_LE_PUBLIC; + bt_addr_copy(&le_addr.a, &link_key->addr); + bt_settings_encode_key(key, sizeof(key), "link_key", + &le_addr, NULL); + settings_delete(key); + } + + BT_DBG("%s", bt_addr_str(&link_key->addr)); + (void)memset(link_key, 0, sizeof(*link_key)); +} + +void bt_keys_link_key_clear_addr(const bt_addr_t *addr) +{ + int i; + struct bt_keys_link_key *key; + + if (!addr) { + for (i = 0; i < ARRAY_SIZE(key_pool); i++) { + key = &key_pool[i]; + bt_keys_link_key_clear(key); + } + return; + } + + key = bt_keys_find_link_key(addr); + if (key) { + bt_keys_link_key_clear(key); + } +} + +void bt_keys_link_key_store(struct bt_keys_link_key *link_key) +{ +#if 0 //MBHJ + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + int err; + char key[BT_SETTINGS_KEY_MAX]; + bt_addr_le_t le_addr; + + le_addr.type = BT_ADDR_LE_PUBLIC; + bt_addr_copy(&le_addr.a, &link_key->addr); + bt_settings_encode_key(key, sizeof(key), "link_key", + &le_addr, NULL); + + err = settings_save_one(key, link_key->storage_start, + BT_KEYS_LINK_KEY_STORAGE_LEN); + if (err) { + BT_ERR("Failed to svae link key (err %d)", err); + } + } +#endif +} + +#if defined(CONFIG_BT_SETTINGS) + +static int link_key_set(const char *name, size_t len_rd, + settings_read_cb read_cb, void *cb_arg) +{ + int err; + ssize_t len; + bt_addr_le_t le_addr; + struct bt_keys_link_key *link_key; + char val[BT_KEYS_LINK_KEY_STORAGE_LEN]; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -EINVAL; + } + + len = read_cb(cb_arg, val, sizeof(val)); + if (len < 0) { + BT_ERR("Failed to read value (err %zu)", len); + return -EINVAL; + } + + BT_DBG("name %s val %s", log_strdup(name), + len ? bt_hex(val, sizeof(val)) : "(null)"); + + err = bt_settings_decode_key(name, &le_addr); + if (err) { + BT_ERR("Unable to decode address %s", name); + return -EINVAL; + } + + link_key = bt_keys_get_link_key(&le_addr.a); + if (len != BT_KEYS_LINK_KEY_STORAGE_LEN) { + if (link_key) { + bt_keys_link_key_clear(link_key); + BT_DBG("Clear keys for %s", bt_addr_le_str(&le_addr)); + } else { + BT_WARN("Unable to find deleted keys for %s", + bt_addr_le_str(&le_addr)); + } + + return 0; + } + + memcpy(link_key->storage_start, val, len); + BT_DBG("Successfully restored link key for %s", + bt_addr_le_str(&le_addr)); +#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) + if (aging_counter_val < link_key->aging_counter) { + aging_counter_val = link_key->aging_counter; + } +#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */ + + return 0; +} + +static int link_key_commit(void) +{ + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt_link_key, "bt/link_key", NULL, link_key_set, + link_key_commit, NULL); + +void bt_keys_link_key_update_usage(const bt_addr_t *addr) +{ + struct bt_keys_link_key *link_key = bt_keys_find_link_key(addr); + + if (!link_key) { + return; + } + + if (last_keys_updated == link_key) { + return; + } + + link_key->aging_counter = ++aging_counter_val; + last_keys_updated = link_key; + + BT_DBG("Aging counter for %s is set to %u", bt_addr_str(addr), + link_key->aging_counter); + + if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) { + bt_keys_link_key_store(link_key); + } +} + +#endif diff --git a/components/ble/ble_stack/host/l2cap.c b/components/ble/ble_stack/host/l2cap.c new file mode 100644 index 00000000..93a2cc58 --- /dev/null +++ b/components/ble/ble_stack/host/l2cap.c @@ -0,0 +1,1955 @@ +/* l2cap.c - L2CAP handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_L2CAP) +#define LOG_MODULE_NAME bt_l2cap +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" + +#include "config.h" + +#define LE_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, chan.rtx_work) +#define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work) + +#define L2CAP_LE_MIN_MTU 23 + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_ACL_RX_COUNT - 1) +#else +#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_RX_BUF_COUNT - 1) +#endif + +#define L2CAP_LE_CID_DYN_START 0x0040 +#define L2CAP_LE_CID_DYN_END 0x007f +#define L2CAP_LE_CID_IS_DYN(_cid) \ + (_cid >= L2CAP_LE_CID_DYN_START && _cid <= L2CAP_LE_CID_DYN_END) + +#define L2CAP_LE_PSM_FIXED_START 0x0001 +#define L2CAP_LE_PSM_FIXED_END 0x007f +#define L2CAP_LE_PSM_DYN_START 0x0080 +#define L2CAP_LE_PSM_DYN_END 0x00ff + +#define L2CAP_CONN_TIMEOUT K_SECONDS(40) +#define L2CAP_DISC_TIMEOUT K_SECONDS(2) + +#if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) +static sys_slist_t le_channels; +#endif + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +/* Size of MTU is based on the maximum amount of data the buffer can hold + * excluding ACL and driver headers. + */ +#define L2CAP_MAX_LE_MPS BT_L2CAP_RX_MTU +/* For now use MPS - SDU length to disable segmentation */ +#define L2CAP_MAX_LE_MTU (L2CAP_MAX_LE_MPS - 2) + +#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false) +#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true) + +static sys_slist_t servers; + +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +/* L2CAP signalling channel specific context */ +struct bt_l2cap { + /* The channel this context is associated with */ + struct bt_l2cap_le_chan chan; +}; + +static struct bt_l2cap bt_l2cap_pool[CONFIG_BT_MAX_CONN]; + +static u8_t get_ident(void) +{ + static u8_t ident; + + ident++; + /* handle integer overflow (0 is not valid) */ + if (!ident) { + ident++; + } + + return ident; +} + +#if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) +void bt_l2cap_le_fixed_chan_register(struct bt_l2cap_fixed_chan *chan) +{ + BT_DBG("CID 0x%04x", chan->cid); + + sys_slist_append(&le_channels, &chan->node); +} +#endif + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static struct bt_l2cap_le_chan *l2cap_chan_alloc_cid(struct bt_conn *conn, + struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + u16_t cid; + + /* + * No action needed if there's already a CID allocated, e.g. in + * the case of a fixed channel. + */ + if (ch && ch->rx.cid > 0) { + return ch; + } + + for (cid = L2CAP_LE_CID_DYN_START; cid <= L2CAP_LE_CID_DYN_END; cid++) { + if (ch && !bt_l2cap_le_lookup_rx_cid(conn, cid)) { + ch->rx.cid = cid; + return ch; + } + } + + return NULL; +} + +static struct bt_l2cap_le_chan * +__l2cap_lookup_ident(struct bt_conn *conn, u16_t ident, bool remove) +{ + struct bt_l2cap_chan *chan; + sys_snode_t *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan->ident == ident) { + if (remove) { + sys_slist_remove(&conn->channels, prev, + &chan->node); + } + return BT_L2CAP_LE_CHAN(chan); + } + + prev = &chan->node; + } + + return NULL; +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +void bt_l2cap_chan_remove(struct bt_conn *conn, struct bt_l2cap_chan *ch) +{ + struct bt_l2cap_chan *chan; + sys_snode_t *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (chan == ch) { + sys_slist_remove(&conn->channels, prev, &chan->node); + return; + } + + prev = &chan->node; + } +} + +const char *bt_l2cap_chan_state_str(bt_l2cap_chan_state_t state) +{ + switch (state) { + case BT_L2CAP_DISCONNECTED: + return "disconnected"; + case BT_L2CAP_CONNECT: + return "connect"; + case BT_L2CAP_CONFIG: + return "config"; + case BT_L2CAP_CONNECTED: + return "connected"; + case BT_L2CAP_DISCONNECT: + return "disconnect"; + default: + return "unknown"; + } +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +#if defined(CONFIG_BT_DEBUG_L2CAP) +void bt_l2cap_chan_set_state_debug(struct bt_l2cap_chan *chan, + bt_l2cap_chan_state_t state, + const char *func, int line) +{ + BT_DBG("chan %p psm 0x%04x %s -> %s", chan, chan->psm, + bt_l2cap_chan_state_str(chan->state), + bt_l2cap_chan_state_str(state)); + + /* check transitions validness */ + switch (state) { + case BT_L2CAP_DISCONNECTED: + /* regardless of old state always allows this state */ + break; + case BT_L2CAP_CONNECT: + if (chan->state != BT_L2CAP_DISCONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_L2CAP_CONFIG: + if (chan->state != BT_L2CAP_CONNECT) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_L2CAP_CONNECTED: + if (chan->state != BT_L2CAP_CONFIG && + chan->state != BT_L2CAP_CONNECT) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + case BT_L2CAP_DISCONNECT: + if (chan->state != BT_L2CAP_CONFIG && + chan->state != BT_L2CAP_CONNECTED) { + BT_WARN("%s()%d: invalid transition", func, line); + } + break; + default: + BT_ERR("%s()%d: unknown (%u) state was set", func, line, state); + return; + } + + chan->state = state; +} +#else +void bt_l2cap_chan_set_state(struct bt_l2cap_chan *chan, + bt_l2cap_chan_state_t state) +{ + chan->state = state; +} +#endif /* CONFIG_BT_DEBUG_L2CAP */ +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +void bt_l2cap_chan_del(struct bt_l2cap_chan *chan) +{ + BT_DBG("conn %p chan %p", chan->conn, chan); + + if (!chan->conn) { + goto destroy; + } + + if (chan->ops->disconnected) { + chan->ops->disconnected(chan); + } + + chan->conn = NULL; + +destroy: +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + /* Reset internal members of common channel */ + bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECTED); + chan->psm = 0U; +#endif + + if (chan->destroy) { + chan->destroy(chan); + } + + #ifdef BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS + if(chan->rtx_work.timer.timer.hdl) + k_delayed_work_del_timer(&chan->rtx_work); + #endif +} + +static void l2cap_rtx_timeout(struct k_work *work) +{ + struct bt_l2cap_le_chan *chan = LE_CHAN_RTX(work); + + BT_ERR("chan %p timeout", chan); + + bt_l2cap_chan_remove(chan->chan.conn, &chan->chan); + bt_l2cap_chan_del(&chan->chan); +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan, + struct net_buf *buf); + +static void l2cap_rx_process(struct k_work *work) +{ + struct bt_l2cap_le_chan *ch = CHAN_RX(work); + struct net_buf *buf; + + while ((buf = net_buf_get(&ch->rx_queue, K_NO_WAIT))) { + BT_DBG("ch %p buf %p", ch, buf); + l2cap_chan_le_recv(ch, buf); + net_buf_unref(buf); + } +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +void bt_l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan, + bt_l2cap_chan_destroy_t destroy) +{ + /* Attach channel to the connection */ + sys_slist_append(&conn->channels, &chan->node); + chan->conn = conn; + chan->destroy = destroy; + + BT_DBG("conn %p chan %p", conn, chan); +} + +static bool l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan, + bt_l2cap_chan_destroy_t destroy) +{ + struct bt_l2cap_le_chan *ch; + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + ch = l2cap_chan_alloc_cid(conn, chan); +#else + ch = BT_L2CAP_LE_CHAN(chan); +#endif + + if (!ch) { + BT_ERR("Unable to allocate L2CAP CID"); + return false; + } + + k_delayed_work_init(&chan->rtx_work, l2cap_rtx_timeout); + + bt_l2cap_chan_add(conn, chan, destroy); + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + if (L2CAP_LE_CID_IS_DYN(ch->rx.cid)) { + k_work_init(&ch->rx_work, l2cap_rx_process); + k_fifo_init(&ch->rx_queue, 20); + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECT); + } +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + + return true; +} + +void bt_l2cap_connected(struct bt_conn *conn) +{ + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + struct bt_l2cap_fixed_chan *fchan; + #endif + struct bt_l2cap_chan *chan; + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + bt_l2cap_br_connected(conn); + return; + } + + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + SYS_SLIST_FOR_EACH_CONTAINER(&le_channels, fchan, node) { + #else + Z_STRUCT_SECTION_FOREACH(bt_l2cap_fixed_chan, fchan) { + #endif + struct bt_l2cap_le_chan *ch; + + if (fchan->accept(conn, &chan) < 0) { + continue; + } + + ch = BT_L2CAP_LE_CHAN(chan); + + /* Fill up remaining fixed channel context attached in + * fchan->accept() + */ + ch->rx.cid = fchan->cid; + ch->tx.cid = fchan->cid; + + if (!l2cap_chan_add(conn, chan, NULL)) { + return; + } + + if (chan->ops->connected) { + chan->ops->connected(chan); + } + + /* Always set output status to fixed channels */ + atomic_set_bit(chan->status, BT_L2CAP_STATUS_OUT); + + if (chan->ops->status) { + chan->ops->status(chan, chan->status); + } + } +} + +void bt_l2cap_disconnected(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan, *next; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&conn->channels, chan, next, node) { + bt_l2cap_chan_del(chan); + } +} + +static struct net_buf *l2cap_create_le_sig_pdu(struct net_buf *buf, + u8_t code, u8_t ident, + u16_t len) +{ + struct bt_l2cap_sig_hdr *hdr; + + /* Don't wait more than the minimum RTX timeout of 2 seconds */ + buf = bt_l2cap_create_pdu_timeout(NULL, 0, K_SECONDS(2)); + if (!buf) { + /* If it was not possible to allocate a buffer within the + * timeout return NULL. + */ + BT_ERR("Unable to allocate buffer for op 0x%02x", code); + return NULL; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = code; + hdr->ident = ident; + hdr->len = sys_cpu_to_le16(len); + + return buf; +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan, + struct net_buf *buf, s32_t timeout) +{ + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126: + * + * The value of this timer is implementation-dependent but the minimum + * initial value is 1 second and the maximum initial value is 60 + * seconds. One RTX timer shall exist for each outstanding signaling + * request, including each Echo Request. The timer disappears on the + * final expiration, when the response is received, or the physical + * link is lost. + */ + if (timeout) { + k_delayed_work_submit(&chan->chan.rtx_work, timeout); + } else { + k_delayed_work_cancel(&chan->chan.rtx_work); + } + + bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); +} + +static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch) +{ + struct net_buf *buf; + struct bt_l2cap_le_conn_req *req; + + ch->chan.ident = get_ident(); + + buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_LE_CONN_REQ, + ch->chan.ident, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->psm = sys_cpu_to_le16(ch->chan.psm); + req->scid = sys_cpu_to_le16(ch->rx.cid); + req->mtu = sys_cpu_to_le16(ch->rx.mtu); + req->mps = sys_cpu_to_le16(ch->rx.mps); + req->credits = sys_cpu_to_le16(ch->rx.init_credits); + + l2cap_chan_send_req(ch, buf, L2CAP_CONN_TIMEOUT); + + return 0; +} + +static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, u8_t status) +{ + /* Skip channels already connected or with a pending request */ + if (chan->state != BT_L2CAP_CONNECT || chan->ident) { + return; + } + + if (status) { + bt_l2cap_chan_remove(chan->conn, chan); + bt_l2cap_chan_del(chan); + return; + } + + /* Retry to connect */ + l2cap_le_conn_req(BT_L2CAP_LE_CHAN(chan)); +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +void bt_l2cap_encrypt_change(struct bt_conn *conn, u8_t hci_status) +{ + struct bt_l2cap_chan *chan; + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + l2cap_br_encrypt_change(conn, hci_status); + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + l2cap_le_encrypt_change(chan, hci_status); +#endif + + if (chan->ops->encrypt_change) { + chan->ops->encrypt_change(chan, hci_status); + } + } +} + +struct net_buf *bt_l2cap_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, s32_t timeout) +{ + return bt_conn_create_pdu_timeout(pool, + sizeof(struct bt_l2cap_hdr) + reserve, + timeout); +} + +int bt_l2cap_send_cb(struct bt_conn *conn, u16_t cid, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data) +{ + struct bt_l2cap_hdr *hdr; + + BT_DBG("conn %p cid %u len %zu", conn, cid, net_buf_frags_len(buf)); + + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + hdr->cid = sys_cpu_to_le16(cid); + + return bt_conn_send_cb(conn, buf, cb, user_data); +} + +static void l2cap_send_reject(struct bt_conn *conn, u8_t ident, + u16_t reason, void *data, u8_t data_len) +{ + struct bt_l2cap_cmd_reject *rej; + struct net_buf *buf; + + buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_CMD_REJECT, ident, + sizeof(*rej) + data_len); + if (!buf) { + return; + } + + rej = net_buf_add(buf, sizeof(*rej)); + rej->reason = sys_cpu_to_le16(reason); + + if (data) { + net_buf_add_mem(buf, data, data_len); + } + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); +} + +static void le_conn_param_rsp(struct bt_l2cap *l2cap, struct net_buf *buf) +{ + struct bt_l2cap_conn_param_rsp *rsp = (void *)buf->data; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small LE conn param rsp"); + return; + } + + BT_DBG("LE conn param rsp result %u", sys_le16_to_cpu(rsp->result)); +} + +static void le_conn_param_update_req(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_le_conn_param param; + struct bt_l2cap_conn_param_rsp *rsp; + struct bt_l2cap_conn_param_req *req = (void *)buf->data; + bool accepted; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small LE conn update param req"); + return; + } + + if (conn->role != BT_HCI_ROLE_MASTER) { + l2cap_send_reject(conn, ident, BT_L2CAP_REJ_NOT_UNDERSTOOD, + NULL, 0); + return; + } + + param.interval_min = sys_le16_to_cpu(req->min_interval); + param.interval_max = sys_le16_to_cpu(req->max_interval); + param.latency = sys_le16_to_cpu(req->latency); + param.timeout = sys_le16_to_cpu(req->timeout); + + BT_DBG("min 0x%04x max 0x%04x latency: 0x%04x timeout: 0x%04x", + param.interval_min, param.interval_max, param.latency, + param.timeout); + + buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_CONN_PARAM_RSP, ident, + sizeof(*rsp)); + if (!buf) { + return; + } + + accepted = le_param_req(conn, ¶m); + + rsp = net_buf_add(buf, sizeof(*rsp)); + if (accepted) { + rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_ACCEPTED); + } else { + rsp->result = sys_cpu_to_le16(BT_L2CAP_CONN_PARAM_REJECTED); + } + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); + + if (accepted) { + bt_conn_le_conn_update(conn, ¶m); + } +} + +struct bt_l2cap_chan *bt_l2cap_le_lookup_tx_cid(struct bt_conn *conn, + u16_t cid) +{ + struct bt_l2cap_chan *chan; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BT_L2CAP_LE_CHAN(chan)->tx.cid == cid) { + return chan; + } + } + + return NULL; +} + +struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn, + u16_t cid) +{ + struct bt_l2cap_chan *chan; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BT_L2CAP_LE_CHAN(chan)->rx.cid == cid) { + return chan; + } + } + + return NULL; +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static struct bt_l2cap_server *l2cap_server_lookup_psm(u16_t psm) +{ + struct bt_l2cap_server *server; + + SYS_SLIST_FOR_EACH_CONTAINER(&servers, server, node) { + if (server->psm == psm) { + return server; + } + } + + return NULL; +} + +int bt_l2cap_server_register(struct bt_l2cap_server *server) +{ + if (!server->accept) { + return -EINVAL; + } + + if (server->psm) { + if (server->psm < L2CAP_LE_PSM_FIXED_START || + server->psm > L2CAP_LE_PSM_DYN_END) { + return -EINVAL; + } + + /* Check if given PSM is already in use */ + if (l2cap_server_lookup_psm(server->psm)) { + BT_DBG("PSM already registered"); + return -EADDRINUSE; + } + } else { + u16_t psm; + + for (psm = L2CAP_LE_PSM_DYN_START; + psm <= L2CAP_LE_PSM_DYN_END; psm++) { + if (!l2cap_server_lookup_psm(psm)) { + break; + } + } + + if (psm > L2CAP_LE_PSM_DYN_END) { + BT_WARN("No free dynamic PSMs available"); + return -EADDRNOTAVAIL; + } + + BT_DBG("Allocated PSM 0x%04x for new server", psm); + server->psm = psm; + } + + if (server->sec_level > BT_SECURITY_L4) { + return -EINVAL; + } else if (server->sec_level < BT_SECURITY_L1) { + /* Level 0 is only applicable for BR/EDR */ + server->sec_level = BT_SECURITY_L1; + } + + BT_DBG("PSM 0x%04x", server->psm); + + sys_slist_append(&servers, &server->node); + + return 0; +} + +static void l2cap_chan_rx_init(struct bt_l2cap_le_chan *chan) +{ + BT_DBG("chan %p", chan); + + /* Use existing MTU if defined */ + if (!chan->rx.mtu) { + chan->rx.mtu = L2CAP_MAX_LE_MTU; + } + + /* Use existing credits if defined */ + if (!chan->rx.init_credits) { + if (chan->chan.ops->alloc_buf) { + /* Auto tune credits to receive a full packet */ + chan->rx.init_credits = (chan->rx.mtu + + (L2CAP_MAX_LE_MPS - 1)) / + L2CAP_MAX_LE_MPS; + } else { + chan->rx.init_credits = L2CAP_LE_MAX_CREDITS; + } + } + + /* MPS shall not be bigger than MTU + 2 as the remaining bytes cannot + * be used. + */ + chan->rx.mps = MIN(chan->rx.mtu + 2, L2CAP_MAX_LE_MPS); + k_sem_init(&chan->rx.credits, 0, BT_UINT_MAX); + + if (BT_DBG_ENABLED && + chan->rx.init_credits * chan->rx.mps < chan->rx.mtu + 2) { + BT_WARN("Not enough credits for a full packet"); + } +} + +static void l2cap_chan_tx_init(struct bt_l2cap_le_chan *chan) +{ + BT_DBG("chan %p", chan); + + (void)memset(&chan->tx, 0, sizeof(chan->tx)); + k_sem_init(&chan->tx.credits, 0, BT_UINT_MAX); + k_fifo_init(&chan->tx_queue, 20); +} + +static void l2cap_chan_tx_give_credits(struct bt_l2cap_le_chan *chan, + u16_t credits) +{ + BT_DBG("chan %p credits %u", chan, credits); + + while (credits--) { + k_sem_give(&chan->tx.credits); + } + + if (atomic_test_and_set_bit(chan->chan.status, BT_L2CAP_STATUS_OUT) && + chan->chan.ops->status) { + chan->chan.ops->status(&chan->chan, chan->chan.status); + } +} + +static void l2cap_chan_rx_give_credits(struct bt_l2cap_le_chan *chan, + u16_t credits) +{ + BT_DBG("chan %p credits %u", chan, credits); + + while (credits--) { + k_sem_give(&chan->rx.credits); + } +} + +static void l2cap_chan_destroy(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + struct net_buf *buf; + + BT_DBG("chan %p cid 0x%04x", ch, ch->rx.cid); + + /* Cancel ongoing work */ + k_delayed_work_cancel(&chan->rtx_work); + + if (ch->tx_buf) { + net_buf_unref(ch->tx_buf); + ch->tx_buf = NULL; + } + + /* Remove buffers on the TX queue */ + while ((buf = net_buf_get(&ch->tx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + /* Remove buffers on the RX queue */ + while ((buf = net_buf_get(&ch->rx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + /* Destroy segmented SDU if it exists */ + if (ch->_sdu) { + net_buf_unref(ch->_sdu); + ch->_sdu = NULL; + ch->_sdu_len = 0U; + } +} + +static u16_t le_err_to_result(int err) +{ + switch (err) { + case -ENOMEM: + return BT_L2CAP_LE_ERR_NO_RESOURCES; + case -EACCES: + return BT_L2CAP_LE_ERR_AUTHORIZATION; + case -EPERM: + return BT_L2CAP_LE_ERR_KEY_SIZE; + case -ENOTSUP: + /* This handle the cases where a fixed channel is registered but + * for some reason (e.g. controller not suporting a feature) + * cannot be used. + */ + return BT_L2CAP_LE_ERR_PSM_NOT_SUPP; + default: + return BT_L2CAP_LE_ERR_UNACCEPT_PARAMS; + } +} + +static void le_conn_req(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_server *server; + struct bt_l2cap_le_conn_req *req = (void *)buf->data; + struct bt_l2cap_le_conn_rsp *rsp; + u16_t psm, scid, mtu, mps, credits; + int err; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small LE conn req packet size"); + return; + } + + psm = sys_le16_to_cpu(req->psm); + scid = sys_le16_to_cpu(req->scid); + mtu = sys_le16_to_cpu(req->mtu); + mps = sys_le16_to_cpu(req->mps); + credits = sys_le16_to_cpu(req->credits); + + BT_DBG("psm 0x%02x scid 0x%04x mtu %u mps %u credits %u", psm, scid, + mtu, mps, credits); + + if (mtu < L2CAP_LE_MIN_MTU || mps < L2CAP_LE_MIN_MTU) { + BT_ERR("Invalid LE-Conn Req params"); + return; + } + + buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_LE_CONN_RSP, ident, + sizeof(*rsp)); + if (!buf) { + return; + } + + rsp = net_buf_add(buf, sizeof(*rsp)); + (void)memset(rsp, 0, sizeof(*rsp)); + + /* Check if there is a server registered */ + server = l2cap_server_lookup_psm(psm); + if (!server) { + rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_PSM_NOT_SUPP); + goto rsp; + } + + /* Check if connection has minimum required security level */ + #if defined(CONFIG_BT_SMP) + if (conn->sec_level < server->sec_level) { + rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_AUTHENTICATION); + goto rsp; + } + #endif + + if (!L2CAP_LE_CID_IS_DYN(scid)) { + rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_INVALID_SCID); + goto rsp; + } + + chan = bt_l2cap_le_lookup_tx_cid(conn, scid); + if (chan) { + rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_SCID_IN_USE); + goto rsp; + } + + /* Request server to accept the new connection and allocate the + * channel. + */ + err = server->accept(conn, &chan); + if (err < 0) { + rsp->result = sys_cpu_to_le16(le_err_to_result(err)); + goto rsp; + } + + chan->required_sec_level = server->sec_level; + + if (l2cap_chan_add(conn, chan, l2cap_chan_destroy)) { + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + + /* Init TX parameters */ + l2cap_chan_tx_init(ch); + ch->tx.cid = scid; + ch->tx.mps = mps; + ch->tx.mtu = mtu; + ch->tx.init_credits = credits; + l2cap_chan_tx_give_credits(ch, credits); + + /* Init RX parameters */ + l2cap_chan_rx_init(ch); + l2cap_chan_rx_give_credits(ch, ch->rx.init_credits); + + /* Set channel PSM */ + chan->psm = server->psm; + + /* Update state */ + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECTED); + + if (chan->ops->connected) { + chan->ops->connected(chan); + } + + /* Prepare response protocol data */ + rsp->dcid = sys_cpu_to_le16(ch->rx.cid); + rsp->mps = sys_cpu_to_le16(ch->rx.mps); + rsp->mtu = sys_cpu_to_le16(ch->rx.mtu); + rsp->credits = sys_cpu_to_le16(ch->rx.init_credits); + rsp->result = BT_L2CAP_LE_SUCCESS; + } else { + rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_NO_RESOURCES); + } +rsp: + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); +} + +static struct bt_l2cap_le_chan *l2cap_remove_rx_cid(struct bt_conn *conn, + u16_t cid) +{ + struct bt_l2cap_chan *chan; + sys_snode_t *prev = NULL; + + /* Protect fixed channels against accidental removal */ + if (!L2CAP_LE_CID_IS_DYN(cid)) { + return NULL; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BT_L2CAP_LE_CHAN(chan)->rx.cid == cid) { + sys_slist_remove(&conn->channels, prev, &chan->node); + return BT_L2CAP_LE_CHAN(chan); + } + + prev = &chan->node; + } + + return NULL; +} + +static void le_disconn_req(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_le_chan *chan; + struct bt_l2cap_disconn_req *req = (void *)buf->data; + struct bt_l2cap_disconn_rsp *rsp; + u16_t dcid; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small LE conn req packet size"); + return; + } + + dcid = sys_le16_to_cpu(req->dcid); + + BT_DBG("dcid 0x%04x scid 0x%04x", dcid, sys_le16_to_cpu(req->scid)); + + chan = l2cap_remove_rx_cid(conn, dcid); + if (!chan) { + struct bt_l2cap_cmd_reject_cid_data data; + + data.scid = req->scid; + data.dcid = req->dcid; + + l2cap_send_reject(conn, ident, BT_L2CAP_REJ_INVALID_CID, &data, + sizeof(data)); + return; + } + + buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_DISCONN_RSP, ident, + sizeof(*rsp)); + if (!buf) { + return; + } + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->dcid = sys_cpu_to_le16(chan->rx.cid); + rsp->scid = sys_cpu_to_le16(chan->tx.cid); + + bt_l2cap_chan_del(&chan->chan); + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); +} + +#if defined(CONFIG_BT_SMP) +static int l2cap_change_security(struct bt_l2cap_le_chan *chan, u16_t err) +{ + switch (err) { + case BT_L2CAP_LE_ERR_ENCRYPTION: + if (chan->chan.required_sec_level >= BT_SECURITY_L2) { + return -EALREADY; + } + chan->chan.required_sec_level = BT_SECURITY_L2; + break; + case BT_L2CAP_LE_ERR_AUTHENTICATION: + if (chan->chan.required_sec_level < BT_SECURITY_L2) { + chan->chan.required_sec_level = BT_SECURITY_L2; + } else if (chan->chan.required_sec_level < BT_SECURITY_L3) { + chan->chan.required_sec_level = BT_SECURITY_L3; + } else if (chan->chan.required_sec_level < BT_SECURITY_L4) { + chan->chan.required_sec_level = BT_SECURITY_L4; + } else { + return -EALREADY; + } + break; + default: + return -EINVAL; + } + + return bt_conn_set_security(chan->chan.conn, + chan->chan.required_sec_level); +} +#endif //CONFIG_BT_SMP + +static void le_conn_rsp(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_le_chan *chan; + struct bt_l2cap_le_conn_rsp *rsp = (void *)buf->data; + u16_t dcid, mtu, mps, credits, result; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small LE conn rsp packet size"); + return; + } + + dcid = sys_le16_to_cpu(rsp->dcid); + mtu = sys_le16_to_cpu(rsp->mtu); + mps = sys_le16_to_cpu(rsp->mps); + credits = sys_le16_to_cpu(rsp->credits); + result = sys_le16_to_cpu(rsp->result); + + BT_DBG("dcid 0x%04x mtu %u mps %u credits %u result 0x%04x", dcid, + mtu, mps, credits, result); + + /* Keep the channel in case of security errors */ + if (result == BT_L2CAP_LE_SUCCESS || + result == BT_L2CAP_LE_ERR_AUTHENTICATION || + result == BT_L2CAP_LE_ERR_ENCRYPTION) { + chan = l2cap_lookup_ident(conn, ident); + } else { + chan = l2cap_remove_ident(conn, ident); + } + + if (!chan) { + BT_ERR("Cannot find channel for ident %u", ident); + return; + } + + /* Cancel RTX work */ + k_delayed_work_cancel(&chan->chan.rtx_work); + + /* Reset ident since it got a response */ + chan->chan.ident = 0U; + + switch (result) { + case BT_L2CAP_LE_SUCCESS: + chan->tx.cid = dcid; + chan->tx.mtu = mtu; + chan->tx.mps = mps; + + /* Update state */ + bt_l2cap_chan_set_state(&chan->chan, BT_L2CAP_CONNECTED); + + if (chan->chan.ops->connected) { + chan->chan.ops->connected(&chan->chan); + } + + /* Give credits */ + l2cap_chan_tx_give_credits(chan, credits); + l2cap_chan_rx_give_credits(chan, chan->rx.init_credits); + + break; + case BT_L2CAP_LE_ERR_AUTHENTICATION: + case BT_L2CAP_LE_ERR_ENCRYPTION: + #if defined(CONFIG_BT_SMP) + /* If security needs changing wait it to be completed */ + if (l2cap_change_security(chan, result) == 0) { + return; + } + #endif + bt_l2cap_chan_remove(conn, &chan->chan); + __attribute__((fallthrough)); + default: + bt_l2cap_chan_del(&chan->chan); + } +} + +static void le_disconn_rsp(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_le_chan *chan; + struct bt_l2cap_disconn_rsp *rsp = (void *)buf->data; + u16_t scid; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small LE disconn rsp packet size"); + return; + } + + scid = sys_le16_to_cpu(rsp->scid); + + BT_DBG("dcid 0x%04x scid 0x%04x", sys_le16_to_cpu(rsp->dcid), scid); + + chan = l2cap_remove_rx_cid(conn, scid); + if (!chan) { + return; + } + + bt_l2cap_chan_del(&chan->chan); +} + +static inline struct net_buf *l2cap_alloc_seg(struct net_buf *buf) +{ + struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id); + struct net_buf *seg; + + /* Try to use original pool if possible */ + seg = net_buf_alloc(pool, K_NO_WAIT); + if (seg) { + net_buf_reserve(seg, BT_L2CAP_CHAN_SEND_RESERVE); + return seg; + } + + /* Fallback to using global connection tx pool */ + return bt_l2cap_create_pdu(NULL, 0); +} + +static struct net_buf *l2cap_chan_create_seg(struct bt_l2cap_le_chan *ch, + struct net_buf *buf, + size_t sdu_hdr_len) +{ + struct net_buf *seg; + u16_t headroom; + u16_t len; + + /* Segment if data (+ data headroom) is bigger than MPS */ + if (buf->len + sdu_hdr_len > ch->tx.mps) { + goto segment; + } + + headroom = BT_L2CAP_CHAN_SEND_RESERVE + sdu_hdr_len; + + /* Check if original buffer has enough headroom and don't have any + * fragments. + */ + if (net_buf_headroom(buf) >= headroom && !buf->frags) { + if (sdu_hdr_len) { + /* Push SDU length if set */ + net_buf_push_le16(buf, net_buf_frags_len(buf)); + } + return net_buf_ref(buf); + } + +segment: + seg = l2cap_alloc_seg(buf); + if (!seg) { + return NULL; + } + + if (sdu_hdr_len) { + net_buf_add_le16(seg, net_buf_frags_len(buf)); + } + + /* Don't send more that TX MPS including SDU length */ + len = MIN(net_buf_tailroom(seg), ch->tx.mps - sdu_hdr_len); + /* Limit if original buffer is smaller than the segment */ + len = MIN(buf->len, len); + net_buf_add_mem(seg, buf->data, len); + net_buf_pull(buf, len); + + BT_DBG("ch %p seg %p len %u", ch, seg, seg->len); + + return seg; +} + +void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data) +{ + struct bt_l2cap_chan *chan = user_data; + + BT_DBG("conn %p chan %p", conn, chan); + + if (chan->ops->sent) { + chan->ops->sent(chan); + } +} + +static int l2cap_chan_le_send(struct bt_l2cap_le_chan *ch, struct net_buf *buf, + u16_t sdu_hdr_len) +{ + struct net_buf *seg; + int len; + + /* Wait for credits */ + if (k_sem_take(&ch->tx.credits, K_NO_WAIT)) { + BT_DBG("No credits to transmit packet"); + return -EAGAIN; + } + + seg = l2cap_chan_create_seg(ch, buf, sdu_hdr_len); + if (!seg) { + return -ENOMEM; + } + + /* Channel may have been disconnected while waiting for a buffer */ + if (!ch->chan.conn) { + net_buf_unref(buf); + return -ECONNRESET; + } + + BT_DBG("ch %p cid 0x%04x len %u credits %u", ch, ch->tx.cid, + seg->len, k_sem_count_get(&ch->tx.credits)); + + len = seg->len - sdu_hdr_len; + + /* Set a callback if there is no data left in the buffer and sent + * callback has been set. + */ + if ((buf == seg || !buf->len) && ch->chan.ops->sent) { + bt_l2cap_send_cb(ch->chan.conn, ch->tx.cid, seg, + l2cap_chan_sdu_sent, &ch->chan); + } else { + bt_l2cap_send(ch->chan.conn, ch->tx.cid, seg); + } + + /* Check if there is no credits left clear output status and notify its + * change. + */ + if (!k_sem_count_get(&ch->tx.credits)) { + atomic_clear_bit(ch->chan.status, BT_L2CAP_STATUS_OUT); + if (ch->chan.ops->status) { + ch->chan.ops->status(&ch->chan, ch->chan.status); + } + } + + return len; +} + +static int l2cap_chan_le_send_sdu(struct bt_l2cap_le_chan *ch, + struct net_buf **buf, u16_t sent) +{ + int ret, total_len; + struct net_buf *frag; + + total_len = net_buf_frags_len(*buf) + sent; + + if (total_len > ch->tx.mtu) { + return -EMSGSIZE; + } + + frag = *buf; + if (!frag->len && frag->frags) { + frag = frag->frags; + } + + if (!sent) { + /* Add SDU length for the first segment */ + ret = l2cap_chan_le_send(ch, frag, BT_L2CAP_SDU_HDR_LEN); + if (ret < 0) { + if (ret == -EAGAIN) { + /* Store sent data into user_data */ + memcpy(net_buf_user_data(frag), &sent, + sizeof(sent)); + } + *buf = frag; + return ret; + } + sent = ret; + } + + /* Send remaining segments */ + for (ret = 0; sent < total_len; sent += ret) { + /* Proceed to next fragment */ + if (!frag->len) { + frag = net_buf_frag_del(NULL, frag); + } + + ret = l2cap_chan_le_send(ch, frag, 0); + if (ret < 0) { + if (ret == -EAGAIN) { + /* Store sent data into user_data */ + memcpy(net_buf_user_data(frag), &sent, + sizeof(sent)); + } + *buf = frag; + return ret; + } + } + + BT_DBG("ch %p cid 0x%04x sent %u total_len %u", ch, ch->tx.cid, sent, + total_len); + + net_buf_unref(frag); + + return ret; +} + +static struct net_buf *l2cap_chan_le_get_tx_buf(struct bt_l2cap_le_chan *ch) +{ + struct net_buf *buf; + + /* Return current buffer */ + if (ch->tx_buf) { + buf = ch->tx_buf; + ch->tx_buf = NULL; + return buf; + } + + return net_buf_get(&ch->tx_queue, K_NO_WAIT); +} + +static void l2cap_chan_le_send_resume(struct bt_l2cap_le_chan *ch) +{ + struct net_buf *buf; + + /* Resume tx in case there are buffers in the queue */ + while ((buf = l2cap_chan_le_get_tx_buf(ch))) { + #if defined(BFLB_BLE) + int sent = *((int *)net_buf_user_data(buf)); + #else + u16_t sent = *((u16_t *)net_buf_user_data(buf)); + #endif + + BT_DBG("buf %p sent %u", buf, sent); + + sent = l2cap_chan_le_send_sdu(ch, &buf, sent); + if (sent < 0) { + if (sent == -EAGAIN) { + ch->tx_buf = buf; + } + break; + } + } +} + +static void le_credits(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_le_credits *ev = (void *)buf->data; + struct bt_l2cap_le_chan *ch; + u16_t credits, cid; + + if (buf->len < sizeof(*ev)) { + BT_ERR("Too small LE Credits packet size"); + return; + } + + cid = sys_le16_to_cpu(ev->cid); + credits = sys_le16_to_cpu(ev->credits); + + BT_DBG("cid 0x%04x credits %u", cid, credits); + + chan = bt_l2cap_le_lookup_tx_cid(conn, cid); + if (!chan) { + BT_ERR("Unable to find channel of LE Credits packet"); + return; + } + + ch = BT_L2CAP_LE_CHAN(chan); + + if (k_sem_count_get(&ch->tx.credits) + credits > UINT16_MAX) { + BT_ERR("Credits overflow"); + bt_l2cap_chan_disconnect(chan); + return; + } + + l2cap_chan_tx_give_credits(ch, credits); + + BT_DBG("chan %p total credits %u", ch, + k_sem_count_get(&ch->tx.credits)); + + l2cap_chan_le_send_resume(ch); +} + +static void reject_cmd(struct bt_l2cap *l2cap, u8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_le_chan *chan; + + /* Check if there is a outstanding channel */ + chan = l2cap_remove_ident(conn, ident); + if (!chan) { + return; + } + + bt_l2cap_chan_del(&chan->chan); +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_l2cap *l2cap = CONTAINER_OF(chan, struct bt_l2cap, chan); + struct bt_l2cap_sig_hdr *hdr; + u16_t len; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small L2CAP signaling PDU"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + len = sys_le16_to_cpu(hdr->len); + + BT_DBG("Signaling code 0x%02x ident %u len %u", hdr->code, + hdr->ident, len); + + if (buf->len != len) { + BT_ERR("L2CAP length mismatch (%u != %u)", buf->len, len); + return 0; + } + + if (!hdr->ident) { + BT_ERR("Invalid ident value in L2CAP PDU"); + return 0; + } + + switch (hdr->code) { + case BT_L2CAP_CONN_PARAM_RSP: + le_conn_param_rsp(l2cap, buf); + break; +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + case BT_L2CAP_LE_CONN_REQ: + le_conn_req(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_LE_CONN_RSP: + le_conn_rsp(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_DISCONN_REQ: + le_disconn_req(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_DISCONN_RSP: + le_disconn_rsp(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_LE_CREDITS: + le_credits(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_CMD_REJECT: + reject_cmd(l2cap, hdr->ident, buf); + break; +#else + case BT_L2CAP_CMD_REJECT: + /* Ignored */ + break; +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + case BT_L2CAP_CONN_PARAM_REQ: + if(IS_ENABLED(CONFIG_BT_CENTRAL)) { + le_conn_param_update_req(l2cap, hdr->ident, buf); + break; + } +#if defined(BFLB_BLE) + __attribute__((fallthrough)); +#endif + + /* Fall-through */ + default: + BT_WARN("Unknown L2CAP PDU code 0x%02x", hdr->code); + l2cap_send_reject(chan->conn, hdr->ident, + BT_L2CAP_REJ_NOT_UNDERSTOOD, NULL, 0); + break; + } + + return 0; +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static void l2cap_chan_send_credits(struct bt_l2cap_le_chan *chan, + struct net_buf *buf, u16_t credits) +{ + struct bt_l2cap_le_credits *ev; + + /* Cap the number of credits given */ + if (credits > chan->rx.init_credits) { + credits = chan->rx.init_credits; + } + + l2cap_chan_rx_give_credits(chan, credits); + + buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_LE_CREDITS, get_ident(), + sizeof(*ev)); + if (!buf) { + return; + } + + ev = net_buf_add(buf, sizeof(*ev)); + ev->cid = sys_cpu_to_le16(chan->rx.cid); + ev->credits = sys_cpu_to_le16(credits); + + bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf); + + BT_DBG("chan %p credits %u", chan, k_sem_count_get(&chan->rx.credits)); +} + +static void l2cap_chan_update_credits(struct bt_l2cap_le_chan *chan, + struct net_buf *buf) +{ + s16_t credits; + + /* Restore enough credits to complete the sdu */ + credits = ((chan->_sdu_len - net_buf_frags_len(buf)) + + (chan->rx.mps - 1)) / chan->rx.mps; + credits -= k_sem_count_get(&chan->rx.credits); + if (credits <= 0) { + return; + } + + l2cap_chan_send_credits(chan, buf, credits); +} + +int bt_l2cap_chan_recv_complete(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + struct bt_conn *conn = chan->conn; + u16_t credits; + + __ASSERT_NO_MSG(chan); + __ASSERT_NO_MSG(buf); + + if (!conn) { + return -ENOTCONN; + } + + if (conn->type != BT_CONN_TYPE_LE) { + return -ENOTSUP; + } + + BT_DBG("chan %p buf %p", chan, buf); + + /* Restore credits used by packet */ + memcpy(&credits, net_buf_user_data(buf), sizeof(credits)); + + l2cap_chan_send_credits(ch, buf, credits); + + net_buf_unref(buf); + + return 0; +} + +static struct net_buf *l2cap_alloc_frag(s32_t timeout, void *user_data) +{ + struct bt_l2cap_le_chan *chan = user_data; + struct net_buf *frag = NULL; + + frag = chan->chan.ops->alloc_buf(&chan->chan); + if (!frag) { + return NULL; + } + + BT_DBG("frag %p tailroom %zu", frag, net_buf_tailroom(frag)); + + return frag; +} + +static void l2cap_chan_le_recv_sdu(struct bt_l2cap_le_chan *chan, + struct net_buf *buf, u16_t seg) +{ + int err; + + BT_DBG("chan %p len %zu", chan, net_buf_frags_len(buf)); + + /* Receiving complete SDU, notify channel and reset SDU buf */ + err = chan->chan.ops->recv(&chan->chan, buf); + if (err < 0) { + if (err != -EINPROGRESS) { + BT_ERR("err %d", err); + bt_l2cap_chan_disconnect(&chan->chan); + net_buf_unref(buf); + } + return; + } + + l2cap_chan_send_credits(chan, buf, seg); + net_buf_unref(buf); +} + +static void l2cap_chan_le_recv_seg(struct bt_l2cap_le_chan *chan, + struct net_buf *buf) +{ + u16_t len; + u16_t seg = 0U; + + len = net_buf_frags_len(chan->_sdu); + if (len) { + memcpy(&seg, net_buf_user_data(chan->_sdu), sizeof(seg)); + } + + if (len + buf->len > chan->_sdu_len) { + BT_ERR("SDU length mismatch"); + bt_l2cap_chan_disconnect(&chan->chan); + return; + } + + seg++; + /* Store received segments in user_data */ + memcpy(net_buf_user_data(chan->_sdu), &seg, sizeof(seg)); + + BT_DBG("chan %p seg %d len %zu", chan, seg, net_buf_frags_len(buf)); + + /* Append received segment to SDU */ + len = net_buf_append_bytes(chan->_sdu, buf->len, buf->data, K_NO_WAIT, + l2cap_alloc_frag, chan); + if (len != buf->len) { + BT_ERR("Unable to store SDU"); + bt_l2cap_chan_disconnect(&chan->chan); + return; + } + + if (net_buf_frags_len(chan->_sdu) < chan->_sdu_len) { + /* Give more credits if remote has run out of them, this + * should only happen if the remote cannot fully utilize the + * MPS for some reason. + */ + if (!k_sem_count_get(&chan->rx.credits) && + seg == chan->rx.init_credits) { + l2cap_chan_update_credits(chan, buf); + } + return; + } + + buf = chan->_sdu; + chan->_sdu = NULL; + chan->_sdu_len = 0U; + + l2cap_chan_le_recv_sdu(chan, buf, seg); +} + +static void l2cap_chan_le_recv(struct bt_l2cap_le_chan *chan, + struct net_buf *buf) +{ + u16_t sdu_len; + int err; + + if (k_sem_take(&chan->rx.credits, K_NO_WAIT)) { + BT_ERR("No credits to receive packet"); + bt_l2cap_chan_disconnect(&chan->chan); + return; + } + + /* Check if segments already exist */ + if (chan->_sdu) { + l2cap_chan_le_recv_seg(chan, buf); + return; + } + + sdu_len = net_buf_pull_le16(buf); + + BT_DBG("chan %p len %u sdu_len %u", chan, buf->len, sdu_len); + + if (sdu_len > chan->rx.mtu) { + BT_ERR("Invalid SDU length"); + bt_l2cap_chan_disconnect(&chan->chan); + return; + } + + /* Always allocate buffer from the channel if supported. */ + if (chan->chan.ops->alloc_buf) { + chan->_sdu = chan->chan.ops->alloc_buf(&chan->chan); + if (!chan->_sdu) { + BT_ERR("Unable to allocate buffer for SDU"); + bt_l2cap_chan_disconnect(&chan->chan); + return; + } + chan->_sdu_len = sdu_len; + l2cap_chan_le_recv_seg(chan, buf); + return; + } + + err = chan->chan.ops->recv(&chan->chan, buf); + if (err) { + if (err != -EINPROGRESS) { + BT_ERR("err %d", err); + bt_l2cap_chan_disconnect(&chan->chan); + } + return; + } + + l2cap_chan_send_credits(chan, buf, 1); +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + +static void l2cap_chan_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan); + + if (L2CAP_LE_CID_IS_DYN(ch->rx.cid)) { + net_buf_put(&ch->rx_queue, net_buf_ref(buf)); + k_work_submit(&ch->rx_work); + return; + } +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ + + BT_DBG("chan %p len %u", chan, buf->len); + + chan->ops->recv(chan, buf); + net_buf_unref(buf); +} + +void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf) +{ + struct bt_l2cap_hdr *hdr; + struct bt_l2cap_chan *chan; + u16_t cid; + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + bt_l2cap_br_recv(conn, buf); + return; + } + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small L2CAP PDU received"); + net_buf_unref(buf); + return; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + cid = sys_le16_to_cpu(hdr->cid); + + BT_DBG("Packet for CID %u len %u", cid, buf->len); + + chan = bt_l2cap_le_lookup_rx_cid(conn, cid); + if (!chan) { + BT_WARN("Ignoring data for unknown CID 0x%04x", cid); + net_buf_unref(buf); + return; + } + + l2cap_chan_recv(chan, buf); +} + +int bt_l2cap_update_conn_param(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + struct bt_l2cap_conn_param_req *req; + struct net_buf *buf; + + buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_CONN_PARAM_REQ, + get_ident(), sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->min_interval = sys_cpu_to_le16(param->interval_min); + req->max_interval = sys_cpu_to_le16(param->interval_max); + req->latency = sys_cpu_to_le16(param->latency); + req->timeout = sys_cpu_to_le16(param->timeout); + + bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf); + + return 0; +} + +static void l2cap_connected(struct bt_l2cap_chan *chan) +{ + BT_DBG("ch %p cid 0x%04x", BT_L2CAP_LE_CHAN(chan), + BT_L2CAP_LE_CHAN(chan)->rx.cid); +} + +static void l2cap_disconnected(struct bt_l2cap_chan *chan) +{ + BT_DBG("ch %p cid 0x%04x", BT_L2CAP_LE_CHAN(chan), + BT_L2CAP_LE_CHAN(chan)->rx.cid); +} + +static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + int i; + static struct bt_l2cap_chan_ops ops = { + .connected = l2cap_connected, + .disconnected = l2cap_disconnected, + .recv = l2cap_recv, + }; + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_l2cap_pool); i++) { + struct bt_l2cap *l2cap = &bt_l2cap_pool[i]; + + if (l2cap->chan.chan.conn) { + continue; + } + + l2cap->chan.chan.ops = &ops; + *chan = &l2cap->chan.chan; + + return 0; + } + + BT_ERR("No available L2CAP context for conn %p", conn); + + return -ENOMEM; +} + +BT_L2CAP_CHANNEL_DEFINE(le_fixed_chan, BT_L2CAP_CID_LE_SIG, l2cap_accept); + +void bt_l2cap_init(void) +{ + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + static struct bt_l2cap_fixed_chan chan = { + .cid = BT_L2CAP_CID_LE_SIG, + .accept = l2cap_accept, + }; + + bt_l2cap_le_fixed_chan_register(&chan); + #endif + if (IS_ENABLED(CONFIG_BT_BREDR)) { + bt_l2cap_br_init(); + } +} + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) +static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch, + u16_t psm) +{ + if (psm < L2CAP_LE_PSM_FIXED_START || psm > L2CAP_LE_PSM_DYN_END) { + return -EINVAL; + } + + l2cap_chan_tx_init(ch); + l2cap_chan_rx_init(ch); + + if (!l2cap_chan_add(conn, &ch->chan, l2cap_chan_destroy)) { + return -ENOMEM; + } + + ch->chan.psm = psm; + + return l2cap_le_conn_req(ch); +} + +int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, + u16_t psm) +{ + BT_DBG("conn %p chan %p psm 0x%04x", conn, chan, psm); + + if (!conn || conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (!chan) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + return bt_l2cap_br_chan_connect(conn, chan, psm); + } + + if (chan->required_sec_level > BT_SECURITY_L4) { + return -EINVAL; + } else if (chan->required_sec_level == BT_SECURITY_L0) { + chan->required_sec_level = BT_SECURITY_L1; + } + + return l2cap_le_connect(conn, BT_L2CAP_LE_CHAN(chan), psm); +} + +int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan) +{ + struct bt_conn *conn = chan->conn; + struct net_buf *buf; + struct bt_l2cap_disconn_req *req; + struct bt_l2cap_le_chan *ch; + + if (!conn) { + return -ENOTCONN; + } + + if (IS_ENABLED(CONFIG_BT_BREDR) && + conn->type == BT_CONN_TYPE_BR) { + return bt_l2cap_br_chan_disconnect(chan); + } + + ch = BT_L2CAP_LE_CHAN(chan); + + BT_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, ch->rx.cid, + ch->tx.cid); + + ch->chan.ident = get_ident(); + + buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_DISCONN_REQ, + ch->chan.ident, sizeof(*req)); + if (!buf) { + return -ENOMEM; + } + + req = net_buf_add(buf, sizeof(*req)); + req->dcid = sys_cpu_to_le16(ch->rx.cid); + req->scid = sys_cpu_to_le16(ch->tx.cid); + + l2cap_chan_send_req(ch, buf, L2CAP_DISC_TIMEOUT); + bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECT); + + return 0; +} + +int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + int err; + + if (!buf) { + return -EINVAL; + } + + BT_DBG("chan %p buf %p len %zu", chan, buf, net_buf_frags_len(buf)); + + if (!chan->conn || chan->conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (IS_ENABLED(CONFIG_BT_BREDR) && + chan->conn->type == BT_CONN_TYPE_BR) { + return bt_l2cap_br_chan_send(chan, buf); + } + + err = l2cap_chan_le_send_sdu(BT_L2CAP_LE_CHAN(chan), &buf, 0); + if (err < 0) { + if (err == -EAGAIN) { + /* Queue buffer to be sent later */ + net_buf_put(&(BT_L2CAP_LE_CHAN(chan))->tx_queue, buf); + return *((u16_t *)net_buf_user_data(buf)); + } + BT_ERR("failed to send message %d", err); + } + + return err; +} +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ diff --git a/components/ble/ble_stack/host/l2cap_br.c b/components/ble/ble_stack/host/l2cap_br.c new file mode 100644 index 00000000..6a4a9876 --- /dev/null +++ b/components/ble/ble_stack/host/l2cap_br.c @@ -0,0 +1,1569 @@ +/* l2cap_br.c - L2CAP BREDR oriented handling */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_L2CAP) +#define LOG_MODULE_NAME bt_l2cap_br +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "sdp_internal.h" +#include "avdtp_internal.h" +#include "a2dp_internal.h" +#include "rfcomm_internal.h" +#include "hfp_hf.h" + +#define BR_CHAN(_ch) CONTAINER_OF(_ch, struct bt_l2cap_br_chan, chan) +#define BR_CHAN_RTX(_w) CONTAINER_OF(_w, struct bt_l2cap_br_chan, chan.rtx_work) + +#define L2CAP_BR_PSM_START 0x0001 +#define L2CAP_BR_PSM_END 0xffff + +#define L2CAP_BR_CID_DYN_START 0x0040 +#define L2CAP_BR_CID_DYN_END 0xffff +#define L2CAP_BR_CID_IS_DYN(_cid) \ + (_cid >= L2CAP_BR_CID_DYN_START && _cid <= L2CAP_BR_CID_DYN_END) + +#define L2CAP_BR_MIN_MTU 48 +#define L2CAP_BR_DEFAULT_MTU 672 + +#define L2CAP_BR_PSM_SDP 0x0001 + +#define L2CAP_BR_INFO_TIMEOUT K_SECONDS(4) +#define L2CAP_BR_CFG_TIMEOUT K_SECONDS(4) +#define L2CAP_BR_DISCONN_TIMEOUT K_SECONDS(1) +#define L2CAP_BR_CONN_TIMEOUT K_SECONDS(40) + +/* + * L2CAP extended feature mask: + * BR/EDR fixed channel support enabled + */ +#define L2CAP_FEAT_FIXED_CHAN_MASK 0x00000080 + +enum { + /* Connection oriented channels flags */ + L2CAP_FLAG_CONN_LCONF_DONE, /* local config accepted by remote */ + L2CAP_FLAG_CONN_RCONF_DONE, /* remote config accepted by local */ + L2CAP_FLAG_CONN_ACCEPTOR, /* getting incoming connection req */ + L2CAP_FLAG_CONN_PENDING, /* remote sent pending result in rsp */ + + /* Signaling channel flags */ + L2CAP_FLAG_SIG_INFO_PENDING, /* retrieving remote l2cap info */ + L2CAP_FLAG_SIG_INFO_DONE, /* remote l2cap info is done */ + + /* fixed channels flags */ + L2CAP_FLAG_FIXED_CONNECTED, /* fixed connected */ +}; + +static sys_slist_t br_servers; + + +/* Pool for outgoing BR/EDR signaling packets, min MTU is 48 */ +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_FIXED_DEFINE(br_sig_pool, CONFIG_BT_MAX_CONN, + BT_L2CAP_BUF_SIZE(L2CAP_BR_MIN_MTU), NULL); +#else +struct net_buf_pool br_sig_pool; +#endif + +/* BR/EDR L2CAP signalling channel specific context */ +struct bt_l2cap_br { + /* The channel this context is associated with */ + struct bt_l2cap_br_chan chan; + uint8_t info_ident; + uint8_t info_fixed_chan; + uint32_t info_feat_mask; +}; + +static struct bt_l2cap_br bt_l2cap_br_pool[CONFIG_BT_MAX_CONN]; + +struct bt_l2cap_chan *bt_l2cap_br_lookup_rx_cid(struct bt_conn *conn, + uint16_t cid) +{ + struct bt_l2cap_chan *chan; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BR_CHAN(chan)->rx.cid == cid) { + return chan; + } + } + + return NULL; +} + +struct bt_l2cap_chan *bt_l2cap_br_lookup_tx_cid(struct bt_conn *conn, + uint16_t cid) +{ + struct bt_l2cap_chan *chan; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BR_CHAN(chan)->tx.cid == cid) { + return chan; + } + } + + return NULL; +} + +static struct bt_l2cap_br_chan* +l2cap_br_chan_alloc_cid(struct bt_conn *conn, struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_br_chan *ch = BR_CHAN(chan); + uint16_t cid; + + /* + * No action needed if there's already a CID allocated, e.g. in + * the case of a fixed channel. + */ + if (ch->rx.cid > 0) { + return ch; + } + + /* + * L2CAP_BR_CID_DYN_END is 0xffff so we don't check against it since + * cid is uint16_t, just check against uint16_t overflow + */ + for (cid = L2CAP_BR_CID_DYN_START; cid; cid++) { + if (!bt_l2cap_br_lookup_rx_cid(conn, cid)) { + ch->rx.cid = cid; + return ch; + } + } + + return NULL; +} + +static void l2cap_br_chan_cleanup(struct bt_l2cap_chan *chan) +{ + bt_l2cap_chan_remove(chan->conn, chan); + bt_l2cap_chan_del(chan); +} + +static void l2cap_br_chan_destroy(struct bt_l2cap_chan *chan) +{ + BT_DBG("chan %p cid 0x%04x", BR_CHAN(chan), BR_CHAN(chan)->rx.cid); + + /* Cancel ongoing work */ + k_delayed_work_cancel(&chan->rtx_work); + + atomic_clear(BR_CHAN(chan)->flags); +} + +static void l2cap_br_rtx_timeout(struct k_work *work) +{ + struct bt_l2cap_br_chan *chan = BR_CHAN_RTX(work); + + BT_WARN("chan %p timeout", chan); + + if (chan->rx.cid == BT_L2CAP_CID_BR_SIG) { + BT_DBG("Skip BR/EDR signalling channel "); + atomic_clear_bit(chan->flags, L2CAP_FLAG_SIG_INFO_PENDING); + return; + } + + BT_DBG("chan %p %s scid 0x%04x", chan, + bt_l2cap_chan_state_str(chan->chan.state), + chan->rx.cid); + + switch (chan->chan.state) { + case BT_L2CAP_CONFIG: + bt_l2cap_br_chan_disconnect(&chan->chan); + break; + case BT_L2CAP_DISCONNECT: + case BT_L2CAP_CONNECT: + l2cap_br_chan_cleanup(&chan->chan); + break; + default: + break; + } +} + +static bool l2cap_br_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan, + bt_l2cap_chan_destroy_t destroy) +{ + struct bt_l2cap_br_chan *ch = l2cap_br_chan_alloc_cid(conn, chan); + + if (!ch) { + BT_DBG("Unable to allocate L2CAP CID"); + return false; + } + + k_delayed_work_init(&chan->rtx_work, l2cap_br_rtx_timeout); + bt_l2cap_chan_add(conn, chan, destroy); + + return true; +} + +static uint8_t l2cap_br_get_ident(void) +{ + static uint8_t ident; + + ident++; + /* handle integer overflow (0 is not valid) */ + if (!ident) { + ident++; + } + + return ident; +} + +static void l2cap_br_chan_send_req(struct bt_l2cap_br_chan *chan, + struct net_buf *buf, s32_t timeout) +{ + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126: + * + * The value of this timer is implementation-dependent but the minimum + * initial value is 1 second and the maximum initial value is 60 + * seconds. One RTX timer shall exist for each outstanding signaling + * request, including each Echo Request. The timer disappears on the + * final expiration, when the response is received, or the physical + * link is lost. + */ + k_delayed_work_submit(&chan->chan.rtx_work, timeout); + + bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_BR_SIG, buf); +} + +static void l2cap_br_get_info(struct bt_l2cap_br *l2cap, uint16_t info_type) +{ + struct bt_l2cap_info_req *info; + struct net_buf *buf; + struct bt_l2cap_sig_hdr *hdr; + + BT_DBG("info type %u", info_type); + + if (atomic_test_bit(l2cap->chan.flags, L2CAP_FLAG_SIG_INFO_PENDING)) { + return; + } + + switch (info_type) { + case BT_L2CAP_INFO_FEAT_MASK: + case BT_L2CAP_INFO_FIXED_CHAN: + break; + default: + BT_WARN("Unsupported info type %u", info_type); + return; + } + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + atomic_set_bit(l2cap->chan.flags, L2CAP_FLAG_SIG_INFO_PENDING); + l2cap->info_ident = l2cap_br_get_ident(); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_INFO_REQ; + hdr->ident = l2cap->info_ident; + hdr->len = sys_cpu_to_le16(sizeof(*info)); + + info = net_buf_add(buf, sizeof(*info)); + info->type = sys_cpu_to_le16(info_type); + + l2cap_br_chan_send_req(&l2cap->chan, buf, L2CAP_BR_INFO_TIMEOUT); +} + +static void connect_fixed_channel(struct bt_l2cap_br_chan *chan) +{ + if (atomic_test_and_set_bit(chan->flags, L2CAP_FLAG_FIXED_CONNECTED)) { + return; + } + + if (chan->chan.ops && chan->chan.ops->connected) { + chan->chan.ops->connected(&chan->chan); + } +} + +static void connect_optional_fixed_channels(struct bt_l2cap_br *l2cap) +{ + /* can be change to loop if more BR/EDR fixed channels are added */ + if (l2cap->info_fixed_chan & BIT(BT_L2CAP_CID_BR_SMP)) { + struct bt_l2cap_chan *chan; + + chan = bt_l2cap_br_lookup_rx_cid(l2cap->chan.chan.conn, + BT_L2CAP_CID_BR_SMP); + if (chan) { + connect_fixed_channel(BR_CHAN(chan)); + } + } +} + +static int l2cap_br_info_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_l2cap_info_rsp *rsp; + uint16_t type, result; + int err = 0; + + if (atomic_test_bit(l2cap->chan.flags, L2CAP_FLAG_SIG_INFO_DONE)) { + return 0; + } + + if (atomic_test_and_clear_bit(l2cap->chan.flags, + L2CAP_FLAG_SIG_INFO_PENDING)) { + /* + * Release RTX timer since got the response & there's pending + * command request. + */ + k_delayed_work_cancel(&l2cap->chan.chan.rtx_work); + } + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small info rsp packet size"); + err = -EINVAL; + goto done; + } + + if (ident != l2cap->info_ident) { + BT_WARN("Idents mismatch"); + err = -EINVAL; + goto done; + } + + rsp = net_buf_pull_mem(buf, sizeof(*rsp)); + result = sys_le16_to_cpu(rsp->result); + if (result != BT_L2CAP_INFO_SUCCESS) { + BT_WARN("Result unsuccessful"); + err = -EINVAL; + goto done; + } + + type = sys_le16_to_cpu(rsp->type); + + switch (type) { + case BT_L2CAP_INFO_FEAT_MASK: + l2cap->info_feat_mask = net_buf_pull_le32(buf); + BT_DBG("remote info mask 0x%08x", l2cap->info_feat_mask); + + if (!(l2cap->info_feat_mask & L2CAP_FEAT_FIXED_CHAN_MASK)) { + break; + } + + l2cap_br_get_info(l2cap, BT_L2CAP_INFO_FIXED_CHAN); + return 0; + case BT_L2CAP_INFO_FIXED_CHAN: + l2cap->info_fixed_chan = net_buf_pull_u8(buf); + BT_DBG("remote fixed channel mask 0x%02x", + l2cap->info_fixed_chan); + + connect_optional_fixed_channels(l2cap); + + break; + default: + BT_WARN("type 0x%04x unsupported", type); + err = -EINVAL; + break; + } +done: + atomic_set_bit(l2cap->chan.flags, L2CAP_FLAG_SIG_INFO_DONE); + l2cap->info_ident = 0U; + return err; +} + +static uint8_t get_fixed_channels_mask(void) +{ + uint8_t mask = 0U; + + /* this needs to be enhanced if AMP Test Manager support is added */ + Z_STRUCT_SECTION_FOREACH(bt_l2cap_br_fixed_chan, fchan) { + mask |= BIT(fchan->cid); + } + + return mask; +} + +static int l2cap_br_info_req(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_info_req *req = (void *)buf->data; + struct bt_l2cap_info_rsp *rsp; + struct net_buf *rsp_buf; + struct bt_l2cap_sig_hdr *hdr_info; + uint16_t type; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small info req packet size"); + return -EINVAL; + } + + rsp_buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + type = sys_le16_to_cpu(req->type); + BT_DBG("type 0x%04x", type); + + hdr_info = net_buf_add(rsp_buf, sizeof(*hdr_info)); + hdr_info->code = BT_L2CAP_INFO_RSP; + hdr_info->ident = ident; + + rsp = net_buf_add(rsp_buf, sizeof(*rsp)); + + switch (type) { + case BT_L2CAP_INFO_FEAT_MASK: + rsp->type = sys_cpu_to_le16(BT_L2CAP_INFO_FEAT_MASK); + rsp->result = sys_cpu_to_le16(BT_L2CAP_INFO_SUCCESS); + net_buf_add_le32(rsp_buf, L2CAP_FEAT_FIXED_CHAN_MASK); + hdr_info->len = sys_cpu_to_le16(sizeof(*rsp) + sizeof(uint32_t)); + break; + case BT_L2CAP_INFO_FIXED_CHAN: + rsp->type = sys_cpu_to_le16(BT_L2CAP_INFO_FIXED_CHAN); + rsp->result = sys_cpu_to_le16(BT_L2CAP_INFO_SUCCESS); + /* fixed channel mask protocol data is 8 octets wide */ + (void)memset(net_buf_add(rsp_buf, 8), 0, 8); + rsp->data[0] = get_fixed_channels_mask(); + + hdr_info->len = sys_cpu_to_le16(sizeof(*rsp) + 8); + break; + default: + rsp->type = req->type; + rsp->result = sys_cpu_to_le16(BT_L2CAP_INFO_NOTSUPP); + hdr_info->len = sys_cpu_to_le16(sizeof(*rsp)); + break; + } + + bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, rsp_buf); + + return 0; +} + +void bt_l2cap_br_connected(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan; + + Z_STRUCT_SECTION_FOREACH(bt_l2cap_br_fixed_chan, fchan) { + struct bt_l2cap_br_chan *ch; + + if (!fchan->accept) { + continue; + } + + if (fchan->accept(conn, &chan) < 0) { + continue; + } + + ch = BR_CHAN(chan); + + ch->rx.cid = fchan->cid; + ch->tx.cid = fchan->cid; + + if (!l2cap_br_chan_add(conn, chan, NULL)) { + return; + } + + /* + * other fixed channels will be connected after Information + * Response is received + */ + if (fchan->cid == BT_L2CAP_CID_BR_SIG) { + struct bt_l2cap_br *sig_ch; + + connect_fixed_channel(ch); + + sig_ch = CONTAINER_OF(ch, struct bt_l2cap_br, chan); + l2cap_br_get_info(sig_ch, BT_L2CAP_INFO_FEAT_MASK); + } + } +} + +static struct bt_l2cap_server *l2cap_br_server_lookup_psm(uint16_t psm) +{ + struct bt_l2cap_server *server; + + SYS_SLIST_FOR_EACH_CONTAINER(&br_servers, server, node) { + if (server->psm == psm) { + return server; + } + } + + return NULL; +} + +static void l2cap_br_conf_add_mtu(struct net_buf *buf, const uint16_t mtu) +{ + net_buf_add_u8(buf, BT_L2CAP_CONF_OPT_MTU); + net_buf_add_u8(buf, sizeof(mtu)); + net_buf_add_le16(buf, mtu); +} + +static void l2cap_br_conf(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_sig_hdr *hdr; + struct bt_l2cap_conf_req *conf; + struct net_buf *buf; + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONF_REQ; + hdr->ident = l2cap_br_get_ident(); + conf = net_buf_add(buf, sizeof(*conf)); + (void)memset(conf, 0, sizeof(*conf)); + + conf->dcid = sys_cpu_to_le16(BR_CHAN(chan)->tx.cid); + /* + * Add MTU option if app set non default BR/EDR L2CAP MTU, + * otherwise sent empty configuration data meaning default MTU + * to be used. + */ + if (BR_CHAN(chan)->rx.mtu != L2CAP_BR_DEFAULT_MTU) { + l2cap_br_conf_add_mtu(buf, BR_CHAN(chan)->rx.mtu); + } + + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + + /* + * TODO: + * might be needed to start tracking number of configuration iterations + * on both directions + */ + l2cap_br_chan_send_req(BR_CHAN(chan), buf, L2CAP_BR_CFG_TIMEOUT); +} + +enum l2cap_br_conn_security_result { + L2CAP_CONN_SECURITY_PASSED, + L2CAP_CONN_SECURITY_REJECT, + L2CAP_CONN_SECURITY_PENDING +}; + +/* + * Security helper against channel connection. + * Returns L2CAP_CONN_SECURITY_PASSED if: + * - existing security on link is applicable for requested PSM in connection, + * - legacy (non SSP) devices connecting with low security requirements, + * Returns L2CAP_CONN_SECURITY_PENDING if: + * - channel connection process is on hold since there were valid security + * conditions triggering authentication indirectly in subcall. + * Returns L2CAP_CONN_SECURITY_REJECT if: + * - bt_conn_set_security API returns < 0. + */ + +static enum l2cap_br_conn_security_result +l2cap_br_conn_security(struct bt_l2cap_chan *chan, const uint16_t psm) +{ + int check; + + /* For SDP PSM there's no need to change existing security on link */ + if (chan->required_sec_level == BT_SECURITY_L0) { + return L2CAP_CONN_SECURITY_PASSED; + } + + /* + * No link key needed for legacy devices (pre 2.1) and when low security + * level is required. + */ + if (chan->required_sec_level == BT_SECURITY_L1 && + !BT_FEAT_HOST_SSP(chan->conn->br.features)) { + return L2CAP_CONN_SECURITY_PASSED; + } + + switch (chan->required_sec_level) { + case BT_SECURITY_L4: + case BT_SECURITY_L3: + case BT_SECURITY_L2: + break; + default: + /* + * For non SDP PSM connections GAP's Security Mode 4 requires at + * least unauthenticated link key and enabled encryption if + * remote supports SSP before any L2CAP CoC traffic. So preset + * local to MEDIUM security to trigger it if needed. + */ + if (BT_FEAT_HOST_SSP(chan->conn->br.features)) { + chan->required_sec_level = BT_SECURITY_L2; + } + break; + } + + check = bt_conn_set_security(chan->conn, chan->required_sec_level); + + /* + * Check case when on existing connection security level already covers + * channel (service) security requirements against link security and + * bt_conn_set_security API returns 0 what implies also there was no + * need to trigger authentication. + */ + if (check == 0 && + chan->conn->sec_level >= chan->required_sec_level) { + return L2CAP_CONN_SECURITY_PASSED; + } + + /* + * If 'check' still holds 0, it means local host just sent HCI + * authentication command to start procedure to increase link security + * since service/profile requires that. + */ + if (check == 0) { + return L2CAP_CONN_SECURITY_PENDING; + }; + + /* + * For any other values in 'check' it means there was internal + * validation condition forbidding to start authentication at this + * moment. + */ + return L2CAP_CONN_SECURITY_REJECT; +} + +static void l2cap_br_send_conn_rsp(struct bt_conn *conn, uint16_t scid, + uint16_t dcid, uint8_t ident, uint16_t result) +{ + struct net_buf *buf; + struct bt_l2cap_conn_rsp *rsp; + struct bt_l2cap_sig_hdr *hdr; + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONN_RSP; + hdr->ident = ident; + hdr->len = sys_cpu_to_le16(sizeof(*rsp)); + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->dcid = sys_cpu_to_le16(dcid); + rsp->scid = sys_cpu_to_le16(scid); + rsp->result = sys_cpu_to_le16(result); + + if (result == BT_L2CAP_BR_PENDING) { + rsp->status = sys_cpu_to_le16(BT_L2CAP_CS_AUTHEN_PEND); + } else { + rsp->status = sys_cpu_to_le16(BT_L2CAP_CS_NO_INFO); + } + + bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf); +} + +static int l2cap_br_conn_req_reply(struct bt_l2cap_chan *chan, uint16_t result) +{ + /* Send response to connection request only when in acceptor role */ + if (!atomic_test_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_ACCEPTOR)) { + return -ESRCH; + } + + l2cap_br_send_conn_rsp(chan->conn, BR_CHAN(chan)->tx.cid, + BR_CHAN(chan)->rx.cid, chan->ident, result); + chan->ident = 0U; + + return 0; +} + +static void l2cap_br_conn_req(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_server *server; + struct bt_l2cap_conn_req *req = (void *)buf->data; + uint16_t psm, scid, result; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small L2CAP conn req packet size"); + return; + } + + psm = sys_le16_to_cpu(req->psm); + scid = sys_le16_to_cpu(req->scid); + + BT_DBG("psm 0x%02x scid 0x%04x", psm, scid); + + /* Check if there is a server registered */ + server = l2cap_br_server_lookup_psm(psm); + if (!server) { + result = BT_L2CAP_BR_ERR_PSM_NOT_SUPP; + goto no_chan; + } + + /* + * Report security violation for non SDP channel without encryption when + * remote supports SSP. + */ + if (server->sec_level != BT_SECURITY_L0 && + BT_FEAT_HOST_SSP(conn->br.features) && !conn->encrypt) { + result = BT_L2CAP_BR_ERR_SEC_BLOCK; + goto no_chan; + } + + if (!L2CAP_BR_CID_IS_DYN(scid)) { + result = BT_L2CAP_BR_ERR_INVALID_SCID; + goto no_chan; + } + + chan = bt_l2cap_br_lookup_tx_cid(conn, scid); + if (chan) { + /* + * we have a chan here but this is due to SCID being already in + * use so it is not channel we are suppose to pass to + * l2cap_br_conn_req_reply as wrong DCID would be used + */ + result = BT_L2CAP_BR_ERR_SCID_IN_USE; + goto no_chan; + } + + /* + * Request server to accept the new connection and allocate the + * channel. If no free channels available for PSM server reply with + * proper result and quit since chan pointer is uninitialized then. + */ + if (server->accept(conn, &chan) < 0) { + result = BT_L2CAP_BR_ERR_NO_RESOURCES; + goto no_chan; + } + + chan->required_sec_level = server->sec_level; + + l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy); + BR_CHAN(chan)->tx.cid = scid; + chan->ident = ident; + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECT); + atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_ACCEPTOR); + + /* Disable fragmentation of l2cap rx pdu */ + BR_CHAN(chan)->rx.mtu = MIN(BR_CHAN(chan)->rx.mtu, + CONFIG_BT_L2CAP_RX_MTU); + + switch (l2cap_br_conn_security(chan, psm)) { + case L2CAP_CONN_SECURITY_PENDING: + result = BT_L2CAP_BR_PENDING; + /* TODO: auth timeout */ + break; + case L2CAP_CONN_SECURITY_PASSED: + result = BT_L2CAP_BR_SUCCESS; + break; + case L2CAP_CONN_SECURITY_REJECT: + default: + result = BT_L2CAP_BR_ERR_SEC_BLOCK; + break; + } + /* Reply on connection request as acceptor */ + l2cap_br_conn_req_reply(chan, result); + + if (result != BT_L2CAP_BR_SUCCESS) { + /* Disconnect link when security rules were violated */ + if (result == BT_L2CAP_BR_ERR_SEC_BLOCK) { + bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL); + } + + return; + } + + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONFIG); + l2cap_br_conf(chan); + return; + +no_chan: + l2cap_br_send_conn_rsp(conn, scid, 0, ident, result); +} + +static void l2cap_br_conf_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, + uint16_t len, struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_conf_rsp *rsp = (void *)buf->data; + uint16_t flags, scid, result, opt_len; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small L2CAP conf rsp packet size"); + return; + } + + flags = sys_le16_to_cpu(rsp->flags); + scid = sys_le16_to_cpu(rsp->scid); + result = sys_le16_to_cpu(rsp->result); + opt_len = len - sizeof(*rsp); + + BT_DBG("scid 0x%04x flags 0x%02x result 0x%02x len %u", scid, flags, + result, opt_len); + + chan = bt_l2cap_br_lookup_rx_cid(conn, scid); + if (!chan) { + BT_ERR("channel mismatch!"); + return; + } + + /* Release RTX work since got the response */ + k_delayed_work_cancel(&chan->rtx_work); + + /* + * TODO: handle other results than success and parse response data if + * available + */ + switch (result) { + case BT_L2CAP_CONF_SUCCESS: + atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_LCONF_DONE); + + if (chan->state == BT_L2CAP_CONFIG && + atomic_test_bit(BR_CHAN(chan)->flags, + L2CAP_FLAG_CONN_RCONF_DONE)) { + BT_DBG("scid 0x%04x rx MTU %u dcid 0x%04x tx MTU %u", + BR_CHAN(chan)->rx.cid, BR_CHAN(chan)->rx.mtu, + BR_CHAN(chan)->tx.cid, BR_CHAN(chan)->tx.mtu); + + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECTED); + if (chan->ops && chan->ops->connected) { + chan->ops->connected(chan); + } + } + break; + default: + /* currently disconnect channel on non success result */ + bt_l2cap_chan_disconnect(chan); + break; + } +} + +int bt_l2cap_br_server_register(struct bt_l2cap_server *server) +{ + if (server->psm < L2CAP_BR_PSM_START || !server->accept) { + return -EINVAL; + } + + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((server->psm & 0x0101) != 0x0001) { + return -EINVAL; + } + + if (server->sec_level > BT_SECURITY_L4) { + return -EINVAL; + } else if (server->sec_level == BT_SECURITY_L0 && + server->psm != L2CAP_BR_PSM_SDP) { + server->sec_level = BT_SECURITY_L1; + } + + /* Check if given PSM is already in use */ + if (l2cap_br_server_lookup_psm(server->psm)) { + BT_DBG("PSM already registered"); + return -EADDRINUSE; + } + + BT_DBG("PSM 0x%04x", server->psm); + + sys_slist_append(&br_servers, &server->node); + + return 0; +} + +static void l2cap_br_send_reject(struct bt_conn *conn, uint8_t ident, + uint16_t reason, void *data, uint8_t data_len) +{ + struct bt_l2cap_cmd_reject *rej; + struct bt_l2cap_sig_hdr *hdr; + struct net_buf *buf; + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CMD_REJECT; + hdr->ident = ident; + hdr->len = sys_cpu_to_le16(sizeof(*rej) + data_len); + + rej = net_buf_add(buf, sizeof(*rej)); + rej->reason = sys_cpu_to_le16(reason); + + /* + * optional data if available must be already in little-endian format + * made by caller.and be compliant with Core 4.2 [Vol 3, Part A, 4.1, + * table 4.4] + */ + if (data) { + net_buf_add_mem(buf, data, data_len); + } + + bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf); +} + +static uint16_t l2cap_br_conf_opt_mtu(struct bt_l2cap_chan *chan, + struct net_buf *buf, size_t len) +{ + uint16_t mtu, result = BT_L2CAP_CONF_SUCCESS; + + /* Core 4.2 [Vol 3, Part A, 5.1] MTU payload length */ + if (len != 2) { + BT_ERR("tx MTU length %zu invalid", len); + result = BT_L2CAP_CONF_REJECT; + goto done; + } + + /* pulling MTU value moves buf data to next option item */ + mtu = net_buf_pull_le16(buf); + if (mtu < L2CAP_BR_MIN_MTU) { + result = BT_L2CAP_CONF_UNACCEPT; + BR_CHAN(chan)->tx.mtu = L2CAP_BR_MIN_MTU; + BT_DBG("tx MTU %u invalid", mtu); + goto done; + } + + BR_CHAN(chan)->tx.mtu = mtu; + BT_DBG("tx MTU %u", mtu); +done: + return result; +} + +static void l2cap_br_conf_req(struct bt_l2cap_br *l2cap, uint8_t ident, + uint16_t len, struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_conf_req *req; + struct bt_l2cap_sig_hdr *hdr; + struct bt_l2cap_conf_rsp *rsp; + struct bt_l2cap_conf_opt *opt; + uint16_t flags, dcid, opt_len, hint, result = BT_L2CAP_CONF_SUCCESS; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small L2CAP conf req packet size"); + return; + } + + req = net_buf_pull_mem(buf, sizeof(*req)); + flags = sys_le16_to_cpu(req->flags); + dcid = sys_le16_to_cpu(req->dcid); + opt_len = len - sizeof(*req); + + BT_DBG("dcid 0x%04x flags 0x%02x len %u", dcid, flags, opt_len); + + chan = bt_l2cap_br_lookup_rx_cid(conn, dcid); + if (!chan) { + BT_ERR("rx channel mismatch!"); + struct bt_l2cap_cmd_reject_cid_data data = {.scid = req->dcid, + .dcid = 0, + }; + + l2cap_br_send_reject(conn, ident, BT_L2CAP_REJ_INVALID_CID, + &data, sizeof(data)); + return; + } + + if (!opt_len) { + BT_DBG("tx default MTU %u", L2CAP_BR_DEFAULT_MTU); + BR_CHAN(chan)->tx.mtu = L2CAP_BR_DEFAULT_MTU; + goto send_rsp; + } + + while (buf->len >= sizeof(*opt)) { + opt = net_buf_pull_mem(buf, sizeof(*opt)); + + /* make sure opt object can get safe dereference in iteration */ + if (buf->len < opt->len) { + BT_ERR("Received too short option data"); + result = BT_L2CAP_CONF_REJECT; + break; + } + + hint = opt->type & BT_L2CAP_CONF_HINT; + + switch (opt->type & BT_L2CAP_CONF_MASK) { + case BT_L2CAP_CONF_OPT_MTU: + /* getting MTU modifies buf internals */ + result = l2cap_br_conf_opt_mtu(chan, buf, opt->len); + /* + * MTU is done. For now bailout the loop but later on + * there can be a need to continue checking next options + * that are after MTU value and then goto is not proper + * way out here. + */ + goto send_rsp; + default: + if (!hint) { + BT_DBG("option %u not handled", opt->type); + goto send_rsp; + } + + /* Update buffer to point at next option */ + net_buf_pull(buf, opt->len); + break; + } + } + +send_rsp: + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONF_RSP; + hdr->ident = ident; + rsp = net_buf_add(buf, sizeof(*rsp)); + (void)memset(rsp, 0, sizeof(*rsp)); + + rsp->result = sys_cpu_to_le16(result); + rsp->scid = sys_cpu_to_le16(BR_CHAN(chan)->tx.cid); + + /* + * TODO: If options other than MTU bacame meaningful then processing + * the options chain need to be modified and taken into account when + * sending back to peer. + */ + if (result == BT_L2CAP_CONF_UNACCEPT) { + l2cap_br_conf_add_mtu(buf, BR_CHAN(chan)->tx.mtu); + } + + hdr->len = sys_cpu_to_le16(buf->len - sizeof(*hdr)); + + bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf); + + if (result != BT_L2CAP_CONF_SUCCESS) { + return; + } + + atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_RCONF_DONE); + + if (atomic_test_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_LCONF_DONE) && + chan->state == BT_L2CAP_CONFIG) { + BT_DBG("scid 0x%04x rx MTU %u dcid 0x%04x tx MTU %u", + BR_CHAN(chan)->rx.cid, BR_CHAN(chan)->rx.mtu, + BR_CHAN(chan)->tx.cid, BR_CHAN(chan)->tx.mtu); + + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECTED); + if (chan->ops && chan->ops->connected) { + chan->ops->connected(chan); + } + } +} + +static struct bt_l2cap_br_chan *l2cap_br_remove_tx_cid(struct bt_conn *conn, + uint16_t cid) +{ + struct bt_l2cap_chan *chan; + sys_snode_t *prev = NULL; + + /* Protect fixed channels against accidental removal */ + if (!L2CAP_BR_CID_IS_DYN(cid)) { + return NULL; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + if (BR_CHAN(chan)->tx.cid == cid) { + sys_slist_remove(&conn->channels, prev, &chan->node); + return BR_CHAN(chan); + } + + prev = &chan->node; + } + + return NULL; +} + +static void l2cap_br_disconn_req(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_br_chan *chan; + struct bt_l2cap_disconn_req *req = (void *)buf->data; + struct bt_l2cap_disconn_rsp *rsp; + struct bt_l2cap_sig_hdr *hdr; + uint16_t scid, dcid; + + if (buf->len < sizeof(*req)) { + BT_ERR("Too small disconn req packet size"); + return; + } + + dcid = sys_le16_to_cpu(req->dcid); + scid = sys_le16_to_cpu(req->scid); + + BT_DBG("scid 0x%04x dcid 0x%04x", dcid, scid); + + chan = l2cap_br_remove_tx_cid(conn, scid); + if (!chan) { + struct bt_l2cap_cmd_reject_cid_data data; + + data.scid = req->scid; + data.dcid = req->dcid; + l2cap_br_send_reject(conn, ident, BT_L2CAP_REJ_INVALID_CID, + &data, sizeof(data)); + return; + } + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_DISCONN_RSP; + hdr->ident = ident; + hdr->len = sys_cpu_to_le16(sizeof(*rsp)); + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->dcid = sys_cpu_to_le16(chan->rx.cid); + rsp->scid = sys_cpu_to_le16(chan->tx.cid); + + bt_l2cap_chan_del(&chan->chan); + + bt_l2cap_send(conn, BT_L2CAP_CID_BR_SIG, buf); +} + +static void l2cap_br_connected(struct bt_l2cap_chan *chan) +{ + BT_DBG("ch %p cid 0x%04x", BR_CHAN(chan), BR_CHAN(chan)->rx.cid); +} + +static void l2cap_br_disconnected(struct bt_l2cap_chan *chan) +{ + BT_DBG("ch %p cid 0x%04x", BR_CHAN(chan), BR_CHAN(chan)->rx.cid); + + if (atomic_test_and_clear_bit(BR_CHAN(chan)->flags, + L2CAP_FLAG_SIG_INFO_PENDING)) { + /* Cancel RTX work on signal channel */ + k_delayed_work_cancel(&chan->rtx_work); + } +} + +int bt_l2cap_br_chan_disconnect(struct bt_l2cap_chan *chan) +{ + struct bt_conn *conn = chan->conn; + struct net_buf *buf; + struct bt_l2cap_disconn_req *req; + struct bt_l2cap_sig_hdr *hdr; + struct bt_l2cap_br_chan *ch; + + if (!conn) { + return -ENOTCONN; + } + + if (chan->state == BT_L2CAP_DISCONNECT) { + return -EALREADY; + } + + ch = BR_CHAN(chan); + + BT_DBG("chan %p scid 0x%04x dcid 0x%04x", chan, ch->rx.cid, + ch->tx.cid); + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_DISCONN_REQ; + hdr->ident = l2cap_br_get_ident(); + hdr->len = sys_cpu_to_le16(sizeof(*req)); + + req = net_buf_add(buf, sizeof(*req)); + req->dcid = sys_cpu_to_le16(ch->tx.cid); + req->scid = sys_cpu_to_le16(ch->rx.cid); + + l2cap_br_chan_send_req(ch, buf, L2CAP_BR_DISCONN_TIMEOUT); + bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECT); + + return 0; +} + +static void l2cap_br_disconn_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_br_chan *chan; + struct bt_l2cap_disconn_rsp *rsp = (void *)buf->data; + uint16_t dcid, scid; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small disconn rsp packet size"); + return; + } + + dcid = sys_le16_to_cpu(rsp->dcid); + scid = sys_le16_to_cpu(rsp->scid); + + BT_DBG("dcid 0x%04x scid 0x%04x", dcid, scid); + + chan = l2cap_br_remove_tx_cid(conn, dcid); + if (!chan) { + BT_WARN("No dcid 0x%04x channel found", dcid); + return; + } + + bt_l2cap_chan_del(&chan->chan); +} + +int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, + uint16_t psm) +{ + struct net_buf *buf; + struct bt_l2cap_sig_hdr *hdr; + struct bt_l2cap_conn_req *req; + + if (!psm) { + return -EINVAL; + } + + if (chan->psm) { + return -EEXIST; + } + + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((psm & 0x0101) != 0x0001) { + return -EINVAL; + } + + if (chan->required_sec_level > BT_SECURITY_L4) { + return -EINVAL; + } else if (chan->required_sec_level == BT_SECURITY_L0 && + psm != L2CAP_BR_PSM_SDP) { + chan->required_sec_level = BT_SECURITY_L1; + } + + switch (chan->state) { + case BT_L2CAP_CONNECTED: + /* Already connected */ + return -EISCONN; + case BT_L2CAP_DISCONNECTED: + /* Can connect */ + break; + case BT_L2CAP_CONFIG: + case BT_L2CAP_DISCONNECT: + default: + /* Bad context */ + return -EBUSY; + } + + if (!l2cap_br_chan_add(conn, chan, l2cap_br_chan_destroy)) { + return -ENOMEM; + } + + chan->psm = psm; + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECT); + atomic_set_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_PENDING); + + switch (l2cap_br_conn_security(chan, psm)) { + case L2CAP_CONN_SECURITY_PENDING: + /* + * Authentication was triggered, wait with sending request on + * connection security changed callback context. + */ + return 0; + case L2CAP_CONN_SECURITY_PASSED: + break; + case L2CAP_CONN_SECURITY_REJECT: + default: + l2cap_br_chan_cleanup(chan); + return -EIO; + } + + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONN_REQ; + hdr->ident = l2cap_br_get_ident(); + hdr->len = sys_cpu_to_le16(sizeof(*req)); + + req = net_buf_add(buf, sizeof(*req)); + req->psm = sys_cpu_to_le16(psm); + req->scid = sys_cpu_to_le16(BR_CHAN(chan)->rx.cid); + + l2cap_br_chan_send_req(BR_CHAN(chan), buf, L2CAP_BR_CONN_TIMEOUT); + + return 0; +} + +static void l2cap_br_conn_rsp(struct bt_l2cap_br *l2cap, uint8_t ident, + struct net_buf *buf) +{ + struct bt_conn *conn = l2cap->chan.chan.conn; + struct bt_l2cap_chan *chan; + struct bt_l2cap_conn_rsp *rsp = (void *)buf->data; + uint16_t dcid, scid, result, status; + + if (buf->len < sizeof(*rsp)) { + BT_ERR("Too small L2CAP conn rsp packet size"); + return; + } + + dcid = sys_le16_to_cpu(rsp->dcid); + scid = sys_le16_to_cpu(rsp->scid); + result = sys_le16_to_cpu(rsp->result); + status = sys_le16_to_cpu(rsp->status); + + BT_DBG("dcid 0x%04x scid 0x%04x result %u status %u", dcid, scid, + result, status); + + chan = bt_l2cap_br_lookup_rx_cid(conn, scid); + if (!chan) { + BT_ERR("No scid 0x%04x channel found", scid); + return; + } + + /* Release RTX work since got the response */ + k_delayed_work_cancel(&chan->rtx_work); + + if (chan->state != BT_L2CAP_CONNECT) { + BT_DBG("Invalid channel %p state %s", chan, + bt_l2cap_chan_state_str(chan->state)); + return; + } + + switch (result) { + case BT_L2CAP_BR_SUCCESS: + chan->ident = 0U; + BR_CHAN(chan)->tx.cid = dcid; + l2cap_br_conf(chan); + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONFIG); + atomic_clear_bit(BR_CHAN(chan)->flags, L2CAP_FLAG_CONN_PENDING); + break; + case BT_L2CAP_BR_PENDING: + k_delayed_work_submit(&chan->rtx_work, L2CAP_BR_CONN_TIMEOUT); + break; + default: + l2cap_br_chan_cleanup(chan); + break; + } +} + +int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_l2cap_br_chan *ch = BR_CHAN(chan); + + if (buf->len > ch->tx.mtu) { + return -EMSGSIZE; + } + + bt_l2cap_send(ch->chan.conn, ch->tx.cid, buf); + + return buf->len; +} + +static int l2cap_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_l2cap_br *l2cap = CONTAINER_OF(chan, struct bt_l2cap_br, chan); + struct bt_l2cap_sig_hdr *hdr; + uint16_t len; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small L2CAP signaling PDU"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + len = sys_le16_to_cpu(hdr->len); + + BT_DBG("Signaling code 0x%02x ident %u len %u", hdr->code, + hdr->ident, len); + + if (buf->len != len) { + BT_ERR("L2CAP length mismatch (%u != %u)", buf->len, len); + return 0; + } + + if (!hdr->ident) { + BT_ERR("Invalid ident value in L2CAP PDU"); + return 0; + } + + switch (hdr->code) { + case BT_L2CAP_INFO_RSP: + l2cap_br_info_rsp(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_INFO_REQ: + l2cap_br_info_req(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_DISCONN_REQ: + l2cap_br_disconn_req(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_CONN_REQ: + l2cap_br_conn_req(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_CONF_RSP: + l2cap_br_conf_rsp(l2cap, hdr->ident, len, buf); + break; + case BT_L2CAP_CONF_REQ: + l2cap_br_conf_req(l2cap, hdr->ident, len, buf); + break; + case BT_L2CAP_DISCONN_RSP: + l2cap_br_disconn_rsp(l2cap, hdr->ident, buf); + break; + case BT_L2CAP_CONN_RSP: + l2cap_br_conn_rsp(l2cap, hdr->ident, buf); + break; + default: + BT_WARN("Unknown/Unsupported L2CAP PDU code 0x%02x", hdr->code); + l2cap_br_send_reject(chan->conn, hdr->ident, + BT_L2CAP_REJ_NOT_UNDERSTOOD, NULL, 0); + break; + } + + return 0; +} + +static void l2cap_br_conn_pend(struct bt_l2cap_chan *chan, uint8_t status) +{ + struct net_buf *buf; + struct bt_l2cap_sig_hdr *hdr; + struct bt_l2cap_conn_req *req; + + if (chan->state != BT_L2CAP_CONNECT) { + return; + } + + BT_DBG("chan %p status 0x%02x encr 0x%02x", chan, status, + chan->conn->encrypt); + + if (status) { + /* + * Security procedure status is non-zero so respond with + * security violation only as channel acceptor. + */ + l2cap_br_conn_req_reply(chan, BT_L2CAP_BR_ERR_SEC_BLOCK); + + /* Release channel allocated to outgoing connection request */ + if (atomic_test_bit(BR_CHAN(chan)->flags, + L2CAP_FLAG_CONN_PENDING)) { + l2cap_br_chan_cleanup(chan); + } + + return; + } + + if (!chan->conn->encrypt) { + return; + } + + /* + * For incoming connection state send confirming outstanding + * response and initiate configuration request. + */ + if (l2cap_br_conn_req_reply(chan, BT_L2CAP_BR_SUCCESS) == 0) { + bt_l2cap_chan_set_state(chan, BT_L2CAP_CONFIG); + /* + * Initialize config request since remote needs to know + * local MTU segmentation. + */ + l2cap_br_conf(chan); + } else if (atomic_test_and_clear_bit(BR_CHAN(chan)->flags, + L2CAP_FLAG_CONN_PENDING)) { + buf = bt_l2cap_create_pdu(&br_sig_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_L2CAP_CONN_REQ; + hdr->ident = l2cap_br_get_ident(); + hdr->len = sys_cpu_to_le16(sizeof(*req)); + + req = net_buf_add(buf, sizeof(*req)); + req->psm = sys_cpu_to_le16(chan->psm); + req->scid = sys_cpu_to_le16(BR_CHAN(chan)->rx.cid); + + l2cap_br_chan_send_req(BR_CHAN(chan), buf, + L2CAP_BR_CONN_TIMEOUT); + } +} + +void l2cap_br_encrypt_change(struct bt_conn *conn, uint8_t hci_status) +{ + struct bt_l2cap_chan *chan; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) { + l2cap_br_conn_pend(chan, hci_status); + + if (chan->ops && chan->ops->encrypt_change) { + chan->ops->encrypt_change(chan, hci_status); + } + } +} + +static void check_fixed_channel(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_br_chan *br_chan = BR_CHAN(chan); + + if (br_chan->rx.cid < L2CAP_BR_CID_DYN_START) { + connect_fixed_channel(br_chan); + } +} + +void bt_l2cap_br_recv(struct bt_conn *conn, struct net_buf *buf) +{ + struct bt_l2cap_hdr *hdr; + struct bt_l2cap_chan *chan; + uint16_t cid; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small L2CAP PDU received"); + net_buf_unref(buf); + return; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + cid = sys_le16_to_cpu(hdr->cid); + + chan = bt_l2cap_br_lookup_rx_cid(conn, cid); + if (!chan) { + BT_WARN("Ignoring data for unknown CID 0x%04x", cid); + net_buf_unref(buf); + return; + } + + /* + * if data was received for fixed channel before Information + * Response we connect channel here. + */ + check_fixed_channel(chan); + + chan->ops->recv(chan, buf); + net_buf_unref(buf); +} + +static int l2cap_br_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + int i; + static const struct bt_l2cap_chan_ops ops = { + .connected = l2cap_br_connected, + .disconnected = l2cap_br_disconnected, + .recv = l2cap_br_recv, + }; + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_l2cap_br_pool); i++) { + struct bt_l2cap_br *l2cap = &bt_l2cap_br_pool[i]; + + if (l2cap->chan.chan.conn) { + continue; + } + + l2cap->chan.chan.ops = &ops; + *chan = &l2cap->chan.chan; + atomic_set(l2cap->chan.flags, 0); + return 0; + } + + BT_ERR("No available L2CAP context for conn %p", conn); + + return -ENOMEM; +} + +BT_L2CAP_BR_CHANNEL_DEFINE(br_fixed_chan, BT_L2CAP_CID_BR_SIG, l2cap_br_accept); + +void bt_l2cap_br_init(void) +{ +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + k_lifo_init(&br_sig_pool.free, CONFIG_BT_MAX_CONN); + net_buf_init(&br_sig_pool, CONFIG_BT_MAX_CONN, BT_L2CAP_BUF_SIZE(L2CAP_BR_MIN_MTU), NULL); +#endif + sys_slist_init(&br_servers); + + bt_sdp_init(); + + if (IS_ENABLED(CONFIG_BT_RFCOMM)) { + bt_rfcomm_init(); + } + + if (IS_ENABLED(CONFIG_BT_HFP)) { + bt_hfp_hf_init(); + } + + if (IS_ENABLED(CONFIG_BT_AVDTP)) { + bt_avdtp_init(); + } + + if (IS_ENABLED(CONFIG_BT_A2DP)) { + bt_a2dp_init(); + } + +} diff --git a/components/ble/ble_stack/host/l2cap_internal.h b/components/ble/ble_stack/host/l2cap_internal.h new file mode 100644 index 00000000..0e5376b2 --- /dev/null +++ b/components/ble/ble_stack/host/l2cap_internal.h @@ -0,0 +1,340 @@ +/** @file + * @brief Internal APIs for Bluetooth L2CAP handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +enum l2cap_conn_list_action { + BT_L2CAP_CHAN_LOOKUP, + BT_L2CAP_CHAN_DETACH, +}; + +#define BT_L2CAP_CID_BR_SIG 0x0001 +#define BT_L2CAP_CID_ATT 0x0004 +#define BT_L2CAP_CID_LE_SIG 0x0005 +#define BT_L2CAP_CID_SMP 0x0006 +#define BT_L2CAP_CID_BR_SMP 0x0007 + +#define BT_L2CAP_PSM_RFCOMM 0x0003 + +struct bt_l2cap_hdr { + u16_t len; + u16_t cid; +} __packed; + +struct bt_l2cap_sig_hdr { + u8_t code; + u8_t ident; + u16_t len; +} __packed; + +#define BT_L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define BT_L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define BT_L2CAP_REJ_INVALID_CID 0x0002 + +#define BT_L2CAP_CMD_REJECT 0x01 +struct bt_l2cap_cmd_reject { + u16_t reason; + u8_t data[0]; +} __packed; + +struct bt_l2cap_cmd_reject_cid_data { + u16_t scid; + u16_t dcid; +} __packed; + +#define BT_L2CAP_CONN_REQ 0x02 +struct bt_l2cap_conn_req { + u16_t psm; + u16_t scid; +} __packed; + +/* command statuses in reposnse */ +#define BT_L2CAP_CS_NO_INFO 0x0000 +#define BT_L2CAP_CS_AUTHEN_PEND 0x0001 + +/* valid results in conn response on BR/EDR */ +#define BT_L2CAP_BR_SUCCESS 0x0000 +#define BT_L2CAP_BR_PENDING 0x0001 +#define BT_L2CAP_BR_ERR_PSM_NOT_SUPP 0x0002 +#define BT_L2CAP_BR_ERR_SEC_BLOCK 0x0003 +#define BT_L2CAP_BR_ERR_NO_RESOURCES 0x0004 +#define BT_L2CAP_BR_ERR_INVALID_SCID 0x0006 +#define BT_L2CAP_BR_ERR_SCID_IN_USE 0x0007 + +#define BT_L2CAP_CONN_RSP 0x03 +struct bt_l2cap_conn_rsp { + u16_t dcid; + u16_t scid; + u16_t result; + u16_t status; +} __packed; + +#define BT_L2CAP_CONF_SUCCESS 0x0000 +#define BT_L2CAP_CONF_UNACCEPT 0x0001 +#define BT_L2CAP_CONF_REJECT 0x0002 + +#define BT_L2CAP_CONF_REQ 0x04 +struct bt_l2cap_conf_req { + u16_t dcid; + u16_t flags; + u8_t data[0]; +} __packed; + +#define BT_L2CAP_CONF_RSP 0x05 +struct bt_l2cap_conf_rsp { + u16_t scid; + u16_t flags; + u16_t result; + u8_t data[0]; +} __packed; + +/* Option type used by MTU config request data */ +#define BT_L2CAP_CONF_OPT_MTU 0x01 +/* Options bits selecting most significant bit (hint) in type field */ +#define BT_L2CAP_CONF_HINT 0x80 +#define BT_L2CAP_CONF_MASK 0x7f + +struct bt_l2cap_conf_opt { + u8_t type; + u8_t len; + u8_t data[0]; +} __packed; + +#define BT_L2CAP_DISCONN_REQ 0x06 +struct bt_l2cap_disconn_req { + u16_t dcid; + u16_t scid; +} __packed; + +#define BT_L2CAP_DISCONN_RSP 0x07 +struct bt_l2cap_disconn_rsp { + u16_t dcid; + u16_t scid; +} __packed; + +#define BT_L2CAP_INFO_FEAT_MASK 0x0002 +#define BT_L2CAP_INFO_FIXED_CHAN 0x0003 + +#define BT_L2CAP_INFO_REQ 0x0a +struct bt_l2cap_info_req { + u16_t type; +} __packed; + +/* info result */ +#define BT_L2CAP_INFO_SUCCESS 0x0000 +#define BT_L2CAP_INFO_NOTSUPP 0x0001 + +#define BT_L2CAP_INFO_RSP 0x0b +struct bt_l2cap_info_rsp { + u16_t type; + u16_t result; + u8_t data[0]; +} __packed; + +#define BT_L2CAP_CONN_PARAM_REQ 0x12 +struct bt_l2cap_conn_param_req { + u16_t min_interval; + u16_t max_interval; + u16_t latency; + u16_t timeout; +} __packed; + +#define BT_L2CAP_CONN_PARAM_ACCEPTED 0x0000 +#define BT_L2CAP_CONN_PARAM_REJECTED 0x0001 + +#define BT_L2CAP_CONN_PARAM_RSP 0x13 +struct bt_l2cap_conn_param_rsp { + u16_t result; +} __packed; + +#define BT_L2CAP_LE_CONN_REQ 0x14 +struct bt_l2cap_le_conn_req { + u16_t psm; + u16_t scid; + u16_t mtu; + u16_t mps; + u16_t credits; +} __packed; + +/* valid results in conn response on LE */ +#define BT_L2CAP_LE_SUCCESS 0x0000 +#define BT_L2CAP_LE_ERR_PSM_NOT_SUPP 0x0002 +#define BT_L2CAP_LE_ERR_NO_RESOURCES 0x0004 +#define BT_L2CAP_LE_ERR_AUTHENTICATION 0x0005 +#define BT_L2CAP_LE_ERR_AUTHORIZATION 0x0006 +#define BT_L2CAP_LE_ERR_KEY_SIZE 0x0007 +#define BT_L2CAP_LE_ERR_ENCRYPTION 0x0008 +#define BT_L2CAP_LE_ERR_INVALID_SCID 0x0009 +#define BT_L2CAP_LE_ERR_SCID_IN_USE 0x000A +#define BT_L2CAP_LE_ERR_UNACCEPT_PARAMS 0x000B + +#define BT_L2CAP_LE_CONN_RSP 0x15 +struct bt_l2cap_le_conn_rsp { + u16_t dcid; + u16_t mtu; + u16_t mps; + u16_t credits; + u16_t result; +}; + +#define BT_L2CAP_LE_CREDITS 0x16 +struct bt_l2cap_le_credits { + u16_t cid; + u16_t credits; +} __packed; + +#define BT_L2CAP_SDU_HDR_LEN 2 + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +#define BT_L2CAP_RX_MTU CONFIG_BT_L2CAP_RX_MTU +#else +#define BT_L2CAP_RX_MTU (CONFIG_BT_RX_BUF_LEN - \ + BT_HCI_ACL_HDR_SIZE - BT_L2CAP_HDR_SIZE) +#endif + +struct bt_l2cap_fixed_chan { + u16_t cid; + int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan); + sys_snode_t node; +}; + +#define BT_L2CAP_CHANNEL_DEFINE(_name, _cid, _accept) \ + const Z_STRUCT_SECTION_ITERABLE(bt_l2cap_fixed_chan, _name) = { \ + .cid = _cid, \ + .accept = _accept, \ + } + +/* Need a name different than bt_l2cap_fixed_chan for a different section */ +struct bt_l2cap_br_fixed_chan { + u16_t cid; + int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan); +}; + +#define BT_L2CAP_BR_CHANNEL_DEFINE(_name, _cid, _accept) \ + const Z_STRUCT_SECTION_ITERABLE(bt_l2cap_br_fixed_chan, _name) = { \ + .cid = _cid, \ + .accept = _accept, \ + } + +void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data); +/* Register a fixed L2CAP channel for L2CAP */ +void bt_l2cap_le_fixed_chan_register(struct bt_l2cap_fixed_chan *chan); + +/* Notify L2CAP channels of a new connection */ +void bt_l2cap_connected(struct bt_conn *conn); + +/* Notify L2CAP channels of a disconnect event */ +void bt_l2cap_disconnected(struct bt_conn *conn); + +/* Add channel to the connection */ +void bt_l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan, + bt_l2cap_chan_destroy_t destroy); + +/* Remove channel from the connection */ +void bt_l2cap_chan_remove(struct bt_conn *conn, struct bt_l2cap_chan *chan); + +/* Delete channel */ +void bt_l2cap_chan_del(struct bt_l2cap_chan *chan); + +const char *bt_l2cap_chan_state_str(bt_l2cap_chan_state_t state); + +#if defined(CONFIG_BT_DEBUG_L2CAP) +void bt_l2cap_chan_set_state_debug(struct bt_l2cap_chan *chan, + bt_l2cap_chan_state_t state, + const char *func, int line); +#define bt_l2cap_chan_set_state(_chan, _state) \ + bt_l2cap_chan_set_state_debug(_chan, _state, __func__, __LINE__) +#else +void bt_l2cap_chan_set_state(struct bt_l2cap_chan *chan, + bt_l2cap_chan_state_t state); +#endif /* CONFIG_BT_DEBUG_L2CAP */ + +/* + * Notify L2CAP channels of a change in encryption state passing additionally + * HCI status of performed security procedure. + */ +void bt_l2cap_encrypt_change(struct bt_conn *conn, u8_t hci_status); + +/* Prepare an L2CAP PDU to be sent over a connection */ +struct net_buf *bt_l2cap_create_pdu_timeout(struct net_buf_pool *pool, + size_t reserve, s32_t timeout); + +#define bt_l2cap_create_pdu(_pool, _reserve) \ + bt_l2cap_create_pdu_timeout(_pool, _reserve, K_FOREVER) + +/* Prepare a L2CAP Response PDU to be sent over a connection */ +struct net_buf *bt_l2cap_create_rsp(struct net_buf *buf, size_t reserve); + +/* Send L2CAP PDU over a connection + * + * Buffer ownership is transferred to stack so either in case of success + * or error the buffer will be unref internally. + * + * Calling this from RX thread is assumed to never fail so the return can be + * ignored. + */ +int bt_l2cap_send_cb(struct bt_conn *conn, u16_t cid, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data); + +static inline void bt_l2cap_send(struct bt_conn *conn, u16_t cid, + struct net_buf *buf) +{ + bt_l2cap_send_cb(conn, cid, buf, NULL, NULL); +} + +/* Receive a new L2CAP PDU from a connection */ +void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf); + +/* Perform connection parameter update request */ +int bt_l2cap_update_conn_param(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +/* Initialize L2CAP and supported channels */ +void bt_l2cap_init(void); + +/* Lookup channel by Transmission CID */ +struct bt_l2cap_chan *bt_l2cap_le_lookup_tx_cid(struct bt_conn *conn, + u16_t cid); + +/* Lookup channel by Receiver CID */ +struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn, + u16_t cid); + +/* Initialize BR/EDR L2CAP signal layer */ +void bt_l2cap_br_init(void); + +/* Register fixed channel */ +void bt_l2cap_br_fixed_chan_register(struct bt_l2cap_fixed_chan *chan); + +/* Notify BR/EDR L2CAP channels about established new ACL connection */ +void bt_l2cap_br_connected(struct bt_conn *conn); + +/* Lookup BR/EDR L2CAP channel by Receiver CID */ +struct bt_l2cap_chan *bt_l2cap_br_lookup_rx_cid(struct bt_conn *conn, + u16_t cid); + +/* Disconnects dynamic channel */ +int bt_l2cap_br_chan_disconnect(struct bt_l2cap_chan *chan); + +/* Make connection to peer psm server */ +int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, + u16_t psm); + +/* Send packet data to connected peer */ +int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf); + +/* + * Handle security level changed on link passing HCI status of performed + * security procedure. + */ +void l2cap_br_encrypt_change(struct bt_conn *conn, u8_t hci_status); + +/* Handle received data */ +void bt_l2cap_br_recv(struct bt_conn *conn, struct net_buf *buf); diff --git a/components/ble/ble_stack/host/monitor.c b/components/ble/ble_stack/host/monitor.c new file mode 100644 index 00000000..0ba1bf0c --- /dev/null +++ b/components/ble/ble_stack/host/monitor.c @@ -0,0 +1,29 @@ +/** @file + * @brief Custom logging over UART + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#if defined(CONFIG_BT_DEBUG_MONITOR) + +#include +#include +#include "monitor.h" +#include "log.h" + +void bt_monitor_send(uint16_t opcode, const void *data, size_t len) +{ + const uint8_t *buf = data; + + BT_WARN("[Hci]:pkt_type:[0x%x],pkt_data:[%s]\r\n",opcode,bt_hex(buf,len)); +} + +void bt_monitor_new_index(uint8_t type, uint8_t bus, bt_addr_t *addr, + const char *name) +{ + +} +#endif diff --git a/components/ble/ble_stack/host/monitor.h b/components/ble/ble_stack/host/monitor.h new file mode 100644 index 00000000..1e80c1c6 --- /dev/null +++ b/components/ble/ble_stack/host/monitor.h @@ -0,0 +1,102 @@ +/** @file + * @brief Custom monitor protocol logging over UART + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(CONFIG_BT_DEBUG_MONITOR) + +#define BT_MONITOR_NEW_INDEX 0 +#define BT_MONITOR_DEL_INDEX 1 +#define BT_MONITOR_COMMAND_PKT 2 +#define BT_MONITOR_EVENT_PKT 3 +#define BT_MONITOR_ACL_TX_PKT 4 +#define BT_MONITOR_ACL_RX_PKT 5 +#define BT_MONITOR_SCO_TX_PKT 6 +#define BT_MONITOR_SCO_RX_PKT 7 +#define BT_MONITOR_OPEN_INDEX 8 +#define BT_MONITOR_CLOSE_INDEX 9 +#define BT_MONITOR_INDEX_INFO 10 +#define BT_MONITOR_VENDOR_DIAG 11 +#define BT_MONITOR_SYSTEM_NOTE 12 +#define BT_MONITOR_USER_LOGGING 13 +#define BT_MONITOR_NOP 255 + +#define BT_MONITOR_TYPE_PRIMARY 0 +#define BT_MONITOR_TYPE_AMP 1 + +/* Extended header types */ +#define BT_MONITOR_COMMAND_DROPS 1 +#define BT_MONITOR_EVENT_DROPS 2 +#define BT_MONITOR_ACL_RX_DROPS 3 +#define BT_MONITOR_ACL_TX_DROPS 4 +#define BT_MONITOR_SCO_RX_DROPS 5 +#define BT_MONITOR_SCO_TX_DROPS 6 +#define BT_MONITOR_OTHER_DROPS 7 +#define BT_MONITOR_TS32 8 + +#define BT_MONITOR_BASE_HDR_LEN 6 + +#if defined(CONFIG_BT_BREDR) +#define BT_MONITOR_EXT_HDR_MAX 19 +#else +#define BT_MONITOR_EXT_HDR_MAX 15 +#endif + +struct bt_monitor_hdr { + u16_t data_len; + u16_t opcode; + u8_t flags; + u8_t hdr_len; + + u8_t ext[BT_MONITOR_EXT_HDR_MAX]; +} __packed; + +struct bt_monitor_ts32 { + u8_t type; + u32_t ts32; +} __packed; + +struct bt_monitor_new_index { + u8_t type; + u8_t bus; + u8_t bdaddr[6]; + char name[8]; +} __packed; + +struct bt_monitor_user_logging { + u8_t priority; + u8_t ident_len; +} __packed; + +static inline u8_t bt_monitor_opcode(struct net_buf *buf) +{ + switch (bt_buf_get_type(buf)) { + case BT_BUF_CMD: + return BT_MONITOR_COMMAND_PKT; + case BT_BUF_EVT: + return BT_MONITOR_EVENT_PKT; + case BT_BUF_ACL_OUT: + return BT_MONITOR_ACL_TX_PKT; + case BT_BUF_ACL_IN: + return BT_MONITOR_ACL_RX_PKT; + default: + return BT_MONITOR_NOP; + } +} + +void bt_monitor_send(u16_t opcode, const void *data, size_t len); + +void bt_monitor_new_index(u8_t type, u8_t bus, bt_addr_t *addr, + const char *name); + +#else /* !CONFIG_BT_DEBUG_MONITOR */ + +#define bt_monitor_send(opcode, data, len) +#define bt_monitor_new_index(type, bus, addr, name) + +#endif diff --git a/components/ble/ble_stack/host/multi_adv.c b/components/ble/ble_stack/host/multi_adv.c new file mode 100644 index 00000000..0fec68ca --- /dev/null +++ b/components/ble/ble_stack/host/multi_adv.c @@ -0,0 +1,519 @@ +/* + * xx + */ + +#include +#include + +//#include +#include +#include + +#include "multi_adv.h" +#include "work_q.h" + +static struct multi_adv_instant g_multi_adv_list[MAX_MULTI_ADV_INSTANT]; +static struct multi_adv_scheduler g_multi_adv_scheduler; +static struct k_delayed_work g_multi_adv_timer; + +void multi_adv_schedule_timeslot(struct multi_adv_scheduler *adv_scheduler); +int multi_adv_schedule_timer_stop(void); + +int multi_adv_get_instant_num(void) +{ + int i, num = 0; + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + if (inst[i].inuse_flag) + num++; + } + return num; +} + +struct multi_adv_instant *multi_adv_alloc_unused_instant(void) +{ + int i; + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + if (inst[i].inuse_flag == 0) { + inst[i].inuse_flag = 1; + inst[i].instant_id = i+1; + return &(inst[i]); + } + } + return 0; +} + +int multi_adv_delete_instant_by_id(int instant_id) +{ + int i; + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) { + inst[i].inuse_flag = 0; + return 0; + } + } + return -1; +} + +struct multi_adv_instant *multi_adv_find_instant_by_id(int instant_id) +{ + int i; + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) { + return &(inst[i]); + } + } + return 0; +} + +struct multi_adv_instant *multi_adv_find_instant_by_order(int order) +{ + + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + if (inst[order].inuse_flag) { + return &(inst[order]); + } + return 0; +} + +int multi_adv_set_ad_data(uint8_t * ad_data, const struct bt_data *ad, size_t ad_len) +{ + int i, len; + + memset(ad_data, 0, MAX_AD_DATA_LEN); + len = 0; + for (i = 0; i < ad_len; i++) { + /* Check if ad fit in the remaining buffer */ + if (len + ad[i].data_len + 2 > MAX_AD_DATA_LEN) { + break; + } + + ad_data[len++] = ad[i].data_len + 1; + ad_data[len++] = ad[i].type; + + memcpy(&ad_data[len], ad[i].data, ad[i].data_len); + len += ad[i].data_len; + } + + return len; +} + +int change_to_tick(int min_interval, int max_interval) +{ + int tick; + + if (max_interval/SLOT_PER_PERIOD != min_interval/SLOT_PER_PERIOD) { + tick = min_interval/SLOT_PER_PERIOD; + if (min_interval%SLOT_PER_PERIOD) + tick++; + } else { + tick = min_interval/SLOT_PER_PERIOD; + } + if (tick <= 1) + tick = 1; + + return tick; +} + +int calculate_min_multi(int a, int b) +{ + int x = a, y = b, z; + + while (y != 0) { + z = x%y; + x = y; + y = z; + } + + return a*b/x; +} + +void multi_adv_reorder(int inst_num, uint16_t inst_interval[], uint8_t inst_order[]) +{ + int i, j; + + for (i = 0; i < inst_num; i++) { + int max = inst_interval[0]; + int max_idx = 0; + int temp; + + for (j = 1; j < inst_num-i ; j++) { + if (max < inst_interval[j]) { + max = inst_interval[j]; + max_idx = j; + } + } + + temp = inst_interval[inst_num-i-1]; + inst_interval[inst_num-i-1] = inst_interval[max_idx]; + inst_interval[max_idx] = temp; + + temp = inst_order[inst_num-i-1]; + inst_order[inst_num-i-1] = inst_order[max_idx]; + inst_order[max_idx] = temp; + } +} + +int calculate_offset(uint16_t interval[], uint16_t offset[], int num, int duration) +{ + int i, j, k, curr_offset = 0; + int curr_max_instants, min_max_instants, instants; + int offset_range; + + offset_range = interval[num]; + if (offset_range > duration) + offset_range = duration; + + if (num == 0) + return 0; + + min_max_instants = 0x7fffffff; + /* using 0-interval-1 as offset */ + for (i = 0; i < offset_range; i++) { + + curr_max_instants = 0; + /* search slot form 0 - duration to get the max instants number */ + for (j = 0; j < duration; j++) { + + /* get instant number in each slot */ + instants = 0; + for (k = 0; k < num; k++) { + if (j%interval[k] == offset[k]) { + instants++; + } + } + if (j%interval[num] == i) + instants++; + if (curr_max_instants < instants) { + curr_max_instants = instants; + } + } + + /* check if min max instants */ + if (min_max_instants > curr_max_instants) { + min_max_instants = curr_max_instants; + curr_offset = i; + } + } + return curr_offset; + +} + +void multi_adv_schedule_table(int inst_num, uint16_t inst_interval[], uint16_t inst_offset[]) +{ + int i, min_multi, last_min_multi; + /* calculate min multi */ + last_min_multi = min_multi = inst_interval[0]; + for (i = 1; i < inst_num; i++) { + min_multi = calculate_min_multi(min_multi, inst_interval[i]); + if (min_multi > MAX_MIN_MULTI) { + min_multi = last_min_multi; + break; + } + last_min_multi = min_multi; + } + + /* offset calcute for schedule just for small interval range */ + for (i = 0; i < inst_num; i++) { + inst_offset[i] = calculate_offset(inst_interval, inst_offset, i, min_multi); + } +} + +int multi_adv_start_adv_instant(struct multi_adv_instant *adv_instant) +{ + int ret; + + ret = bt_le_adv_start_instant(&adv_instant->param, + adv_instant->ad, adv_instant->ad_len, + adv_instant->sd, adv_instant->sd_len); + if (ret) { + BT_WARN("adv start instant failed: inst_id %d, err %d\r\n", adv_instant->instant_id, ret); + } + return ret; +} + +void multi_adv_schedule_timer_handle(void) +{ + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + + multi_adv_schedule_timer_stop(); + if (adv_scheduler->schedule_state == SCHEDULE_STOP) + return; + + adv_scheduler->slot_clock = adv_scheduler->next_slot_clock; + adv_scheduler->slot_offset = adv_scheduler->next_slot_offset; + + multi_adv_schedule_timeslot(adv_scheduler); + return; +} + +void multi_adv_schedule_timer_callback(struct k_work *timer) +{ + multi_adv_schedule_timer_handle(); + return; +} + +int multi_adv_schedule_timer_start(int timeout) +{ + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + multi_adv_schedule_timer_stop(); + + k_delayed_work_submit(&g_multi_adv_timer, timeout); + adv_scheduler->schedule_timer_active = 1; + + return 1; +} + +int multi_adv_schedule_timer_stop(void) +{ + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + + if (adv_scheduler->schedule_timer_active) { + k_delayed_work_cancel(&g_multi_adv_timer); + adv_scheduler->schedule_timer_active = 0; + } + return 0; +} + +void multi_adv_schedule_timeslot(struct multi_adv_scheduler *adv_scheduler) +{ + int i, inst_num; + int inst_clk, inst_off, match, insts = 0, next_slot, min_next_slot; + struct multi_adv_instant *adv_instant; + uint16_t inst_interval[MAX_MULTI_ADV_INSTANT]; + uint16_t inst_offset[MAX_MULTI_ADV_INSTANT]; + uint8_t inst_order[MAX_MULTI_ADV_INSTANT]; + uint8_t match_order[MAX_MULTI_ADV_INSTANT]; + + inst_num = 0; + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + adv_instant = multi_adv_find_instant_by_order(i); + if (adv_instant) { + inst_interval[inst_num] = adv_instant->instant_interval; + inst_offset[inst_num] = adv_instant->instant_offset; + inst_order[inst_num] = i; + inst_num++; + } + } + + inst_clk = adv_scheduler->slot_clock; + inst_off = adv_scheduler->slot_offset; + match = 0; + for (i = 0; i < inst_num; i++) { + if ((inst_clk%inst_interval[i]) == inst_offset[i]) { + match_order[match] = i; + match++; + } + } + +// BT_DBG("multi_adv_schedule_timeslot, num = %d, match = %d", inst_num, match); + if (match) { + int offset_per_instant, diff; + offset_per_instant = TIME_PRIOD_MS/match; + diff = inst_off - (inst_off+offset_per_instant/2)/offset_per_instant*offset_per_instant;//TODO may be error + + /* means this is the time to start */ + if (diff <= 2) { + insts = (inst_off+offset_per_instant/2)/offset_per_instant; + + /* start instant */ + adv_instant = multi_adv_find_instant_by_order(inst_order[match_order[insts]]); + if(adv_instant) + multi_adv_start_adv_instant(adv_instant); + } + + /* next instant in the same slot */ + if (match-insts > 1) { + adv_scheduler->next_slot_offset = adv_scheduler->slot_offset+offset_per_instant; + adv_scheduler->next_slot_clock = adv_scheduler->slot_clock; + + if ((adv_scheduler->next_slot_offset >= (TIME_PRIOD_MS-2))&&(adv_scheduler->slot_offset <= (TIME_PRIOD_MS+2))) { + adv_scheduler->next_slot_clock++; + adv_scheduler->next_slot_offset = 0; + } + multi_adv_schedule_timer_start(offset_per_instant); + return; + } + } + + /* next instant not in the same slot */ + min_next_slot = 0x7fffffff; + for (i = 0; i < inst_num; i++) { + if (inst_clk-inst_offset[i] < 0) { + match = 0; + } else { + match = (inst_clk-inst_offset[i])/inst_interval[i]+1; + } + next_slot = match*inst_interval[i]+inst_offset[i]; + if (next_slot < min_next_slot) { + min_next_slot = next_slot; + } + } + adv_scheduler->next_slot_offset = 0; + adv_scheduler->next_slot_clock = min_next_slot; + + next_slot = (adv_scheduler->next_slot_clock - adv_scheduler->slot_clock)*TIME_PRIOD_MS+(adv_scheduler->next_slot_offset-adv_scheduler->slot_offset); + multi_adv_schedule_timer_start(next_slot); + return; +} + +void multi_adv_schedule_stop(void) +{ + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + + multi_adv_schedule_timer_stop(); + adv_scheduler->schedule_state = SCHEDULE_STOP; +} + +void multi_adv_schedule_start(void) +{ + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + + /* get all instant and calculate ticks and */ + if (adv_scheduler->schedule_state == SCHEDULE_START) { + multi_adv_schedule_stop(); + } + + /* reinit scheduler */ + adv_scheduler->slot_clock = 0; + adv_scheduler->slot_offset = 0; + adv_scheduler->schedule_state = SCHEDULE_START; + multi_adv_schedule_timeslot(adv_scheduler); +} + +void multi_adv_new_schedule(void) +{ + int i; + struct multi_adv_instant *adv_instant, *high_duty_instant; + struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler; + uint16_t inst_offset[MAX_MULTI_ADV_INSTANT]; + uint16_t inst_interval[MAX_MULTI_ADV_INSTANT]; + uint8_t inst_order[MAX_MULTI_ADV_INSTANT]; + int inst_num = 0; + + if (adv_scheduler->schedule_state == SCHEDULE_START) { + multi_adv_schedule_stop(); + } + /* get all instant and calculate ticks and */ + high_duty_instant = 0; + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + adv_instant = multi_adv_find_instant_by_order(i); + if (adv_instant) { + /* if high duty cycle adv found */ + if (adv_instant->param.interval_min < HIGH_DUTY_CYCLE_INTERVAL) { + high_duty_instant = adv_instant; + break; + } + + inst_interval[inst_num] = change_to_tick(adv_instant->param.interval_min, adv_instant->param.interval_max); + inst_order[inst_num] = i; + inst_num++; + } + } + + if (high_duty_instant) { + //BT_WARN("High Duty Cycle Instants, id = %d, interval = %d\n", adv_instant->instant_id, adv_instant->param.interval_min); + multi_adv_start_adv_instant(adv_instant); + return; + } + + /* instant number equal 0 and 1 */ + if (inst_num == 0) { + bt_le_adv_stop(); + return; + } + if (inst_num == 1) { + adv_instant = multi_adv_find_instant_by_order(inst_order[0]); + if(!adv_instant) + return; + multi_adv_start_adv_instant(adv_instant); + return; + } + + /* reorder by inst_interval */ + multi_adv_reorder(inst_num, inst_interval, inst_order); + + /* calcuate schedule table */ + multi_adv_schedule_table(inst_num, inst_interval, inst_offset); + + /* set interval and offset to instant */ + for (i = 0; i < inst_num; i++) { + adv_instant = multi_adv_find_instant_by_order(inst_order[i]); + if(!adv_instant){ + continue; + } + adv_instant->instant_interval = inst_interval[i]; + adv_instant->instant_offset = inst_offset[i]; + + //BT_WARN("adv_instant id = %d, interval = %d, offset = %d\n", adv_instant->instant_id, adv_instant->instant_interval, adv_instant->instant_offset); + } + + multi_adv_schedule_start(); +} + +int bt_le_multi_adv_thread_init(void) +{ + /* timer and event init */ + k_delayed_work_init(&g_multi_adv_timer, multi_adv_schedule_timer_callback); + return 0; +} + +int bt_le_multi_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len, int *instant_id) +{ + int instant_num; + struct multi_adv_instant *adv_instant; + + instant_num = multi_adv_get_instant_num(); + if (instant_num >= MAX_MULTI_ADV_INSTANT) + return -1; + + adv_instant = multi_adv_alloc_unused_instant(); + if (adv_instant == 0) + return -1; + + memcpy(&(adv_instant->param), param, sizeof(struct bt_le_adv_param)); + + adv_instant->ad_len = multi_adv_set_ad_data(adv_instant->ad, ad, ad_len); + adv_instant->sd_len = multi_adv_set_ad_data(adv_instant->sd, sd, sd_len); + + multi_adv_new_schedule(); + + *instant_id = adv_instant->instant_id; + return 0; +} + +int bt_le_multi_adv_stop(int instant_id) +{ + if (multi_adv_find_instant_by_id(instant_id) == 0) + return -1; + + //BT_WARN("%s id[%d]\n", __func__, instant_id); + multi_adv_delete_instant_by_id(instant_id); + multi_adv_new_schedule(); + + return 0; +} + +bool bt_le_multi_adv_id_is_vaild(int instant_id) +{ + int i; + struct multi_adv_instant *inst = &(g_multi_adv_list[0]); + + for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) { + if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) { + return true; + } + } + return false; +} + diff --git a/components/ble/ble_stack/host/multi_adv.h b/components/ble/ble_stack/host/multi_adv.h new file mode 100644 index 00000000..80a50f93 --- /dev/null +++ b/components/ble/ble_stack/host/multi_adv.h @@ -0,0 +1,68 @@ +/* + * xx + */ + +#ifndef _MULTI_ADV_H_ +#define _MULTI_ADV_H_ + +#define MAX_MULTI_ADV_INSTANT 4 +#define MAX_AD_DATA_LEN 31 + +#define TIME_PRIOD_MS (10*(MAX_MULTI_ADV_INSTANT -2)) +#define SLOT_PER_PERIOD (TIME_PRIOD_MS*8/5) + +#define MAX_MIN_MULTI (30000/TIME_PRIOD_MS) + +#define HIGH_DUTY_CYCLE_INTERVAL (20*8/5) + + +struct multi_adv_instant { + uint8_t inuse_flag; + + /* for parameters */ + struct bt_le_adv_param param; + uint8_t ad[MAX_AD_DATA_LEN]; + uint8_t ad_len; + uint8_t sd[MAX_AD_DATA_LEN]; + uint8_t sd_len; + + /* own address maybe used */ + bt_addr_t own_addr; + uint8_t own_addr_valid; + + /* for schedule */ + int instant_id; + int instant_interval; + int instant_offset; + uint32_t clock; + uint32_t clock_instant_offset; + uint32_t clock_instant_total; + uint32_t next_wakeup_time; +}; + +typedef enum { + SCHEDULE_IDLE, + SCHEDULE_READY, + SCHEDULE_START, + SCHEDULE_STOP, +}SCHEDULE_STATE; + +struct multi_adv_scheduler { + SCHEDULE_STATE schedule_state; + uint8_t schedule_timer_active; + uint32_t slot_clock; + uint16_t slot_offset; + uint16_t next_slot_offset; + uint32_t next_slot_clock; +}; + +int bt_le_multi_adv_thread_init(void); +int bt_le_multi_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len, int *instant_id); +int bt_le_multi_adv_stop(int instant_id); + +bool bt_le_multi_adv_id_is_vaild(int instant_id); + +#endif + diff --git a/components/ble/ble_stack/host/rfcomm.c b/components/ble/ble_stack/host/rfcomm.c new file mode 100644 index 00000000..a7379c19 --- /dev/null +++ b/components/ble/ble_stack/host/rfcomm.c @@ -0,0 +1,1746 @@ +/* rfcomm.c - RFCOMM handling */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_RFCOMM) +#define LOG_MODULE_NAME bt_rfcomm +#include "log.h" + +#include +#include + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "rfcomm_internal.h" + +#define RFCOMM_CHANNEL_START 0x01 +#define RFCOMM_CHANNEL_END 0x1e + +#define RFCOMM_MIN_MTU BT_RFCOMM_SIG_MIN_MTU +#define RFCOMM_DEFAULT_MTU 127 + +#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL) +#define RFCOMM_MAX_CREDITS (CONFIG_BT_ACL_RX_COUNT - 1) +#else +#define RFCOMM_MAX_CREDITS (CONFIG_BT_RX_BUF_COUNT - 1) +#endif + +#define RFCOMM_CREDITS_THRESHOLD (RFCOMM_MAX_CREDITS / 2) +#define RFCOMM_DEFAULT_CREDIT RFCOMM_MAX_CREDITS + +#define RFCOMM_CONN_TIMEOUT K_SECONDS(60) +#define RFCOMM_DISC_TIMEOUT K_SECONDS(20) +#define RFCOMM_IDLE_TIMEOUT K_SECONDS(2) + +#define DLC_RTX(_w) CONTAINER_OF(_w, struct bt_rfcomm_dlc, rtx_work) + +#define SESSION_RTX(_w) CONTAINER_OF(_w, struct bt_rfcomm_session, rtx_work) + +static struct bt_rfcomm_server *servers; + +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +/* Pool for dummy buffers to wake up the tx threads */ +NET_BUF_POOL_DEFINE(dummy_pool, CONFIG_BT_MAX_CONN, 0, 0, NULL); +#else +struct net_buf_pool dummy_pool; +#endif + +#define RFCOMM_SESSION(_ch) CONTAINER_OF(_ch, \ + struct bt_rfcomm_session, br_chan.chan) + +static struct bt_rfcomm_session bt_rfcomm_pool[CONFIG_BT_MAX_CONN]; +static struct k_thread *rfcomm_tx_thread; +static struct bt_rfcomm_dlc *thread_dlc; + +/* reversed, 8-bit, poly=0x07 */ +static const uint8_t rfcomm_crc_table[256] = { + 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 +}; + +static uint8_t rfcomm_calc_fcs(uint16_t len, const uint8_t *data) +{ + uint8_t fcs = 0xff; + + while (len--) { + fcs = rfcomm_crc_table[fcs ^ *data++]; + } + + /* Ones compliment */ + return (0xff - fcs); +} + +static bool rfcomm_check_fcs(uint16_t len, const uint8_t *data, + uint8_t recvd_fcs) +{ + uint8_t fcs = 0xff; + + while (len--) { + fcs = rfcomm_crc_table[fcs ^ *data++]; + } + + /* Ones compliment */ + fcs = rfcomm_crc_table[fcs ^ recvd_fcs]; + + /*0xCF is the reversed order of 11110011.*/ + return (fcs == 0xcf); +} + +static struct bt_rfcomm_dlc *rfcomm_dlcs_lookup_dlci(struct bt_rfcomm_dlc *dlcs, + uint8_t dlci) +{ + for (; dlcs; dlcs = dlcs->_next) { + if (dlcs->dlci == dlci) { + return dlcs; + } + } + + return NULL; +} + +static struct bt_rfcomm_dlc *rfcomm_dlcs_remove_dlci(struct bt_rfcomm_dlc *dlcs, + uint8_t dlci) +{ + struct bt_rfcomm_dlc *tmp; + + if (!dlcs) { + return NULL; + } + + /* If first node is the one to be removed */ + if (dlcs->dlci == dlci) { + dlcs->session->dlcs = dlcs->_next; + return dlcs; + } + + for (tmp = dlcs, dlcs = dlcs->_next; dlcs; dlcs = dlcs->_next) { + if (dlcs->dlci == dlci) { + tmp->_next = dlcs->_next; + return dlcs; + } + tmp = dlcs; + } + + return NULL; +} + +static struct bt_rfcomm_server *rfcomm_server_lookup_channel(uint8_t channel) +{ + struct bt_rfcomm_server *server; + + for (server = servers; server; server = server->_next) { + if (server->channel == channel) { + return server; + } + } + + return NULL; +} + +static struct bt_rfcomm_session * +rfcomm_sessions_lookup_bt_conn(struct bt_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_rfcomm_pool); i++) { + struct bt_rfcomm_session *session = &bt_rfcomm_pool[i]; + + if (session->br_chan.chan.conn == conn) { + return session; + } + } + + return NULL; +} + +int bt_rfcomm_server_register(struct bt_rfcomm_server *server) +{ + if (server->channel < RFCOMM_CHANNEL_START || + server->channel > RFCOMM_CHANNEL_END || !server->accept) { + return -EINVAL; + } + + /* Check if given channel is already in use */ + if (rfcomm_server_lookup_channel(server->channel)) { + BT_DBG("Channel already registered"); + return -EADDRINUSE; + } + + BT_DBG("Channel 0x%02x", server->channel); + + server->_next = servers; + servers = server; + + return 0; +} + +static void rfcomm_dlc_tx_give_credits(struct bt_rfcomm_dlc *dlc, + uint8_t credits) +{ + BT_DBG("dlc %p credits %u", dlc, credits); + + while (credits--) { + k_sem_give(&dlc->tx_credits); + } + + BT_DBG("dlc %p updated credits %u", dlc, + k_sem_count_get(&dlc->tx_credits)); +} + +static void rfcomm_dlc_destroy(struct bt_rfcomm_dlc *dlc) +{ + BT_DBG("dlc %p", dlc); + + k_delayed_work_cancel(&dlc->rtx_work); + dlc->state = BT_RFCOMM_STATE_IDLE; + dlc->session = NULL; + + if (dlc->ops && dlc->ops->disconnected) { + dlc->ops->disconnected(dlc); + } +} + +static void rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc) +{ + uint8_t old_state = dlc->state; + + BT_DBG("dlc %p", dlc); + + if (dlc->state == BT_RFCOMM_STATE_DISCONNECTED) { + return; + } + + dlc->state = BT_RFCOMM_STATE_DISCONNECTED; + + switch (old_state) { + case BT_RFCOMM_STATE_CONNECTED: + /* Queue a dummy buffer to wake up and stop the + * tx thread for states where it was running. + */ + net_buf_put(&dlc->tx_queue, + net_buf_alloc(&dummy_pool, K_NO_WAIT)); + + /* There could be a writer waiting for credits so return a + * dummy credit to wake it up. + */ + rfcomm_dlc_tx_give_credits(dlc, 1); + k_sem_give(&dlc->session->fc); + break; + default: + rfcomm_dlc_destroy(dlc); + break; + } +} + +static void rfcomm_session_disconnected(struct bt_rfcomm_session *session) +{ + struct bt_rfcomm_dlc *dlc; + + BT_DBG("Session %p", session); + + if (session->state == BT_RFCOMM_STATE_DISCONNECTED) { + return; + } + + for (dlc = session->dlcs; dlc;) { + struct bt_rfcomm_dlc *next; + + /* prefetch since disconnected callback may cleanup */ + next = dlc->_next; + dlc->_next = NULL; + + rfcomm_dlc_disconnect(dlc); + + dlc = next; + } + + session->state = BT_RFCOMM_STATE_DISCONNECTED; + session->dlcs = NULL; +} + +struct net_buf *bt_rfcomm_create_pdu(struct net_buf_pool *pool) +{ + /* Length in RFCOMM header can be 2 bytes depending on length of user + * data + */ + return bt_conn_create_pdu(pool, + sizeof(struct bt_l2cap_hdr) + + sizeof(struct bt_rfcomm_hdr) + 1); +} + +static int rfcomm_send_sabm(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t cr, fcs; + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_CMD_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_SABM, BT_RFCOMM_PF_NON_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_disc(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t fcs, cr; + + BT_DBG("dlci %d", dlci); + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_RESP_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_DISC, BT_RFCOMM_PF_NON_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static void rfcomm_session_disconnect(struct bt_rfcomm_session *session) +{ + if (session->dlcs) { + return; + } + + session->state = BT_RFCOMM_STATE_DISCONNECTING; + rfcomm_send_disc(session, 0); + k_delayed_work_submit(&session->rtx_work, RFCOMM_DISC_TIMEOUT); +} + +static struct net_buf *rfcomm_make_uih_msg(struct bt_rfcomm_session *session, + uint8_t cr, uint8_t type, + uint8_t len) +{ + struct bt_rfcomm_hdr *hdr; + struct bt_rfcomm_msg_hdr *msg_hdr; + struct net_buf *buf; + uint8_t hdr_cr; + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr_cr = BT_RFCOMM_UIH_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(0, hdr_cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UIH, BT_RFCOMM_PF_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(sizeof(*msg_hdr) + len); + + msg_hdr = net_buf_add(buf, sizeof(*msg_hdr)); + msg_hdr->type = BT_RFCOMM_SET_MSG_TYPE(type, cr); + msg_hdr->len = BT_RFCOMM_SET_LEN_8(len); + + return buf; +} + +static void rfcomm_connected(struct bt_l2cap_chan *chan) +{ + struct bt_rfcomm_session *session = RFCOMM_SESSION(chan); + + BT_DBG("Session %p", session); + + /* Need to include UIH header and FCS*/ + session->mtu = MIN(session->br_chan.rx.mtu, + session->br_chan.tx.mtu) - + BT_RFCOMM_HDR_SIZE + BT_RFCOMM_FCS_SIZE; + + if (session->state == BT_RFCOMM_STATE_CONNECTING) { + rfcomm_send_sabm(session, 0); + } +} + +static void rfcomm_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_rfcomm_session *session = RFCOMM_SESSION(chan); + + BT_DBG("Session %p", session); + + k_delayed_work_cancel(&session->rtx_work); + rfcomm_session_disconnected(session); + session->state = BT_RFCOMM_STATE_IDLE; +} + +static void rfcomm_dlc_rtx_timeout(struct k_work *work) +{ + struct bt_rfcomm_dlc *dlc = DLC_RTX(work); + struct bt_rfcomm_session *session = dlc->session; + + BT_WARN("dlc %p state %d timeout", dlc, dlc->state); + + rfcomm_dlcs_remove_dlci(session->dlcs, dlc->dlci); + rfcomm_dlc_disconnect(dlc); + rfcomm_session_disconnect(session); +} + +static void rfcomm_dlc_init(struct bt_rfcomm_dlc *dlc, + struct bt_rfcomm_session *session, + uint8_t dlci, + bt_rfcomm_role_t role) +{ + BT_DBG("dlc %p", dlc); + + dlc->dlci = dlci; + dlc->session = session; + dlc->rx_credit = RFCOMM_DEFAULT_CREDIT; + dlc->state = BT_RFCOMM_STATE_INIT; + dlc->role = role; + k_delayed_work_init(&dlc->rtx_work, rfcomm_dlc_rtx_timeout); + + /* Start a conn timer which includes auth as well */ + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_CONN_TIMEOUT); + + dlc->_next = session->dlcs; + session->dlcs = dlc; +} + +static struct bt_rfcomm_dlc *rfcomm_dlc_accept(struct bt_rfcomm_session *session, + uint8_t dlci) +{ + struct bt_rfcomm_server *server; + struct bt_rfcomm_dlc *dlc; + uint8_t channel; + + channel = BT_RFCOMM_GET_CHANNEL(dlci); + server = rfcomm_server_lookup_channel(channel); + if (!server) { + BT_ERR("Server Channel not registered"); + return NULL; + } + + if (server->accept(session->br_chan.chan.conn, &dlc) < 0) { + BT_DBG("Incoming connection rejected"); + return NULL; + } + + if (!BT_RFCOMM_CHECK_MTU(dlc->mtu)) { + rfcomm_dlc_destroy(dlc); + return NULL; + } + + rfcomm_dlc_init(dlc, session, dlci, BT_RFCOMM_ROLE_ACCEPTOR); + dlc->mtu = MIN(dlc->mtu, session->mtu); + + return dlc; +} + +static int rfcomm_send_dm(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t fcs, cr; + + BT_DBG("dlci %d", dlci); + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_RESP_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); + /* For DM PF bit is not relevant, we set it 1 */ + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_DM, BT_RFCOMM_PF_NON_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static void rfcomm_check_fc(struct bt_rfcomm_dlc *dlc) +{ + BT_DBG("%p", dlc); + + BT_DBG("Wait for credits or MSC FC %p", dlc); + /* Wait for credits or MSC FC */ + k_sem_take(&dlc->tx_credits, K_FOREVER); + + if (dlc->session->cfc == BT_RFCOMM_CFC_SUPPORTED) { + return; + } + + k_sem_take(&dlc->session->fc, K_FOREVER); + + /* Give the sems immediately so that sem will be available for all + * the bufs in the queue. It will be blocked only once all the bufs + * are sent (which will preempt this thread) and FCOFF / FC bit + * with 1, is received. + */ + k_sem_give(&dlc->session->fc); + k_sem_give(&dlc->tx_credits); +} + +static void rfcomm_dlc_tx_thread(void *p1) +{ + struct bt_rfcomm_dlc *dlc = thread_dlc; + s32_t timeout = K_FOREVER; + struct net_buf *buf; + + BT_DBG("Started for dlc %p", dlc); + + while (dlc->state == BT_RFCOMM_STATE_CONNECTED || + dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) { + /* Get next packet for dlc */ + BT_DBG("Wait for buf %p", dlc); + buf = net_buf_get(&dlc->tx_queue, timeout); + /* If its dummy buffer or non user disconnect then break */ + if ((dlc->state != BT_RFCOMM_STATE_CONNECTED && + dlc->state != BT_RFCOMM_STATE_USER_DISCONNECT) || + !buf || !buf->len) { + if (buf) { + net_buf_unref(buf); + } + break; + } + + rfcomm_check_fc(dlc); + if (dlc->state != BT_RFCOMM_STATE_CONNECTED && + dlc->state != BT_RFCOMM_STATE_USER_DISCONNECT) { + net_buf_unref(buf); + break; + } + + if (bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf) < 0) { + /* This fails only if channel is disconnected */ + dlc->state = BT_RFCOMM_STATE_DISCONNECTED; + net_buf_unref(buf); + break; + } + + if (dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) { + timeout = K_NO_WAIT; + } + } + + BT_DBG("dlc %p disconnected - cleaning up", dlc); + + /* Give back any allocated buffers */ + while ((buf = net_buf_get(&dlc->tx_queue, K_NO_WAIT))) { + net_buf_unref(buf); + } + + if (dlc->state == BT_RFCOMM_STATE_USER_DISCONNECT) { + dlc->state = BT_RFCOMM_STATE_DISCONNECTING; + } + + if (dlc->state == BT_RFCOMM_STATE_DISCONNECTING) { + rfcomm_send_disc(dlc->session, dlc->dlci); + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); + } else { + rfcomm_dlc_destroy(dlc); + } + + BT_DBG("dlc %p exiting", dlc); + k_thread_delete(rfcomm_tx_thread); +} + +static int rfcomm_send_ua(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t cr, fcs; + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_RESP_CR(session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UA, BT_RFCOMM_PF_NON_UIH); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_NON_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_msc(struct bt_rfcomm_dlc *dlc, uint8_t cr, + uint8_t v24_signal) +{ + struct bt_rfcomm_msc *msc; + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(dlc->session, cr, BT_RFCOMM_MSC, + sizeof(*msc)); + + msc = net_buf_add(buf, sizeof(*msc)); + /* cr bit should be always 1 in MSC */ + msc->dlci = BT_RFCOMM_SET_ADDR(dlc->dlci, 1); + msc->v24_signal = v24_signal; + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf); +} + +static int rfcomm_send_rls(struct bt_rfcomm_dlc *dlc, uint8_t cr, + uint8_t line_status) +{ + struct bt_rfcomm_rls *rls; + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(dlc->session, cr, BT_RFCOMM_RLS, + sizeof(*rls)); + + rls = net_buf_add(buf, sizeof(*rls)); + /* cr bit should be always 1 in RLS */ + rls->dlci = BT_RFCOMM_SET_ADDR(dlc->dlci, 1); + rls->line_status = line_status; + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf); +} + +static int rfcomm_send_rpn(struct bt_rfcomm_session *session, uint8_t cr, + struct bt_rfcomm_rpn *rpn) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_RPN, sizeof(*rpn)); + + net_buf_add_mem(buf, rpn, sizeof(*rpn)); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_test(struct bt_rfcomm_session *session, uint8_t cr, + uint8_t *pattern, uint8_t len) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_TEST, len); + + net_buf_add_mem(buf, pattern, len); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_nsc(struct bt_rfcomm_session *session, uint8_t cmd_type) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, BT_RFCOMM_MSG_RESP_CR, + BT_RFCOMM_NSC, sizeof(cmd_type)); + + net_buf_add_u8(buf, cmd_type); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_fcon(struct bt_rfcomm_session *session, uint8_t cr) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_FCON, 0); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static int rfcomm_send_fcoff(struct bt_rfcomm_session *session, uint8_t cr) +{ + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(session, cr, BT_RFCOMM_FCOFF, 0); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&session->br_chan.chan, buf); +} + +static void rfcomm_dlc_connected(struct bt_rfcomm_dlc *dlc) +{ + dlc->state = BT_RFCOMM_STATE_CONNECTED; + + rfcomm_send_msc(dlc, BT_RFCOMM_MSG_CMD_CR, BT_RFCOMM_DEFAULT_V24_SIG); + + if (dlc->session->cfc == BT_RFCOMM_CFC_UNKNOWN) { + /* This means PN negotiation is not done for this session and + * can happen only for 1.0b device. + */ + dlc->session->cfc = BT_RFCOMM_CFC_NOT_SUPPORTED; + } + + if (dlc->session->cfc == BT_RFCOMM_CFC_NOT_SUPPORTED) { + BT_DBG("CFC not supported %p", dlc); + rfcomm_send_fcon(dlc->session, BT_RFCOMM_MSG_CMD_CR); + /* Use tx_credits as binary sem for MSC FC */ + k_sem_init(&dlc->tx_credits, 0, 1); + } + + /* Cancel conn timer */ + k_delayed_work_cancel(&dlc->rtx_work); + + k_fifo_init(&dlc->tx_queue, 20); + rfcomm_tx_thread = &dlc->tx_thread; + thread_dlc = dlc; + k_thread_create(rfcomm_tx_thread, "rfcomm_dlc", + CONFIG_BT_RFCOMM_TX_STACK_SIZE, + rfcomm_dlc_tx_thread, + CONFIG_BT_RFCOMM_TX_PRIO); + + if (dlc->ops && dlc->ops->connected) { + dlc->ops->connected(dlc); + } +} + +enum security_result { + RFCOMM_SECURITY_PASSED, + RFCOMM_SECURITY_REJECT, + RFCOMM_SECURITY_PENDING +}; + +static enum security_result rfcomm_dlc_security(struct bt_rfcomm_dlc *dlc) +{ + struct bt_conn *conn = dlc->session->br_chan.chan.conn; + + BT_DBG("dlc %p", dlc); + + /* If current security level is greater than or equal to required + * security level then return SUCCESS. + * For SSP devices the current security will be atleast MEDIUM + * since L2CAP is enforcing it + */ + if (conn->sec_level >= dlc->required_sec_level) { + return RFCOMM_SECURITY_PASSED; + } + + if (!bt_conn_set_security(conn, dlc->required_sec_level)) { + /* If Security elevation is initiated or in progress */ + return RFCOMM_SECURITY_PENDING; + } + + /* Security request failed */ + return RFCOMM_SECURITY_REJECT; +} + +static void rfcomm_dlc_drop(struct bt_rfcomm_dlc *dlc) +{ + BT_DBG("dlc %p", dlc); + + rfcomm_dlcs_remove_dlci(dlc->session->dlcs, dlc->dlci); + rfcomm_dlc_destroy(dlc); +} + +static int rfcomm_dlc_close(struct bt_rfcomm_dlc *dlc) +{ + BT_DBG("dlc %p", dlc); + + switch (dlc->state) { + case BT_RFCOMM_STATE_SECURITY_PENDING: + if (dlc->role == BT_RFCOMM_ROLE_ACCEPTOR) { + rfcomm_send_dm(dlc->session, dlc->dlci); + } + //__fallthrough; + case BT_RFCOMM_STATE_INIT: + rfcomm_dlc_drop(dlc); + break; + case BT_RFCOMM_STATE_CONNECTING: + case BT_RFCOMM_STATE_CONFIG: + dlc->state = BT_RFCOMM_STATE_DISCONNECTING; + rfcomm_send_disc(dlc->session, dlc->dlci); + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); + break; + case BT_RFCOMM_STATE_CONNECTED: + dlc->state = BT_RFCOMM_STATE_DISCONNECTING; + + /* Queue a dummy buffer to wake up and stop the + * tx thread. + */ + net_buf_put(&dlc->tx_queue, + net_buf_alloc(&dummy_pool, K_NO_WAIT)); + + /* There could be a writer waiting for credits so return a + * dummy credit to wake it up. + */ + rfcomm_dlc_tx_give_credits(dlc, 1); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + case BT_RFCOMM_STATE_DISCONNECTED: + break; + case BT_RFCOMM_STATE_IDLE: + default: + return -EINVAL; + } + + return 0; +} + +static void rfcomm_handle_sabm(struct bt_rfcomm_session *session, uint8_t dlci) +{ + if (!dlci) { + if (rfcomm_send_ua(session, dlci) < 0) { + return; + } + + session->state = BT_RFCOMM_STATE_CONNECTED; + } else { + struct bt_rfcomm_dlc *dlc; + enum security_result result; + + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); + if (!dlc) { + dlc = rfcomm_dlc_accept(session, dlci); + if (!dlc) { + rfcomm_send_dm(session, dlci); + return; + } + } + + result = rfcomm_dlc_security(dlc); + switch (result) { + case RFCOMM_SECURITY_PENDING: + dlc->state = BT_RFCOMM_STATE_SECURITY_PENDING; + return; + case RFCOMM_SECURITY_PASSED: + break; + case RFCOMM_SECURITY_REJECT: + default: + rfcomm_send_dm(session, dlci); + rfcomm_dlc_drop(dlc); + return; + } + + if (rfcomm_send_ua(session, dlci) < 0) { + return; + } + + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); + + rfcomm_dlc_connected(dlc); + } +} + +static int rfcomm_send_pn(struct bt_rfcomm_dlc *dlc, uint8_t cr) +{ + struct bt_rfcomm_pn *pn; + struct net_buf *buf; + uint8_t fcs; + + buf = rfcomm_make_uih_msg(dlc->session, cr, BT_RFCOMM_PN, sizeof(*pn)); + + BT_DBG("mtu %x", dlc->mtu); + + pn = net_buf_add(buf, sizeof(*pn)); + pn->dlci = dlc->dlci; + pn->mtu = sys_cpu_to_le16(dlc->mtu); + if (dlc->state == BT_RFCOMM_STATE_CONFIG && + (dlc->session->cfc == BT_RFCOMM_CFC_UNKNOWN || + dlc->session->cfc == BT_RFCOMM_CFC_SUPPORTED)) { + pn->credits = dlc->rx_credit; + if (cr) { + pn->flow_ctrl = BT_RFCOMM_PN_CFC_CMD; + } else { + pn->flow_ctrl = BT_RFCOMM_PN_CFC_RESP; + } + } else { + /* If PN comes in already opened dlc or cfc not supported + * these should be 0 + */ + pn->credits = 0U; + pn->flow_ctrl = 0U; + } + pn->max_retrans = 0U; + pn->ack_timer = 0U; + pn->priority = 0U; + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf); +} + +static int rfcomm_send_credit(struct bt_rfcomm_dlc *dlc, uint8_t credits) +{ + struct bt_rfcomm_hdr *hdr; + struct net_buf *buf; + uint8_t fcs, cr; + + BT_DBG("Dlc %p credits %d", dlc, credits); + + buf = bt_l2cap_create_pdu(NULL, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + cr = BT_RFCOMM_UIH_CR(dlc->session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlc->dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UIH, + BT_RFCOMM_PF_UIH_CREDIT); + hdr->length = BT_RFCOMM_SET_LEN_8(0); + net_buf_add_u8(buf, credits); + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + return bt_l2cap_chan_send(&dlc->session->br_chan.chan, buf); +} + +static int rfcomm_dlc_start(struct bt_rfcomm_dlc *dlc) +{ + enum security_result result; + + BT_DBG("dlc %p", dlc); + + result = rfcomm_dlc_security(dlc); + switch (result) { + case RFCOMM_SECURITY_PASSED: + dlc->mtu = MIN(dlc->mtu, dlc->session->mtu); + dlc->state = BT_RFCOMM_STATE_CONFIG; + rfcomm_send_pn(dlc, BT_RFCOMM_MSG_CMD_CR); + break; + case RFCOMM_SECURITY_PENDING: + dlc->state = BT_RFCOMM_STATE_SECURITY_PENDING; + break; + case RFCOMM_SECURITY_REJECT: + default: + return -EIO; + } + + return 0; +} + +static void rfcomm_handle_ua(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_dlc *dlc, *next; + int err; + + if (!dlci) { + switch (session->state) { + case BT_RFCOMM_STATE_CONNECTING: + session->state = BT_RFCOMM_STATE_CONNECTED; + for (dlc = session->dlcs; dlc; dlc = next) { + next = dlc->_next; + if (dlc->role == BT_RFCOMM_ROLE_INITIATOR && + dlc->state == BT_RFCOMM_STATE_INIT) { + if (rfcomm_dlc_start(dlc) < 0) { + rfcomm_dlc_drop(dlc); + } + } + } + /* Disconnect session if there is no dlcs left */ + rfcomm_session_disconnect(session); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + session->state = BT_RFCOMM_STATE_DISCONNECTED; + /* Cancel disc timer */ + k_delayed_work_cancel(&session->rtx_work); + err = bt_l2cap_chan_disconnect(&session->br_chan.chan); + if (err < 0) { + session->state = BT_RFCOMM_STATE_IDLE; + } + break; + default: + break; + } + } else { + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); + if (!dlc) { + return; + } + + switch (dlc->state) { + case BT_RFCOMM_STATE_CONNECTING: + rfcomm_dlc_connected(dlc); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + rfcomm_dlc_drop(dlc); + rfcomm_session_disconnect(session); + break; + default: + break; + } + } +} + +static void rfcomm_handle_dm(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_dlc *dlc; + + BT_DBG("dlci %d", dlci); + + dlc = rfcomm_dlcs_remove_dlci(session->dlcs, dlci); + if (!dlc) { + return; + } + + rfcomm_dlc_disconnect(dlc); + rfcomm_session_disconnect(session); +} + +static void rfcomm_handle_msc(struct bt_rfcomm_session *session, + struct net_buf *buf, uint8_t cr) +{ + struct bt_rfcomm_msc *msc = (void *)buf->data; + struct bt_rfcomm_dlc *dlc; + uint8_t dlci = BT_RFCOMM_GET_DLCI(msc->dlci); + + BT_DBG("dlci %d", dlci); + + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); + if (!dlc) { + return; + } + + if (cr == BT_RFCOMM_MSG_RESP_CR) { + return; + } + + if (dlc->session->cfc == BT_RFCOMM_CFC_NOT_SUPPORTED) { + /* Only FC bit affects the flow on RFCOMM level */ + if (BT_RFCOMM_GET_FC(msc->v24_signal)) { + /* If FC bit is 1 the device is unable to accept frames. + * Take the semaphore with timeout K_NO_WAIT so that + * dlc thread will be blocked when it tries sem_take + * before sending the data. K_NO_WAIT timeout will make + * sure that RX thread will not be blocked while taking + * the semaphore. + */ + k_sem_take(&dlc->tx_credits, K_NO_WAIT); + } else { + /* Give the sem so that it will unblock the waiting dlc + * thread in sem_take(). + */ + k_sem_give(&dlc->tx_credits); + } + } + + rfcomm_send_msc(dlc, BT_RFCOMM_MSG_RESP_CR, msc->v24_signal); +} + +static void rfcomm_handle_rls(struct bt_rfcomm_session *session, + struct net_buf *buf, uint8_t cr) +{ + struct bt_rfcomm_rls *rls = (void *)buf->data; + uint8_t dlci = BT_RFCOMM_GET_DLCI(rls->dlci); + struct bt_rfcomm_dlc *dlc; + + BT_DBG("dlci %d", dlci); + + if (!cr) { + /* Ignore if its a response */ + return; + } + + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); + if (!dlc) { + return; + } + + /* As per the ETSI same line status has to returned in the response */ + rfcomm_send_rls(dlc, BT_RFCOMM_MSG_RESP_CR, rls->line_status); +} + +static void rfcomm_handle_rpn(struct bt_rfcomm_session *session, + struct net_buf *buf, uint8_t cr) +{ + struct bt_rfcomm_rpn default_rpn, *rpn = (void *)buf->data; + uint8_t dlci = BT_RFCOMM_GET_DLCI(rpn->dlci); + uint8_t data_bits, stop_bits, parity_bits; + /* Exclude fcs to get number of value bytes */ + uint8_t value_len = buf->len - 1; + + BT_DBG("dlci %d", dlci); + + if (!cr) { + /* Ignore if its a response */ + return; + } + + if (value_len == sizeof(*rpn)) { + /* Accept all the values proposed by the sender */ + rpn->param_mask = sys_cpu_to_le16(BT_RFCOMM_RPN_PARAM_MASK_ALL); + rfcomm_send_rpn(session, BT_RFCOMM_MSG_RESP_CR, rpn); + return; + } + + if (value_len != 1U) { + return; + } + + /* If only one value byte then current port settings has to be returned + * We will send default values + */ + default_rpn.dlci = BT_RFCOMM_SET_ADDR(dlci, 1); + default_rpn.baud_rate = BT_RFCOMM_RPN_BAUD_RATE_9600; + default_rpn.flow_control = BT_RFCOMM_RPN_FLOW_NONE; + default_rpn.xoff_char = BT_RFCOMM_RPN_XOFF_CHAR; + default_rpn.xon_char = BT_RFCOMM_RPN_XON_CHAR; + data_bits = BT_RFCOMM_RPN_DATA_BITS_8; + stop_bits = BT_RFCOMM_RPN_STOP_BITS_1; + parity_bits = BT_RFCOMM_RPN_PARITY_NONE; + default_rpn.line_settings = BT_RFCOMM_SET_LINE_SETTINGS(data_bits, + stop_bits, + parity_bits); + default_rpn.param_mask = sys_cpu_to_le16(BT_RFCOMM_RPN_PARAM_MASK_ALL); + + rfcomm_send_rpn(session, BT_RFCOMM_MSG_RESP_CR, &default_rpn); +} + +static void rfcomm_handle_pn(struct bt_rfcomm_session *session, + struct net_buf *buf, uint8_t cr) +{ + struct bt_rfcomm_pn *pn = (void *)buf->data; + struct bt_rfcomm_dlc *dlc; + + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, pn->dlci); + if (!dlc) { + /* Ignore if it is a response */ + if (!cr) { + return; + } + + if (!BT_RFCOMM_CHECK_MTU(pn->mtu)) { + BT_ERR("Invalid mtu %d", pn->mtu); + rfcomm_send_dm(session, pn->dlci); + return; + } + + dlc = rfcomm_dlc_accept(session, pn->dlci); + if (!dlc) { + rfcomm_send_dm(session, pn->dlci); + return; + } + + BT_DBG("Incoming connection accepted dlc %p", dlc); + + dlc->mtu = MIN(dlc->mtu, sys_le16_to_cpu(pn->mtu)); + + if (pn->flow_ctrl == BT_RFCOMM_PN_CFC_CMD) { + if (session->cfc == BT_RFCOMM_CFC_UNKNOWN) { + session->cfc = BT_RFCOMM_CFC_SUPPORTED; + } + k_sem_init(&dlc->tx_credits, 0, UINT32_MAX); + rfcomm_dlc_tx_give_credits(dlc, pn->credits); + } else { + session->cfc = BT_RFCOMM_CFC_NOT_SUPPORTED; + } + + dlc->state = BT_RFCOMM_STATE_CONFIG; + rfcomm_send_pn(dlc, BT_RFCOMM_MSG_RESP_CR); + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); + } else { + /* If its a command */ + if (cr) { + if (!BT_RFCOMM_CHECK_MTU(pn->mtu)) { + BT_ERR("Invalid mtu %d", pn->mtu); + rfcomm_dlc_close(dlc); + return; + } + dlc->mtu = MIN(dlc->mtu, sys_le16_to_cpu(pn->mtu)); + rfcomm_send_pn(dlc, BT_RFCOMM_MSG_RESP_CR); + } else { + if (dlc->state != BT_RFCOMM_STATE_CONFIG) { + return; + } + + dlc->mtu = MIN(dlc->mtu, sys_le16_to_cpu(pn->mtu)); + if (pn->flow_ctrl == BT_RFCOMM_PN_CFC_RESP) { + if (session->cfc == BT_RFCOMM_CFC_UNKNOWN) { + session->cfc = BT_RFCOMM_CFC_SUPPORTED; + } + k_sem_init(&dlc->tx_credits, 0, UINT32_MAX); + rfcomm_dlc_tx_give_credits(dlc, pn->credits); + } else { + session->cfc = BT_RFCOMM_CFC_NOT_SUPPORTED; + } + + dlc->state = BT_RFCOMM_STATE_CONNECTING; + rfcomm_send_sabm(session, dlc->dlci); + } + } +} + +static void rfcomm_handle_disc(struct bt_rfcomm_session *session, uint8_t dlci) +{ + struct bt_rfcomm_dlc *dlc; + + BT_DBG("Dlci %d", dlci); + + if (dlci) { + dlc = rfcomm_dlcs_remove_dlci(session->dlcs, dlci); + if (!dlc) { + rfcomm_send_dm(session, dlci); + return; + } + + rfcomm_send_ua(session, dlci); + rfcomm_dlc_disconnect(dlc); + + if (!session->dlcs) { + /* Start a session idle timer */ + k_delayed_work_submit(&dlc->session->rtx_work, + RFCOMM_IDLE_TIMEOUT); + } + } else { + /* Cancel idle timer */ + k_delayed_work_cancel(&session->rtx_work); + rfcomm_send_ua(session, 0); + rfcomm_session_disconnected(session); + } +} + +static void rfcomm_handle_msg(struct bt_rfcomm_session *session, + struct net_buf *buf) +{ + struct bt_rfcomm_msg_hdr *hdr; + uint8_t msg_type, len, cr; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small RFCOMM message"); + return; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + msg_type = BT_RFCOMM_GET_MSG_TYPE(hdr->type); + cr = BT_RFCOMM_GET_MSG_CR(hdr->type); + len = BT_RFCOMM_GET_LEN(hdr->len); + + BT_DBG("msg type %x cr %x", msg_type, cr); + + switch (msg_type) { + case BT_RFCOMM_PN: + rfcomm_handle_pn(session, buf, cr); + break; + case BT_RFCOMM_MSC: + rfcomm_handle_msc(session, buf, cr); + break; + case BT_RFCOMM_RLS: + rfcomm_handle_rls(session, buf, cr); + break; + case BT_RFCOMM_RPN: + rfcomm_handle_rpn(session, buf, cr); + break; + case BT_RFCOMM_TEST: + if (!cr) { + break; + } + rfcomm_send_test(session, BT_RFCOMM_MSG_RESP_CR, buf->data, + buf->len - 1); + break; + case BT_RFCOMM_FCON: + if (session->cfc == BT_RFCOMM_CFC_SUPPORTED) { + BT_ERR("FCON received when CFC is supported "); + return; + } + + if (!cr) { + break; + } + + /* Give the sem so that it will unblock the waiting dlc threads + * of this session in sem_take(). + */ + k_sem_give(&session->fc); + rfcomm_send_fcon(session, BT_RFCOMM_MSG_RESP_CR); + break; + case BT_RFCOMM_FCOFF: + if (session->cfc == BT_RFCOMM_CFC_SUPPORTED) { + BT_ERR("FCOFF received when CFC is supported "); + return; + } + + if (!cr) { + break; + } + + /* Take the semaphore with timeout K_NO_WAIT so that all the + * dlc threads in this session will be blocked when it tries + * sem_take before sending the data. K_NO_WAIT timeout will + * make sure that RX thread will not be blocked while taking + * the semaphore. + */ + k_sem_take(&session->fc, K_NO_WAIT); + rfcomm_send_fcoff(session, BT_RFCOMM_MSG_RESP_CR); + break; + default: + BT_WARN("Unknown/Unsupported RFCOMM Msg type 0x%02x", msg_type); + rfcomm_send_nsc(session, hdr->type); + break; + } +} + +static void rfcomm_dlc_update_credits(struct bt_rfcomm_dlc *dlc) +{ + uint8_t credits; + + if (dlc->session->cfc == BT_RFCOMM_CFC_NOT_SUPPORTED) { + return; + } + + BT_DBG("dlc %p credits %u", dlc, dlc->rx_credit); + + /* Only give more credits if it went below the defined threshold */ + if (dlc->rx_credit > RFCOMM_CREDITS_THRESHOLD) { + return; + } + + /* Restore credits */ + credits = RFCOMM_MAX_CREDITS - dlc->rx_credit; + dlc->rx_credit += credits; + + rfcomm_send_credit(dlc, credits); +} + +static void rfcomm_handle_data(struct bt_rfcomm_session *session, + struct net_buf *buf, uint8_t dlci, uint8_t pf) + +{ + struct bt_rfcomm_dlc *dlc; + + BT_DBG("dlci %d, pf %d", dlci, pf); + + dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); + if (!dlc) { + BT_ERR("Data recvd in non existing DLC"); + rfcomm_send_dm(session, dlci); + return; + } + + BT_DBG("dlc %p rx credit %d", dlc, dlc->rx_credit); + + if (dlc->state != BT_RFCOMM_STATE_CONNECTED) { + return; + } + + if (pf == BT_RFCOMM_PF_UIH_CREDIT) { + rfcomm_dlc_tx_give_credits(dlc, net_buf_pull_u8(buf)); + } + + if (buf->len > BT_RFCOMM_FCS_SIZE) { + if (dlc->session->cfc == BT_RFCOMM_CFC_SUPPORTED && + !dlc->rx_credit) { + BT_ERR("Data recvd when rx credit is 0"); + rfcomm_dlc_close(dlc); + return; + } + + /* Remove FCS */ + buf->len -= BT_RFCOMM_FCS_SIZE; + if (dlc->ops && dlc->ops->recv) { + dlc->ops->recv(dlc, buf); + } + + dlc->rx_credit--; + rfcomm_dlc_update_credits(dlc); + } +} + +int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf) +{ + struct bt_rfcomm_hdr *hdr; + uint8_t fcs, cr; + + if (!buf) { + return -EINVAL; + } + + BT_DBG("dlc %p tx credit %d", dlc, k_sem_count_get(&dlc->tx_credits)); + + if (dlc->state != BT_RFCOMM_STATE_CONNECTED) { + return -ENOTCONN; + } + + if (buf->len > dlc->mtu) { + return -EMSGSIZE; + } + + if (buf->len > BT_RFCOMM_MAX_LEN_8) { + uint16_t *len; + + /* Length is 2 byte */ + hdr = net_buf_push(buf, sizeof(*hdr) + 1); + len = (uint16_t *)&hdr->length; + *len = BT_RFCOMM_SET_LEN_16(sys_cpu_to_le16(buf->len - + sizeof(*hdr) - 1)); + } else { + hdr = net_buf_push(buf, sizeof(*hdr)); + hdr->length = BT_RFCOMM_SET_LEN_8(buf->len - sizeof(*hdr)); + } + + cr = BT_RFCOMM_UIH_CR(dlc->session->role); + hdr->address = BT_RFCOMM_SET_ADDR(dlc->dlci, cr); + hdr->control = BT_RFCOMM_SET_CTRL(BT_RFCOMM_UIH, + BT_RFCOMM_PF_UIH_NO_CREDIT); + + fcs = rfcomm_calc_fcs(BT_RFCOMM_FCS_LEN_UIH, buf->data); + net_buf_add_u8(buf, fcs); + + net_buf_put(&dlc->tx_queue, buf); + + return buf->len; +} + +static int rfcomm_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_rfcomm_session *session = RFCOMM_SESSION(chan); + struct bt_rfcomm_hdr *hdr = (void *)buf->data; + uint8_t dlci, frame_type, fcs, fcs_len; + + /* Need to consider FCS also*/ + if (buf->len < (sizeof(*hdr) + 1)) { + BT_ERR("Too small RFCOMM Frame"); + return 0; + } + + dlci = BT_RFCOMM_GET_DLCI(hdr->address); + frame_type = BT_RFCOMM_GET_FRAME_TYPE(hdr->control); + + BT_DBG("session %p dlci %x type %x", session, dlci, frame_type); + + fcs_len = (frame_type == BT_RFCOMM_UIH) ? BT_RFCOMM_FCS_LEN_UIH : + BT_RFCOMM_FCS_LEN_NON_UIH; + fcs = *(net_buf_tail(buf) - 1); + if (!rfcomm_check_fcs(fcs_len, buf->data, fcs)) { + BT_ERR("FCS check failed"); + return 0; + } + + if (BT_RFCOMM_LEN_EXTENDED(hdr->length)) { + net_buf_pull(buf, sizeof(*hdr) + 1); + } else { + net_buf_pull(buf, sizeof(*hdr)); + } + + switch (frame_type) { + case BT_RFCOMM_SABM: + rfcomm_handle_sabm(session, dlci); + break; + case BT_RFCOMM_UIH: + if (!dlci) { + rfcomm_handle_msg(session, buf); + } else { + rfcomm_handle_data(session, buf, dlci, + BT_RFCOMM_GET_PF(hdr->control)); + } + break; + case BT_RFCOMM_DISC: + rfcomm_handle_disc(session, dlci); + break; + case BT_RFCOMM_UA: + rfcomm_handle_ua(session, dlci); + break; + case BT_RFCOMM_DM: + rfcomm_handle_dm(session, dlci); + break; + default: + BT_WARN("Unknown/Unsupported RFCOMM Frame type 0x%02x", + frame_type); + break; + } + + return 0; +} + +static void rfcomm_encrypt_change(struct bt_l2cap_chan *chan, + uint8_t hci_status) +{ + struct bt_rfcomm_session *session = RFCOMM_SESSION(chan); + struct bt_conn *conn = chan->conn; + struct bt_rfcomm_dlc *dlc, *next; + + BT_DBG("session %p status 0x%02x encr 0x%02x", session, hci_status, + conn->encrypt); + + for (dlc = session->dlcs; dlc; dlc = next) { + next = dlc->_next; + + if (dlc->state != BT_RFCOMM_STATE_SECURITY_PENDING) { + continue; + } + + if (hci_status || !conn->encrypt || + conn->sec_level < dlc->required_sec_level) { + rfcomm_dlc_close(dlc); + continue; + } + + if (dlc->role == BT_RFCOMM_ROLE_ACCEPTOR) { + rfcomm_send_ua(session, dlc->dlci); + rfcomm_dlc_connected(dlc); + } else { + dlc->mtu = MIN(dlc->mtu, session->mtu); + dlc->state = BT_RFCOMM_STATE_CONFIG; + rfcomm_send_pn(dlc, BT_RFCOMM_MSG_CMD_CR); + } + } +} + +static void rfcomm_session_rtx_timeout(struct k_work *work) +{ + struct bt_rfcomm_session *session = SESSION_RTX(work); + + BT_WARN("session %p state %d timeout", session, session->state); + + switch (session->state) { + case BT_RFCOMM_STATE_CONNECTED: + rfcomm_session_disconnect(session); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + session->state = BT_RFCOMM_STATE_DISCONNECTED; + if (bt_l2cap_chan_disconnect(&session->br_chan.chan) < 0) { + session->state = BT_RFCOMM_STATE_IDLE; + } + break; + } +} + +static struct bt_rfcomm_session *rfcomm_session_new(bt_rfcomm_role_t role) +{ + int i; + static const struct bt_l2cap_chan_ops ops = { + .connected = rfcomm_connected, + .disconnected = rfcomm_disconnected, + .recv = rfcomm_recv, + .encrypt_change = rfcomm_encrypt_change, + }; + + for (i = 0; i < ARRAY_SIZE(bt_rfcomm_pool); i++) { + struct bt_rfcomm_session *session = &bt_rfcomm_pool[i]; + + if (session->br_chan.chan.conn) { + continue; + } + + BT_DBG("session %p initialized", session); + + session->br_chan.chan.ops = &ops; + session->br_chan.rx.mtu = CONFIG_BT_L2CAP_RX_MTU; + session->state = BT_RFCOMM_STATE_INIT; + session->role = role; + session->cfc = BT_RFCOMM_CFC_UNKNOWN; + k_delayed_work_init(&session->rtx_work, + rfcomm_session_rtx_timeout); + k_sem_init(&session->fc, 0, 1); + + return session; + } + + return NULL; +} + +int bt_rfcomm_dlc_connect(struct bt_conn *conn, struct bt_rfcomm_dlc *dlc, + uint8_t channel) +{ + struct bt_rfcomm_session *session; + struct bt_l2cap_chan *chan; + uint8_t dlci; + int ret; + + BT_DBG("conn %p dlc %p channel %d", conn, dlc, channel); + + if (!dlc) { + return -EINVAL; + } + + if (!conn || conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (channel < RFCOMM_CHANNEL_START || channel > RFCOMM_CHANNEL_END) { + return -EINVAL; + } + + if (!BT_RFCOMM_CHECK_MTU(dlc->mtu)) { + return -EINVAL; + } + + session = rfcomm_sessions_lookup_bt_conn(conn); + if (!session) { + session = rfcomm_session_new(BT_RFCOMM_ROLE_INITIATOR); + if (!session) { + return -ENOMEM; + } + } + + dlci = BT_RFCOMM_DLCI(session->role, channel); + + if (rfcomm_dlcs_lookup_dlci(session->dlcs, dlci)) { + return -EBUSY; + } + + rfcomm_dlc_init(dlc, session, dlci, BT_RFCOMM_ROLE_INITIATOR); + + switch (session->state) { + case BT_RFCOMM_STATE_INIT: + if (session->role == BT_RFCOMM_ROLE_ACCEPTOR) { + /* There is an ongoing incoming conn */ + break; + } + chan = &session->br_chan.chan; + chan->required_sec_level = dlc->required_sec_level; + ret = bt_l2cap_chan_connect(conn, chan, BT_L2CAP_PSM_RFCOMM); + if (ret < 0) { + session->state = BT_RFCOMM_STATE_IDLE; + goto fail; + } + session->state = BT_RFCOMM_STATE_CONNECTING; + break; + case BT_RFCOMM_STATE_CONNECTING: + break; + case BT_RFCOMM_STATE_CONNECTED: + ret = rfcomm_dlc_start(dlc); + if (ret < 0) { + goto fail; + } + /* Cancel idle timer if any */ + k_delayed_work_cancel(&session->rtx_work); + break; + default: + BT_ERR("Invalid session state %d", session->state); + ret = -EINVAL; + goto fail; + } + + return 0; + +fail: + rfcomm_dlcs_remove_dlci(session->dlcs, dlc->dlci); + dlc->state = BT_RFCOMM_STATE_IDLE; + dlc->session = NULL; + return ret; +} + +int bt_rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc) +{ + BT_DBG("dlc %p", dlc); + + if (!dlc) { + return -EINVAL; + } + + if (dlc->state == BT_RFCOMM_STATE_CONNECTED) { + /* This is to handle user initiated disconnect to send pending + * bufs in the queue before disconnecting + * Queue a dummy buffer (in case if queue is empty) to wake up + * and stop the tx thread. + */ + dlc->state = BT_RFCOMM_STATE_USER_DISCONNECT; + net_buf_put(&dlc->tx_queue, + net_buf_alloc(&dummy_pool, K_NO_WAIT)); + + k_delayed_work_submit(&dlc->rtx_work, RFCOMM_DISC_TIMEOUT); + + return 0; + } + + return rfcomm_dlc_close(dlc); +} + +static int rfcomm_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + struct bt_rfcomm_session *session; + + BT_DBG("conn %p", conn); + + session = rfcomm_session_new(BT_RFCOMM_ROLE_ACCEPTOR); + if (session) { + *chan = &session->br_chan.chan; + return 0; + } + + BT_ERR("No available RFCOMM context for conn %p", conn); + + return -ENOMEM; +} + +void bt_rfcomm_init(void) +{ +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + k_lifo_init(&dummy_pool.free, CONFIG_BT_MAX_CONN); + net_buf_init(&dummy_pool, CONFIG_BT_MAX_CONN, 1, NULL); +#endif + static struct bt_l2cap_server server = { + .psm = BT_L2CAP_PSM_RFCOMM, + .accept = rfcomm_accept, + .sec_level = BT_SECURITY_L1, + }; + + bt_l2cap_br_server_register(&server); +} diff --git a/components/ble/ble_stack/host/rfcomm_internal.h b/components/ble/ble_stack/host/rfcomm_internal.h new file mode 100644 index 00000000..d0b645e4 --- /dev/null +++ b/components/ble/ble_stack/host/rfcomm_internal.h @@ -0,0 +1,213 @@ +/** @file + * @brief Internal APIs for Bluetooth RFCOMM handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +typedef enum { + BT_RFCOMM_CFC_UNKNOWN, + BT_RFCOMM_CFC_NOT_SUPPORTED, + BT_RFCOMM_CFC_SUPPORTED, +} __packed bt_rfcomm_cfc_t; + +/* RFCOMM signalling connection specific context */ +struct bt_rfcomm_session { + /* L2CAP channel this context is associated with */ + struct bt_l2cap_br_chan br_chan; + /* Response Timeout eXpired (RTX) timer */ + struct k_delayed_work rtx_work; + /* Binary sem for aggregate fc */ + struct k_sem fc; + struct bt_rfcomm_dlc *dlcs; + uint16_t mtu; + uint8_t state; + bt_rfcomm_role_t role; + bt_rfcomm_cfc_t cfc; +}; + +enum { + BT_RFCOMM_STATE_IDLE, + BT_RFCOMM_STATE_INIT, + BT_RFCOMM_STATE_SECURITY_PENDING, + BT_RFCOMM_STATE_CONNECTING, + BT_RFCOMM_STATE_CONNECTED, + BT_RFCOMM_STATE_CONFIG, + BT_RFCOMM_STATE_USER_DISCONNECT, + BT_RFCOMM_STATE_DISCONNECTING, + BT_RFCOMM_STATE_DISCONNECTED, +}; + +struct bt_rfcomm_hdr { + uint8_t address; + uint8_t control; + uint8_t length; +} __packed; + +#define BT_RFCOMM_SABM 0x2f +#define BT_RFCOMM_UA 0x63 +#define BT_RFCOMM_UIH 0xef + +struct bt_rfcomm_msg_hdr { + uint8_t type; + uint8_t len; +} __packed; + +#define BT_RFCOMM_PN 0x20 +struct bt_rfcomm_pn { + uint8_t dlci; + uint8_t flow_ctrl; + uint8_t priority; + uint8_t ack_timer; + uint16_t mtu; + uint8_t max_retrans; + uint8_t credits; +} __packed; + +#define BT_RFCOMM_MSC 0x38 +struct bt_rfcomm_msc { + uint8_t dlci; + uint8_t v24_signal; +} __packed; + +#define BT_RFCOMM_DISC 0x43 +#define BT_RFCOMM_DM 0x0f + +#define BT_RFCOMM_RLS 0x14 +struct bt_rfcomm_rls { + uint8_t dlci; + uint8_t line_status; +} __packed; + +#define BT_RFCOMM_RPN 0x24 +struct bt_rfcomm_rpn { + uint8_t dlci; + uint8_t baud_rate; + uint8_t line_settings; + uint8_t flow_control; + uint8_t xon_char; + uint8_t xoff_char; + uint16_t param_mask; +} __packed; + +#define BT_RFCOMM_TEST 0x08 +#define BT_RFCOMM_NSC 0x04 + +#define BT_RFCOMM_FCON 0x28 +#define BT_RFCOMM_FCOFF 0x18 + +/* Default RPN Settings */ +#define BT_RFCOMM_RPN_BAUD_RATE_9600 0x03 +#define BT_RFCOMM_RPN_DATA_BITS_8 0x03 +#define BT_RFCOMM_RPN_STOP_BITS_1 0x00 +#define BT_RFCOMM_RPN_PARITY_NONE 0x00 +#define BT_RFCOMM_RPN_FLOW_NONE 0x00 +#define BT_RFCOMM_RPN_XON_CHAR 0x11 +#define BT_RFCOMM_RPN_XOFF_CHAR 0x13 + +/* Set 1 to all the param mask except reserved */ +#define BT_RFCOMM_RPN_PARAM_MASK_ALL 0x3f7f + +#define BT_RFCOMM_SET_LINE_SETTINGS(data, stop, parity) ((data & 0x3) | \ + ((stop & 0x1) << 2) | \ + ((parity & 0x7) << 3)) + +/* DV = 1 IC = 0 RTR = 1 RTC = 1 FC = 0 EXT = 0 */ +#define BT_RFCOMM_DEFAULT_V24_SIG 0x8d + +#define BT_RFCOMM_GET_FC(v24_signal) (((v24_signal) & 0x02) >> 1) + +#define BT_RFCOMM_SIG_MIN_MTU 23 +#define BT_RFCOMM_SIG_MAX_MTU 32767 + +#define BT_RFCOMM_CHECK_MTU(mtu) (!!((mtu) >= BT_RFCOMM_SIG_MIN_MTU && \ + (mtu) <= BT_RFCOMM_SIG_MAX_MTU)) + +/* Helper to calculate needed outgoing buffer size. + * Length in rfcomm header can be two bytes depending on user data length. + * One byte in the tail should be reserved for FCS. + */ +#define BT_RFCOMM_BUF_SIZE(mtu) (BT_BUF_RESERVE + \ + BT_HCI_ACL_HDR_SIZE + BT_L2CAP_HDR_SIZE + \ + sizeof(struct bt_rfcomm_hdr) + 1 + (mtu) + \ + BT_RFCOMM_FCS_SIZE) + +#define BT_RFCOMM_GET_DLCI(addr) (((addr) & 0xfc) >> 2) +#define BT_RFCOMM_GET_FRAME_TYPE(ctrl) ((ctrl) & 0xef) +#define BT_RFCOMM_GET_MSG_TYPE(type) (((type) & 0xfc) >> 2) +#define BT_RFCOMM_GET_MSG_CR(type) (((type) & 0x02) >> 1) +#define BT_RFCOMM_GET_LEN(len) (((len) & 0xfe) >> 1) +#define BT_RFCOMM_GET_CHANNEL(dlci) ((dlci) >> 1) +#define BT_RFCOMM_GET_PF(ctrl) (((ctrl) & 0x10) >> 4) + +#define BT_RFCOMM_SET_ADDR(dlci, cr) ((((dlci) & 0x3f) << 2) | \ + ((cr) << 1) | 0x01) +#define BT_RFCOMM_SET_CTRL(type, pf) (((type) & 0xef) | ((pf) << 4)) +#define BT_RFCOMM_SET_LEN_8(len) (((len) << 1) | 1) +#define BT_RFCOMM_SET_LEN_16(len) ((len) << 1) +#define BT_RFCOMM_SET_MSG_TYPE(type, cr) (((type) << 2) | (cr << 1) | 0x01) + +#define BT_RFCOMM_LEN_EXTENDED(len) (!((len) & 0x01)) + +/* For CR in UIH Packet header + * Initiating station have the C/R bit set to 1 and those sent by the + * responding station have the C/R bit set to 0 + */ +#define BT_RFCOMM_UIH_CR(role) ((role) == BT_RFCOMM_ROLE_INITIATOR) + +/* For CR in Non UIH Packet header + * Command + * Initiator --> Responder 1 + * Responder --> Initiator 0 + * Response + * Initiator --> Responder 0 + * Responder --> Initiator 1 + */ +#define BT_RFCOMM_CMD_CR(role) ((role) == BT_RFCOMM_ROLE_INITIATOR) +#define BT_RFCOMM_RESP_CR(role) ((role) == BT_RFCOMM_ROLE_ACCEPTOR) + +/* For CR in MSG header + * If the C/R bit is set to 1 the message is a command, + * if it is set to 0 the message is a response. + */ +#define BT_RFCOMM_MSG_CMD_CR 1 +#define BT_RFCOMM_MSG_RESP_CR 0 + +#define BT_RFCOMM_DLCI(role, channel) ((((channel) & 0x1f) << 1) | \ + ((role) == BT_RFCOMM_ROLE_ACCEPTOR)) + +/* Excluding ext bit */ +#define BT_RFCOMM_MAX_LEN_8 127 + +/* Length can be 2 bytes depending on data size */ +#define BT_RFCOMM_HDR_SIZE (sizeof(struct bt_rfcomm_hdr) + 1) +#define BT_RFCOMM_FCS_SIZE 1 + +#define BT_RFCOMM_FCS_LEN_UIH 2 +#define BT_RFCOMM_FCS_LEN_NON_UIH 3 + +/* For non UIH packets + * The P bit set to 1 shall be used to solicit a response frame with the + * F bit set to 1 from the other station. + */ +#define BT_RFCOMM_PF_NON_UIH 1 + +/* For UIH packets + * Both stations set the P-bit to 0 + * If credit based flow control is used, If P/F is 1 then one credit byte + * will be there after control in the frame else no credit byte. + */ +#define BT_RFCOMM_PF_UIH 0 +#define BT_RFCOMM_PF_UIH_CREDIT 1 +#define BT_RFCOMM_PF_UIH_NO_CREDIT 0 + +#define BT_RFCOMM_PN_CFC_CMD 0xf0 +#define BT_RFCOMM_PN_CFC_RESP 0xe0 + +/* Initialize RFCOMM signal layer */ +void bt_rfcomm_init(void); diff --git a/components/ble/ble_stack/host/sdp.c b/components/ble/ble_stack/host/sdp.c new file mode 100644 index 00000000..9e45b2ed --- /dev/null +++ b/components/ble/ble_stack/host/sdp.c @@ -0,0 +1,2545 @@ +/** @file + * @brief Service Discovery Protocol handling. + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include <../bluetooth/buf.h> +#include <../bluetooth/sdp.h> + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SDP) +#define LOG_MODULE_NAME bt_sdp +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "sdp_internal.h" + +#define SDP_PSM 0x0001 + +#define SDP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_sdp, chan.chan) + +#define IN_RANGE(val, min, max) (val >= min && val <= max) + +#define SDP_DATA_MTU 200 + +#define SDP_MTU (SDP_DATA_MTU + sizeof(struct bt_sdp_hdr)) + +#define MAX_NUM_ATT_ID_FILTER 10 + +#define SDP_SERVICE_HANDLE_BASE 0x10000 + +#define SDP_DATA_ELEM_NEST_LEVEL_MAX 5 + +/* Size of Cont state length */ +#define SDP_CONT_STATE_LEN_SIZE 1 + +/* 1 byte for the no. of services searched till this response */ +/* 2 bytes for the total no. of matching records */ +#define SDP_SS_CONT_STATE_SIZE 3 + +/* 1 byte for the no. of attributes searched till this response */ +#define SDP_SA_CONT_STATE_SIZE 1 + +/* 1 byte for the no. of services searched till this response */ +/* 1 byte for the no. of attributes searched till this response */ +#define SDP_SSA_CONT_STATE_SIZE 2 + +#define SDP_INVALID 0xff + +struct bt_sdp { + struct bt_l2cap_br_chan chan; + struct k_fifo partial_resp_queue; + /* TODO: Allow more than one pending request */ +}; + +static struct bt_sdp_record *db; +static uint8_t num_services; + +static struct bt_sdp bt_sdp_pool[CONFIG_BT_MAX_CONN]; + +/* Pool for outgoing SDP packets */ +#if !defined(BFLB_DYNAMIC_ALLOC_MEM) +NET_BUF_POOL_FIXED_DEFINE(sdp_pool, CONFIG_BT_MAX_CONN, + BT_L2CAP_BUF_SIZE(SDP_MTU), NULL); +#else +struct net_buf_pool sdp_pool; +#endif + +#define SDP_CLIENT_CHAN(_ch) CONTAINER_OF(_ch, struct bt_sdp_client, chan.chan) + +#define SDP_CLIENT_MTU 64 + +struct bt_sdp_client { + struct bt_l2cap_br_chan chan; + /* list of waiting to be resolved UUID params */ + sys_slist_t reqs; + /* required SDP transaction ID */ + uint16_t tid; + /* UUID params holder being now resolved */ + const struct bt_sdp_discover_params *param; + /* PDU continuation state object */ + struct bt_sdp_pdu_cstate cstate; + /* buffer for collecting record data */ + struct net_buf *rec_buf; +}; + +static struct bt_sdp_client bt_sdp_client_pool[CONFIG_BT_MAX_CONN]; + +enum { + BT_SDP_ITER_STOP, + BT_SDP_ITER_CONTINUE, +}; + +struct search_state { + uint16_t att_list_size; + uint8_t current_svc; + uint8_t last_att; + bool pkt_full; +}; + +struct select_attrs_data { + struct bt_sdp_record *rec; + struct net_buf *rsp_buf; + struct bt_sdp *sdp; + struct bt_sdp_data_elem_seq *seq; + struct search_state *state; + uint32_t *filter; + uint16_t max_att_len; + uint16_t att_list_len; + uint8_t cont_state_size; + uint8_t num_filters; + bool new_service; +}; + +/* @typedef bt_sdp_attr_func_t + * @brief SDP attribute iterator callback. + * + * @param attr Attribute found. + * @param att_idx Index of the found attribute in the attribute database. + * @param user_data Data given. + * + * @return BT_SDP_ITER_CONTINUE if should continue to the next attribute + * or BT_SDP_ITER_STOP to stop. + */ +typedef uint8_t (*bt_sdp_attr_func_t)(struct bt_sdp_attribute *attr, + uint8_t att_idx, void *user_data); + +/* @typedef bt_sdp_svc_func_t + * @brief SDP service record iterator callback. + * + * @param rec Service record found. + * @param user_data Data given. + * + * @return BT_SDP_ITER_CONTINUE if should continue to the next service record + * or BT_SDP_ITER_STOP to stop. + */ +typedef uint8_t (*bt_sdp_svc_func_t)(struct bt_sdp_record *rec, + void *user_data); + +/* @brief Callback for SDP connection + * + * Gets called when an SDP connection is established + * + * @param chan L2CAP channel + * + * @return None + */ +static void bt_sdp_connected(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_br_chan *ch = CONTAINER_OF(chan, + struct bt_l2cap_br_chan, + chan); + + struct bt_sdp *sdp = CONTAINER_OF(ch, struct bt_sdp, chan); + + BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid); + + k_fifo_init(&sdp->partial_resp_queue, 20); //MBHJ +} + +/** @brief Callback for SDP disconnection + * + * Gets called when an SDP connection is terminated + * + * @param chan L2CAP channel + * + * @return None + */ +static void bt_sdp_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_l2cap_br_chan *ch = CONTAINER_OF(chan, + struct bt_l2cap_br_chan, + chan); + + struct bt_sdp *sdp = CONTAINER_OF(ch, struct bt_sdp, chan); + + BT_DBG("chan %p cid 0x%04x", ch, ch->tx.cid); + + (void)memset(sdp, 0, sizeof(*sdp)); +} + +/* @brief Creates an SDP PDU + * + * Creates an empty SDP PDU and returns the buffer + * + * @param None + * + * @return Pointer to the net_buf buffer + */ +static struct net_buf *bt_sdp_create_pdu(void) +{ + return bt_l2cap_create_pdu(&sdp_pool, sizeof(struct bt_sdp_hdr)); +} + +/* @brief Sends out an SDP PDU + * + * Sends out an SDP PDU after adding the relevant header + * + * @param chan L2CAP channel + * @param buf Buffer to be sent out + * @param op Opcode to be used in the packet header + * @param tid Transaction ID to be used in the packet header + * + * @return None + */ +static void bt_sdp_send(struct bt_l2cap_chan *chan, struct net_buf *buf, + uint8_t op, uint16_t tid) +{ + struct bt_sdp_hdr *hdr; + uint16_t param_len = buf->len; + + hdr = net_buf_push(buf, sizeof(struct bt_sdp_hdr)); + hdr->op_code = op; + hdr->tid = tid; + hdr->param_len = sys_cpu_to_be16(param_len); + + bt_l2cap_chan_send(chan, buf); +} + +/* @brief Sends an error response PDU + * + * Creates and sends an error response PDU + * + * @param chan L2CAP channel + * @param err Error code to be sent in the packet + * @param tid Transaction ID to be used in the packet header + * + * @return None + */ +static void send_err_rsp(struct bt_l2cap_chan *chan, uint16_t err, + uint16_t tid) +{ + struct net_buf *buf; + + BT_DBG("tid %u, error %u", tid, err); + + buf = bt_sdp_create_pdu(); + + net_buf_add_be16(buf, err); + + bt_sdp_send(chan, buf, BT_SDP_ERROR_RSP, tid); +} + +/* @brief Parses data elements from a net_buf + * + * Parses the first data element from a buffer and splits it into type, size, + * data. Used for parsing incoming requests. Net buf is advanced to the data + * part of the element. + * + * @param buf Buffer to be advanced + * @param data_elem Pointer to the parsed data element structure + * + * @return 0 for success, or relevant error code + */ +static uint16_t parse_data_elem(struct net_buf *buf, + struct bt_sdp_data_elem *data_elem) +{ + uint8_t size_field_len = 0U; /* Space used to accommodate the size */ + + if (buf->len < 1) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + data_elem->type = net_buf_pull_u8(buf); + + switch (data_elem->type & BT_SDP_TYPE_DESC_MASK) { + case BT_SDP_UINT8: + case BT_SDP_INT8: + case BT_SDP_UUID_UNSPEC: + case BT_SDP_BOOL: + data_elem->data_size = BIT(data_elem->type & + BT_SDP_SIZE_DESC_MASK); + break; + case BT_SDP_TEXT_STR_UNSPEC: + case BT_SDP_SEQ_UNSPEC: + case BT_SDP_ALT_UNSPEC: + case BT_SDP_URL_STR_UNSPEC: + size_field_len = BIT((data_elem->type & BT_SDP_SIZE_DESC_MASK) - + BT_SDP_SIZE_INDEX_OFFSET); + if (buf->len < size_field_len) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + switch (size_field_len) { + case 1: + data_elem->data_size = net_buf_pull_u8(buf); + break; + case 2: + data_elem->data_size = net_buf_pull_be16(buf); + break; + case 4: + data_elem->data_size = net_buf_pull_be32(buf); + break; + default: + BT_WARN("Invalid size in remote request"); + return BT_SDP_INVALID_SYNTAX; + } + break; + default: + BT_WARN("Invalid type in remote request"); + return BT_SDP_INVALID_SYNTAX; + } + + if (buf->len < data_elem->data_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + data_elem->total_size = data_elem->data_size + size_field_len + 1; + data_elem->data = buf->data; + + return 0; +} + +/* @brief Searches for an UUID within an attribute + * + * Searches for an UUID within an attribute. If the attribute has data element + * sequences, it recursively searches within them as well. On finding a match + * with the UUID, it sets the found flag. + * + * @param elem Attribute to be used as the search space (haystack) + * @param uuid UUID to be looked for (needle) + * @param found Flag set to true if the UUID is found (to be returned) + * @param nest_level Used to limit the extent of recursion into nested data + * elements, to avoid potential stack overflows + * + * @return Size of the last data element that has been searched + * (used in recursion) + */ +static uint32_t search_uuid(struct bt_sdp_data_elem *elem, struct bt_uuid *uuid, + bool *found, uint8_t nest_level) +{ + const uint8_t *cur_elem; + uint32_t seq_size, size; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_32 u32; + struct bt_uuid_128 u128; + } u; + + if (*found) { + return 0; + } + + /* Limit recursion depth to avoid stack overflows */ + if (nest_level == SDP_DATA_ELEM_NEST_LEVEL_MAX) { + return 0; + } + + seq_size = elem->data_size; + cur_elem = elem->data; + + if ((elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_UUID_UNSPEC) { + if (seq_size == 2U) { + u.uuid.type = BT_UUID_TYPE_16; + u.u16.val = *((uint16_t *)cur_elem); + if (!bt_uuid_cmp(&u.uuid, uuid)) { + *found = true; + } + } else if (seq_size == 4U) { + u.uuid.type = BT_UUID_TYPE_32; + u.u32.val = *((uint32_t *)cur_elem); + if (!bt_uuid_cmp(&u.uuid, uuid)) { + *found = true; + } + } else if (seq_size == 16U) { + u.uuid.type = BT_UUID_TYPE_128; + memcpy(u.u128.val, cur_elem, seq_size); + if (!bt_uuid_cmp(&u.uuid, uuid)) { + *found = true; + } + } else { + BT_WARN("Invalid UUID size in local database"); + BT_ASSERT(0); + } + } + + if ((elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_SEQ_UNSPEC || + (elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_ALT_UNSPEC) { + do { + /* Recursively parse data elements */ + size = search_uuid((struct bt_sdp_data_elem *)cur_elem, + uuid, found, nest_level + 1); + if (*found) { + return 0; + } + cur_elem += sizeof(struct bt_sdp_data_elem); + seq_size -= size; + } while (seq_size); + } + + return elem->total_size; +} + +/* @brief SDP service record iterator. + * + * Iterate over service records from a starting point. + * + * @param func Callback function. + * @param user_data Data to pass to the callback. + * + * @return Pointer to the record where the iterator stopped, or NULL if all + * records are covered + */ +static struct bt_sdp_record *bt_sdp_foreach_svc(bt_sdp_svc_func_t func, + void *user_data) +{ + struct bt_sdp_record *rec = db; + + while (rec) { + if (func(rec, user_data) == BT_SDP_ITER_STOP) { + break; + } + + rec = rec->next; + } + return rec; +} + +/* @brief Inserts a service record into a record pointer list + * + * Inserts a service record into a record pointer list + * + * @param rec The current service record. + * @param user_data Pointer to the destination record list. + * + * @return BT_SDP_ITER_CONTINUE to move on to the next record. + */ +static uint8_t insert_record(struct bt_sdp_record *rec, void *user_data) +{ + struct bt_sdp_record **rec_list = user_data; + + rec_list[rec->index] = rec; + + return BT_SDP_ITER_CONTINUE; +} + +/* @brief Looks for matching UUIDs in a list of service records + * + * Parses out a sequence of UUIDs from an input buffer, and checks if a record + * in the list contains all the UUIDs. If it doesn't, the record is removed + * from the list, so the list contains only the records which has all the + * input UUIDs in them. + * + * @param buf Incoming buffer containing all the UUIDs to be matched + * @param matching_recs List of service records to use for storing matching + * records + * + * @return 0 for success, or relevant error code + */ +static uint16_t find_services(struct net_buf *buf, + struct bt_sdp_record **matching_recs) +{ + struct bt_sdp_data_elem data_elem; + struct bt_sdp_record *record; + uint32_t uuid_list_size; + uint16_t res; + uint8_t att_idx, rec_idx = 0U; + bool found; + union { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_32 u32; + struct bt_uuid_128 u128; + } u; + + res = parse_data_elem(buf, &data_elem); + if (res) { + return res; + } + + if (((data_elem.type & BT_SDP_TYPE_DESC_MASK) != BT_SDP_SEQ_UNSPEC) && + ((data_elem.type & BT_SDP_TYPE_DESC_MASK) != BT_SDP_ALT_UNSPEC)) { + BT_WARN("Invalid type %x in service search pattern", + data_elem.type); + return BT_SDP_INVALID_SYNTAX; + } + + uuid_list_size = data_elem.data_size; + + bt_sdp_foreach_svc(insert_record, matching_recs); + + /* Go over the sequence of UUIDs, and match one UUID at a time */ + while (uuid_list_size) { + res = parse_data_elem(buf, &data_elem); + if (res) { + return res; + } + + if ((data_elem.type & BT_SDP_TYPE_DESC_MASK) != + BT_SDP_UUID_UNSPEC) { + BT_WARN("Invalid type %u in service search pattern", + data_elem.type); + return BT_SDP_INVALID_SYNTAX; + } + + if (buf->len < data_elem.data_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + if (data_elem.data_size == 2U) { + u.uuid.type = BT_UUID_TYPE_16; + u.u16.val = net_buf_pull_be16(buf); + } else if (data_elem.data_size == 4U) { + u.uuid.type = BT_UUID_TYPE_32; + u.u32.val = net_buf_pull_be32(buf); + } else if (data_elem.data_size == 16U) { + u.uuid.type = BT_UUID_TYPE_128; + sys_memcpy_swap(u.u128.val, buf->data, + data_elem.data_size); + net_buf_pull(buf, data_elem.data_size); + } else { + BT_WARN("Invalid UUID len %u in service search pattern", + data_elem.data_size); + net_buf_pull(buf, data_elem.data_size); + } + + uuid_list_size -= data_elem.total_size; + + /* Go over the list of services, and look for a service which + * doesn't have this UUID + */ + for (rec_idx = 0U; rec_idx < num_services; rec_idx++) { + record = matching_recs[rec_idx]; + + if (!record) { + continue; + } + + found = false; + + /* Search for the UUID in all the attrs of the svc */ + for (att_idx = 0U; att_idx < record->attr_count; + att_idx++) { + search_uuid(&record->attrs[att_idx].val, + &u.uuid, &found, 1); + if (found) { + break; + } + } + + /* Remove the record from the list if it doesn't have + * the UUID + */ + if (!found) { + matching_recs[rec_idx] = NULL; + } + } + } + + return 0; +} + +/* @brief Handler for Service Search Request + * + * Parses, processes and responds to a Service Search Request + * + * @param sdp Pointer to the SDP structure + * @param buf Request net buf + * @param tid Transaction ID + * + * @return 0 for success, or relevant error code + */ +static uint16_t sdp_svc_search_req(struct bt_sdp *sdp, struct net_buf *buf, + uint16_t tid) +{ + struct bt_sdp_svc_rsp *rsp; + struct net_buf *resp_buf; + struct bt_sdp_record *record; + struct bt_sdp_record *matching_recs[BT_SDP_MAX_SERVICES]; + uint16_t max_rec_count, total_recs = 0U, current_recs = 0U, res; + uint8_t cont_state_size, cont_state = 0U, idx = 0U, count = 0U; + bool pkt_full = false; + + res = find_services(buf, matching_recs); + if (res) { + /* Error in parsing */ + return res; + } + + if (buf->len < 3) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + max_rec_count = net_buf_pull_be16(buf); + cont_state_size = net_buf_pull_u8(buf); + + /* Zero out the matching services beyond max_rec_count */ + for (idx = 0U; idx < num_services; idx++) { + if (count == max_rec_count) { + matching_recs[idx] = NULL; + continue; + } + + if (matching_recs[idx]) { + count++; + } + } + + /* We send out only SDP_SS_CONT_STATE_SIZE bytes continuation state in + * responses, so expect only SDP_SS_CONT_STATE_SIZE bytes in requests + */ + if (cont_state_size) { + if (cont_state_size != SDP_SS_CONT_STATE_SIZE) { + BT_WARN("Invalid cont state size %u", cont_state_size); + return BT_SDP_INVALID_CSTATE; + } + + if (buf->len < cont_state_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + cont_state = net_buf_pull_u8(buf); + /* We include total_recs in the continuation state. We calculate + * it once and preserve it across all the partial responses + */ + total_recs = net_buf_pull_be16(buf); + } + + BT_DBG("max_rec_count %u, cont_state %u", max_rec_count, cont_state); + + resp_buf = bt_sdp_create_pdu(); + rsp = net_buf_add(resp_buf, sizeof(*rsp)); + + for (; cont_state < num_services; cont_state++) { + record = matching_recs[cont_state]; + + if (!record) { + continue; + } + + /* Calculate total recs only if it is first packet */ + if (!cont_state_size) { + total_recs++; + } + + if (pkt_full) { + continue; + } + + /* 4 bytes per Service Record Handle */ + /* 4 bytes for ContinuationState */ + if ((MIN(SDP_MTU, sdp->chan.tx.mtu) - resp_buf->len) < + (4 + 4 + sizeof(struct bt_sdp_hdr))) { + pkt_full = true; + } + + if (pkt_full) { + /* Packet exhausted: Add continuation state and break */ + BT_DBG("Packet full, num_services_covered %u", + cont_state); + net_buf_add_u8(resp_buf, SDP_SS_CONT_STATE_SIZE); + net_buf_add_u8(resp_buf, cont_state); + + /* If it is the first packet of a partial response, + * continue dry-running to calculate total_recs. + * Else break + */ + if (cont_state_size) { + break; + } + + continue; + } + + /* Add the service record handle to the packet */ + net_buf_add_be32(resp_buf, record->handle); + current_recs++; + } + + /* Add 0 continuation state if packet is exhausted */ + if (!pkt_full) { + net_buf_add_u8(resp_buf, 0); + } else { + net_buf_add_be16(resp_buf, total_recs); + } + + rsp->total_recs = sys_cpu_to_be16(total_recs); + rsp->current_recs = sys_cpu_to_be16(current_recs); + + BT_DBG("Sending response, len %u", resp_buf->len); + bt_sdp_send(&sdp->chan.chan, resp_buf, BT_SDP_SVC_SEARCH_RSP, tid); + + return 0; +} + +/* @brief Copies an attribute into an outgoing buffer + * + * Copies an attribute into a buffer. Recursively calls itself for complex + * attributes. + * + * @param elem Attribute to be copied to the buffer + * @param buf Buffer where the attribute is to be copied + * + * @return Size of the last data element that has been searched + * (used in recursion) + */ +static uint32_t copy_attribute(struct bt_sdp_data_elem *elem, + struct net_buf *buf, uint8_t nest_level) +{ + const uint8_t *cur_elem; + uint32_t size, seq_size, total_size; + + /* Limit recursion depth to avoid stack overflows */ + if (nest_level == SDP_DATA_ELEM_NEST_LEVEL_MAX) { + return 0; + } + + seq_size = elem->data_size; + total_size = elem->total_size; + cur_elem = elem->data; + + /* Copy the header */ + net_buf_add_u8(buf, elem->type); + + switch (total_size - (seq_size + 1U)) { + case 1: + net_buf_add_u8(buf, elem->data_size); + break; + case 2: + net_buf_add_be16(buf, elem->data_size); + break; + case 4: + net_buf_add_be32(buf, elem->data_size); + break; + } + + /* Recursively parse (till the last element is not another data element) + * and then fill the elements + */ + if ((elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_SEQ_UNSPEC || + (elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_ALT_UNSPEC) { + do { + size = copy_attribute((struct bt_sdp_data_elem *) + cur_elem, buf, nest_level + 1); + cur_elem += sizeof(struct bt_sdp_data_elem); + seq_size -= size; + } while (seq_size); + } else if ((elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_UINT8 || + (elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_INT8 || + (elem->type & BT_SDP_TYPE_DESC_MASK) == BT_SDP_UUID_UNSPEC) { + if (seq_size == 1U) { + net_buf_add_u8(buf, *((uint8_t *)elem->data)); + } else if (seq_size == 2U) { + net_buf_add_be16(buf, *((uint16_t *)elem->data)); + } else if (seq_size == 4U) { + net_buf_add_be32(buf, *((uint32_t *)elem->data)); + } else { + /* TODO: Convert 32bit and 128bit values to big-endian*/ + net_buf_add_mem(buf, elem->data, seq_size); + } + } else { + net_buf_add_mem(buf, elem->data, seq_size); + } + + return total_size; +} + +/* @brief SDP attribute iterator. + * + * Iterate over attributes of a service record from a starting index. + * + * @param record Service record whose attributes are to be iterated over. + * @param idx Index in the attribute list from where to start. + * @param func Callback function. + * @param user_data Data to pass to the callback. + * + * @return Index of the attribute where the iterator stopped + */ +static uint8_t bt_sdp_foreach_attr(struct bt_sdp_record *record, uint8_t idx, + bt_sdp_attr_func_t func, void *user_data) +{ + for (; idx < record->attr_count; idx++) { + if (func(&record->attrs[idx], idx, user_data) == + BT_SDP_ITER_STOP) { + break; + } + } + + return idx; +} + +/* @brief Check if an attribute matches a range, and include it in the response + * + * Checks if an attribute matches a given attribute ID or range, and if so, + * includes it in the response packet + * + * @param attr The current attribute + * @param att_idx Index of the current attribute in the database + * @param user_data Pointer to the structure containing response packet, byte + * count, states, etc + * + * @return BT_SDP_ITER_CONTINUE if should continue to the next attribute + * or BT_SDP_ITER_STOP to stop. + */ +static uint8_t select_attrs(struct bt_sdp_attribute *attr, uint8_t att_idx, + void *user_data) +{ + struct select_attrs_data *sad = user_data; + uint16_t att_id_lower, att_id_upper, att_id_cur, space; + uint32_t attr_size, seq_size; + uint8_t idx_filter; + + for (idx_filter = 0U; idx_filter < sad->num_filters; idx_filter++) { + + att_id_lower = (sad->filter[idx_filter] >> 16); + att_id_upper = (sad->filter[idx_filter]); + att_id_cur = attr->id; + + /* Check for range values */ + if (att_id_lower != 0xffff && + (!IN_RANGE(att_id_cur, att_id_lower, att_id_upper))) { + continue; + } + + /* Check for match values */ + if (att_id_lower == 0xffff && att_id_cur != att_id_upper) { + continue; + } + + /* Attribute ID matches */ + + /* 3 bytes for Attribute ID */ + attr_size = 3 + attr->val.total_size; + + /* If this is the first attribute of the service, then we need + * to account for the space required to add the per-service + * data element sequence header as well. + */ + if ((sad->state->current_svc != sad->rec->index) && + sad->new_service) { + /* 3 bytes for Per-Service Data Elem Seq declaration */ + seq_size = attr_size + 3; + } else { + seq_size = attr_size; + } + + if (sad->rsp_buf) { + space = MIN(SDP_MTU, sad->sdp->chan.tx.mtu) - + sad->rsp_buf->len - sizeof(struct bt_sdp_hdr); + + if ((!sad->state->pkt_full) && + ((seq_size > sad->max_att_len) || + (space < seq_size + sad->cont_state_size))) { + /* Packet exhausted */ + sad->state->pkt_full = true; + } + } + + /* Keep filling data only if packet is not exhausted */ + if (!sad->state->pkt_full && sad->rsp_buf) { + /* Add Per-Service Data Element Seq declaration once + * only when we are starting from the first attribute + */ + if (!sad->seq && + (sad->state->current_svc != sad->rec->index)) { + sad->seq = net_buf_add(sad->rsp_buf, + sizeof(*sad->seq)); + sad->seq->type = BT_SDP_SEQ16; + sad->seq->size = 0U; + } + + /* Add attribute ID */ + net_buf_add_u8(sad->rsp_buf, BT_SDP_UINT16); + net_buf_add_be16(sad->rsp_buf, att_id_cur); + + /* Add attribute value */ + copy_attribute(&attr->val, sad->rsp_buf, 1); + + sad->max_att_len -= seq_size; + sad->att_list_len += seq_size; + sad->state->last_att = att_idx; + sad->state->current_svc = sad->rec->index; + } + + if (sad->seq) { + /* Keep adding the sequence size if this packet contains + * the Per-Service Data Element Seq declaration header + */ + sad->seq->size += attr_size; + sad->state->att_list_size += seq_size; + } else { + /* Keep adding the total attr lists size if: + * It's a dry-run, calculating the total attr lists size + */ + sad->state->att_list_size += seq_size; + } + + sad->new_service = false; + break; + } + + /* End the search if: + * 1. We have exhausted the packet + * AND + * 2. This packet doesn't contain the service element declaration header + * AND + * 3. This is not a dry-run (then we look for other attrs that match) + */ + if (sad->state->pkt_full && !sad->seq && sad->rsp_buf) { + return BT_SDP_ITER_STOP; + } + + return BT_SDP_ITER_CONTINUE; +} + +/* @brief Creates attribute list in the given buffer + * + * Populates the attribute list of a service record in the buffer. To be used + * for responding to Service Attribute and Service Search Attribute requests + * + * @param sdp Pointer to the SDP structure + * @param record Service record whose attributes are to be included in the + * response + * @param filter Attribute values/ranges to be used as a filter + * @param num_filters Number of elements in the attribute filter + * @param max_att_len Maximum size of attributes to be included in the response + * @param cont_state_size No. of additional continuation state bytes to keep + * space for in the packet. This will vary based on the type of the request + * @param next_att Starting position of the search in the service's attr list + * @param state State of the overall search + * @param rsp_buf Response buffer which is filled in + * + * @return len Length of the attribute list created + */ +static uint16_t create_attr_list(struct bt_sdp *sdp, struct bt_sdp_record *record, + uint32_t *filter, uint8_t num_filters, + uint16_t max_att_len, uint8_t cont_state_size, + uint8_t next_att, struct search_state *state, + struct net_buf *rsp_buf) +{ + struct select_attrs_data sad; + uint8_t idx_att; + + sad.num_filters = num_filters; + sad.rec = record; + sad.rsp_buf = rsp_buf; + sad.sdp = sdp; + sad.max_att_len = max_att_len; + sad.cont_state_size = cont_state_size; + sad.seq = NULL; + sad.filter = filter; + sad.state = state; + sad.att_list_len = 0U; + sad.new_service = true; + + idx_att = bt_sdp_foreach_attr(sad.rec, next_att, select_attrs, &sad); + + if (sad.seq) { + sad.seq->size = sys_cpu_to_be16(sad.seq->size); + } + + return sad.att_list_len; +} + +/* @brief Extracts the attribute search list from a buffer + * + * Parses a buffer to extract the attribute search list (list of attribute IDs + * and ranges) which are to be used to filter attributes. + * + * @param buf Buffer to be parsed for extracting the attribute search list + * @param filter Empty list of 4byte filters that are filled in. For attribute + * IDs, the lower 2 bytes contain the ID and the upper 2 bytes are set to + * 0xFFFF. For attribute ranges, the lower 2bytes indicate the start ID and + * the upper 2bytes indicate the end ID + * @param num_filters No. of filter elements filled in (to be returned) + * + * @return 0 for success, or relevant error code + */ +static uint16_t get_att_search_list(struct net_buf *buf, uint32_t *filter, + uint8_t *num_filters) +{ + struct bt_sdp_data_elem data_elem; + uint16_t res; + uint32_t size; + + *num_filters = 0U; + res = parse_data_elem(buf, &data_elem); + if (res) { + return res; + } + + size = data_elem.data_size; + + while (size) { + res = parse_data_elem(buf, &data_elem); + if (res) { + return res; + } + + if ((data_elem.type & BT_SDP_TYPE_DESC_MASK) != BT_SDP_UINT8) { + BT_WARN("Invalid type %u in attribute ID list", + data_elem.type); + return BT_SDP_INVALID_SYNTAX; + } + + if (buf->len < data_elem.data_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + /* This is an attribute ID */ + if (data_elem.data_size == 2U) { + filter[(*num_filters)++] = 0xffff0000 | + net_buf_pull_be16(buf); + } + + /* This is an attribute ID range */ + if (data_elem.data_size == 4U) { + filter[(*num_filters)++] = net_buf_pull_be32(buf); + } + + size -= data_elem.total_size; + } + + return 0; +} + +/* @brief Check if a given handle matches that of the current service + * + * Checks if a given handle matches that of the current service + * + * @param rec The current service record + * @param user_data Pointer to the service record handle to be matched + * + * @return BT_SDP_ITER_CONTINUE if should continue to the next record + * or BT_SDP_ITER_STOP to stop. + */ +static uint8_t find_handle(struct bt_sdp_record *rec, void *user_data) +{ + uint32_t *svc_rec_hdl = user_data; + + if (rec->handle == *svc_rec_hdl) { + return BT_SDP_ITER_STOP; + } + + return BT_SDP_ITER_CONTINUE; +} + +/* @brief Handler for Service Attribute Request + * + * Parses, processes and responds to a Service Attribute Request + * + * @param sdp Pointer to the SDP structure + * @param buf Request buffer + * @param tid Transaction ID + * + * @return 0 for success, or relevant error code + */ +static uint16_t sdp_svc_att_req(struct bt_sdp *sdp, struct net_buf *buf, + uint16_t tid) +{ + uint32_t filter[MAX_NUM_ATT_ID_FILTER]; + struct search_state state = { + .current_svc = SDP_INVALID, + .last_att = SDP_INVALID, + .pkt_full = false + }; + struct bt_sdp_record *record; + struct bt_sdp_att_rsp *rsp; + struct net_buf *rsp_buf; + uint32_t svc_rec_hdl; + uint16_t max_att_len, res, att_list_len; + uint8_t num_filters, cont_state_size, next_att = 0U; + + if (buf->len < 6) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + svc_rec_hdl = net_buf_pull_be32(buf); + max_att_len = net_buf_pull_be16(buf); + + /* Set up the filters */ + res = get_att_search_list(buf, filter, &num_filters); + if (res) { + /* Error in parsing */ + return res; + } + + if (buf->len < 1) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + cont_state_size = net_buf_pull_u8(buf); + + /* We only send out 1 byte continuation state in responses, + * so expect only 1 byte in requests + */ + if (cont_state_size) { + if (cont_state_size != SDP_SA_CONT_STATE_SIZE) { + BT_WARN("Invalid cont state size %u", cont_state_size); + return BT_SDP_INVALID_CSTATE; + } + + if (buf->len < cont_state_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + state.last_att = net_buf_pull_u8(buf) + 1; + next_att = state.last_att; + } + + BT_DBG("svc_rec_hdl %u, max_att_len 0x%04x, cont_state %u", svc_rec_hdl, + max_att_len, next_att); + + /* Find the service */ + record = bt_sdp_foreach_svc(find_handle, &svc_rec_hdl); + + if (!record) { + BT_WARN("Handle %u not found", svc_rec_hdl); + return BT_SDP_INVALID_RECORD_HANDLE; + } + + /* For partial responses, restore the search state */ + if (cont_state_size) { + state.current_svc = record->index; + } + + rsp_buf = bt_sdp_create_pdu(); + rsp = net_buf_add(rsp_buf, sizeof(*rsp)); + + /* cont_state_size should include 1 byte header */ + att_list_len = create_attr_list(sdp, record, filter, num_filters, + max_att_len, SDP_SA_CONT_STATE_SIZE + 1, + next_att, &state, rsp_buf); + + if (!att_list_len) { + /* For empty responses, add an empty data element sequence */ + net_buf_add_u8(rsp_buf, BT_SDP_SEQ8); + net_buf_add_u8(rsp_buf, 0); + att_list_len = 2U; + } + + /* Add continuation state */ + if (state.pkt_full) { + BT_DBG("Packet full, state.last_att %u", state.last_att); + net_buf_add_u8(rsp_buf, 1); + net_buf_add_u8(rsp_buf, state.last_att); + } else { + net_buf_add_u8(rsp_buf, 0); + } + + rsp->att_list_len = sys_cpu_to_be16(att_list_len); + + BT_DBG("Sending response, len %u", rsp_buf->len); + bt_sdp_send(&sdp->chan.chan, rsp_buf, BT_SDP_SVC_ATTR_RSP, tid); + + return 0; +} + +/* @brief Handler for Service Search Attribute Request + * + * Parses, processes and responds to a Service Search Attribute Request + * + * @param sdp Pointer to the SDP structure + * @param buf Request buffer + * @param tid Transaction ID + * + * @return 0 for success, or relevant error code + */ +static uint16_t sdp_svc_search_att_req(struct bt_sdp *sdp, struct net_buf *buf, + uint16_t tid) +{ + uint32_t filter[MAX_NUM_ATT_ID_FILTER]; + struct bt_sdp_record *matching_recs[BT_SDP_MAX_SERVICES]; + struct search_state state = { + .att_list_size = 0, + .current_svc = SDP_INVALID, + .last_att = SDP_INVALID, + .pkt_full = false + }; + struct net_buf *rsp_buf, *rsp_buf_cpy; + struct bt_sdp_record *record; + struct bt_sdp_att_rsp *rsp; + struct bt_sdp_data_elem_seq *seq = NULL; + uint16_t max_att_len, res, att_list_len = 0U; + uint8_t num_filters, cont_state_size, next_svc = 0U, next_att = 0U; + bool dry_run = false; + + res = find_services(buf, matching_recs); + if (res) { + return res; + } + + if (buf->len < 2) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + max_att_len = net_buf_pull_be16(buf); + + /* Set up the filters */ + res = get_att_search_list(buf, filter, &num_filters); + + if (res) { + /* Error in parsing */ + return res; + } + + if (buf->len < 1) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + cont_state_size = net_buf_pull_u8(buf); + + /* We only send out 2 bytes continuation state in responses, + * so expect only 2 bytes in requests + */ + if (cont_state_size) { + if (cont_state_size != SDP_SSA_CONT_STATE_SIZE) { + BT_WARN("Invalid cont state size %u", cont_state_size); + return BT_SDP_INVALID_CSTATE; + } + + if (buf->len < cont_state_size) { + BT_WARN("Malformed packet"); + return BT_SDP_INVALID_SYNTAX; + } + + state.current_svc = net_buf_pull_u8(buf); + state.last_att = net_buf_pull_u8(buf) + 1; + next_svc = state.current_svc; + next_att = state.last_att; + } + + BT_DBG("max_att_len 0x%04x, state.current_svc %u, state.last_att %u", + max_att_len, state.current_svc, state.last_att); + + rsp_buf = bt_sdp_create_pdu(); + + rsp = net_buf_add(rsp_buf, sizeof(*rsp)); + + /* Add headers only if this is not a partial response */ + if (!cont_state_size) { + seq = net_buf_add(rsp_buf, sizeof(*seq)); + seq->type = BT_SDP_SEQ16; + seq->size = 0U; + + /* 3 bytes for Outer Data Element Sequence declaration */ + att_list_len = 3U; + } + + rsp_buf_cpy = rsp_buf; + + for (; next_svc < num_services; next_svc++) { + record = matching_recs[next_svc]; + + if (!record) { + continue; + } + + att_list_len += create_attr_list(sdp, record, filter, + num_filters, max_att_len, + SDP_SSA_CONT_STATE_SIZE + 1, + next_att, &state, rsp_buf_cpy); + + /* Check if packet is full and not dry run */ + if (state.pkt_full && !dry_run) { + BT_DBG("Packet full, state.last_att %u", + state.last_att); + dry_run = true; + + /* Add continuation state */ + net_buf_add_u8(rsp_buf, 2); + net_buf_add_u8(rsp_buf, state.current_svc); + net_buf_add_u8(rsp_buf, state.last_att); + + /* Break if it's not a partial response, else dry-run + * Dry run: Look for other services that match + */ + if (cont_state_size) { + break; + } + + rsp_buf_cpy = NULL; + } + + next_att = 0U; + } + + if (!dry_run) { + if (!att_list_len) { + /* For empty responses, add an empty data elem seq */ + net_buf_add_u8(rsp_buf, BT_SDP_SEQ8); + net_buf_add_u8(rsp_buf, 0); + att_list_len = 2U; + } + /* Search exhausted */ + net_buf_add_u8(rsp_buf, 0); + } + + rsp->att_list_len = sys_cpu_to_be16(att_list_len); + if (seq) { + seq->size = sys_cpu_to_be16(state.att_list_size); + } + + BT_DBG("Sending response, len %u", rsp_buf->len); + bt_sdp_send(&sdp->chan.chan, rsp_buf, BT_SDP_SVC_SEARCH_ATTR_RSP, + tid); + + return 0; +} + +static const struct { + uint8_t op_code; + uint16_t (*func)(struct bt_sdp *sdp, struct net_buf *buf, uint16_t tid); +} handlers[] = { + { BT_SDP_SVC_SEARCH_REQ, sdp_svc_search_req }, + { BT_SDP_SVC_ATTR_REQ, sdp_svc_att_req }, + { BT_SDP_SVC_SEARCH_ATTR_REQ, sdp_svc_search_att_req }, +}; + +/* @brief Callback for SDP data receive + * + * Gets called when an SDP PDU is received. Calls the corresponding handler + * based on the op code of the PDU. + * + * @param chan L2CAP channel + * @param buf Received PDU + * + * @return None + */ +static int bt_sdp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_l2cap_br_chan *ch = CONTAINER_OF(chan, + struct bt_l2cap_br_chan, chan); + struct bt_sdp *sdp = CONTAINER_OF(ch, struct bt_sdp, chan); + struct bt_sdp_hdr *hdr; + uint16_t err = BT_SDP_INVALID_SYNTAX; + size_t i; + + BT_DBG("chan %p, ch %p, cid 0x%04x", chan, ch, ch->tx.cid); + + BT_ASSERT(sdp); + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small SDP PDU received"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_DBG("Received SDP code 0x%02x len %u", hdr->op_code, buf->len); + + if (sys_cpu_to_be16(hdr->param_len) != buf->len) { + err = BT_SDP_INVALID_PDU_SIZE; + } else { + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (hdr->op_code != handlers[i].op_code) { + continue; + } + + err = handlers[i].func(sdp, buf, hdr->tid); + break; + } + } + + if (err) { + BT_WARN("SDP error 0x%02x", err); + send_err_rsp(chan, err, hdr->tid); + } + + return 0; +} + +/* @brief Callback for SDP connection accept + * + * Gets called when an incoming SDP connection needs to be authorized. + * Registers the L2CAP callbacks and allocates an SDP context to the connection + * + * @param conn BT connection object + * @param chan L2CAP channel structure (to be returned) + * + * @return 0 for success, or relevant error code + */ +static int bt_sdp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_sdp_connected, + .disconnected = bt_sdp_disconnected, + .recv = bt_sdp_recv, + }; + int i; + + BT_DBG("conn %p", conn); + + for (i = 0; i < ARRAY_SIZE(bt_sdp_pool); i++) { + struct bt_sdp *sdp = &bt_sdp_pool[i]; + + if (sdp->chan.chan.conn) { + continue; + } + + sdp->chan.chan.ops = &ops; + sdp->chan.rx.mtu = SDP_MTU; + + *chan = &sdp->chan.chan; + + return 0; + } + + BT_ERR("No available SDP context for conn %p", conn); + + return -ENOMEM; +} + +void bt_sdp_init(void) +{ +#if defined(BFLB_DYNAMIC_ALLOC_MEM) + k_lifo_init(&sdp_pool.free, CONFIG_BT_MAX_CONN); + net_buf_init(&sdp_pool, CONFIG_BT_MAX_CONN, BT_L2CAP_BUF_SIZE(SDP_MTU), NULL); +#endif + static struct bt_l2cap_server server = { + .psm = SDP_PSM, + .accept = bt_sdp_accept, + .sec_level = BT_SECURITY_L0, + }; + int res; + + res = bt_l2cap_br_server_register(&server); + if (res) { + BT_ERR("L2CAP server registration failed with error %d", res); + } +} + +int bt_sdp_register_service(struct bt_sdp_record *service) +{ + uint32_t handle = SDP_SERVICE_HANDLE_BASE; + + if (!service) { + BT_ERR("No service record specified"); + return 0; + } + + if (num_services == BT_SDP_MAX_SERVICES) { + BT_ERR("Reached max allowed registrations"); + return -ENOMEM; + } + + if (db) { + handle = db->handle + 1; + } + + service->next = db; + service->index = num_services++; + service->handle = handle; + *((uint32_t *)(service->attrs[0].val.data)) = handle; + db = service; + + BT_DBG("Service registered at %u", handle); + + return 0; +} + +#define GET_PARAM(__node) \ + CONTAINER_OF(__node, struct bt_sdp_discover_params, _node) + +/* ServiceSearchAttribute PDU, ref to BT Core 4.2, Vol 3, part B, 4.7.1 */ +static int sdp_client_ssa_search(struct bt_sdp_client *session) +{ + const struct bt_sdp_discover_params *param; + struct bt_sdp_hdr *hdr; + struct net_buf *buf; + + /* + * Select proper user params, if session->param is invalid it means + * getting new UUID from top of to be resolved params list. Otherwise + * the context is in a middle of partial SDP PDU responses and cached + * value from context can be used. + */ + if (!session->param) { + param = GET_PARAM(sys_slist_peek_head(&session->reqs)); + } else { + param = session->param; + } + + if (!param) { + BT_WARN("No UUIDs to be resolved on remote"); + return -EINVAL; + } + + buf = bt_l2cap_create_pdu(&sdp_pool, 0); + + hdr = net_buf_add(buf, sizeof(*hdr)); + + hdr->op_code = BT_SDP_SVC_SEARCH_ATTR_REQ; + /* BT_SDP_SEQ8 means length of sequence is on additional next byte */ + net_buf_add_u8(buf, BT_SDP_SEQ8); + + switch (param->uuid->type) { + case BT_UUID_TYPE_16: + /* Seq length */ + net_buf_add_u8(buf, 0x03); + /* Seq type */ + net_buf_add_u8(buf, BT_SDP_UUID16); + /* Seq value */ + net_buf_add_be16(buf, BT_UUID_16(param->uuid)->val); + break; + case BT_UUID_TYPE_32: + net_buf_add_u8(buf, 0x05); + net_buf_add_u8(buf, BT_SDP_UUID32); + net_buf_add_be32(buf, BT_UUID_32(param->uuid)->val); + break; + case BT_UUID_TYPE_128: + net_buf_add_u8(buf, 0x11); + net_buf_add_u8(buf, BT_SDP_UUID128); + net_buf_add_mem(buf, BT_UUID_128(param->uuid)->val, + ARRAY_SIZE(BT_UUID_128(param->uuid)->val)); + break; + default: + BT_ERR("Unknown UUID type %u", param->uuid->type); + return -EINVAL; + } + + /* Set attribute max bytes count to be returned from server */ + net_buf_add_be16(buf, BT_SDP_MAX_ATTR_LEN); + /* + * Sequence definition where data is sequence of elements and where + * additional next byte points the size of elements within + */ + net_buf_add_u8(buf, BT_SDP_SEQ8); + net_buf_add_u8(buf, 0x05); + /* Data element definition for two following 16bits range elements */ + net_buf_add_u8(buf, BT_SDP_UINT32); + /* Get all attributes. It enables filter out wanted only attributes */ + net_buf_add_be16(buf, 0x0000); + net_buf_add_be16(buf, 0xffff); + + /* + * Update and validate PDU ContinuationState. Initial SSA Request has + * zero length continuation state since no interaction has place with + * server so far, otherwise use the original state taken from remote's + * last response PDU that is cached by SDP client context. + */ + if (session->cstate.length == 0U) { + net_buf_add_u8(buf, 0x00); + } else { + net_buf_add_u8(buf, session->cstate.length); + net_buf_add_mem(buf, session->cstate.data, + session->cstate.length); + } + + /* set overall PDU length */ + hdr->param_len = sys_cpu_to_be16(buf->len - sizeof(*hdr)); + + /* Update context param to the one being resolving now */ + session->param = param; + session->tid++; + hdr->tid = sys_cpu_to_be16(session->tid); + + return bt_l2cap_chan_send(&session->chan.chan, buf); +} + +static void sdp_client_params_iterator(struct bt_sdp_client *session) +{ + struct bt_l2cap_chan *chan = &session->chan.chan; + struct bt_sdp_discover_params *param, *tmp; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&session->reqs, param, tmp, _node) { + if (param != session->param) { + continue; + } + + BT_DBG(""); + + /* Remove already checked UUID node */ + sys_slist_remove(&session->reqs, NULL, ¶m->_node); + /* Invalidate cached param in context */ + session->param = NULL; + /* Reset continuation state in current context */ + (void)memset(&session->cstate, 0, sizeof(session->cstate)); + + /* Check if there's valid next UUID */ + if (!sys_slist_is_empty(&session->reqs)) { + sdp_client_ssa_search(session); + return; + } + + /* No UUID items, disconnect channel */ + bt_l2cap_chan_disconnect(chan); + break; + } +} + +static uint16_t sdp_client_get_total(struct bt_sdp_client *session, + struct net_buf *buf, uint16_t *total) +{ + uint16_t pulled; + uint8_t seq; + + /* + * Pull value of total octets of all attributes available to be + * collected when response gets completed for given UUID. Such info can + * be get from the very first response frame after initial SSA request + * was sent. For subsequent calls related to the same SSA request input + * buf and in/out function parameters stays neutral. + */ + if (session->cstate.length == 0U) { + seq = net_buf_pull_u8(buf); + pulled = 1U; + switch (seq) { + case BT_SDP_SEQ8: + *total = net_buf_pull_u8(buf); + pulled += 1U; + break; + case BT_SDP_SEQ16: + *total = net_buf_pull_be16(buf); + pulled += 2U; + break; + default: + BT_WARN("Sequence type 0x%02x not handled", seq); + *total = 0U; + break; + } + + BT_DBG("Total %u octets of all attributes", *total); + } else { + pulled = 0U; + *total = 0U; + } + + return pulled; +} + +static uint16_t get_record_len(struct net_buf *buf) +{ + uint16_t len; + uint8_t seq; + + seq = net_buf_pull_u8(buf); + + switch (seq) { + case BT_SDP_SEQ8: + len = net_buf_pull_u8(buf); + break; + case BT_SDP_SEQ16: + len = net_buf_pull_be16(buf); + break; + default: + BT_WARN("Sequence type 0x%02x not handled", seq); + len = 0U; + break; + } + + BT_DBG("Record len %u", len); + + return len; +} + +enum uuid_state { + UUID_NOT_RESOLVED, + UUID_RESOLVED, +}; + +static void sdp_client_notify_result(struct bt_sdp_client *session, + enum uuid_state state) +{ + struct bt_conn *conn = session->chan.chan.conn; + struct bt_sdp_client_result result; + uint16_t rec_len; + uint8_t user_ret; + + result.uuid = session->param->uuid; + + if (state == UUID_NOT_RESOLVED) { + result.resp_buf = NULL; + result.next_record_hint = false; + session->param->func(conn, &result); + return; + } + + while (session->rec_buf->len) { + struct net_buf_simple_state buf_state; + + rec_len = get_record_len(session->rec_buf); + /* tell the user about multi record resolution */ + if (session->rec_buf->len > rec_len) { + result.next_record_hint = true; + } else { + result.next_record_hint = false; + } + + /* save the original session buffer */ + net_buf_simple_save(&session->rec_buf->b, &buf_state); + /* initialize internal result buffer instead of memcpy */ + result.resp_buf = session->rec_buf; + /* + * Set user internal result buffer length as same as record + * length to fake user. User will see the individual record + * length as rec_len insted of whole session rec_buf length. + */ + result.resp_buf->len = rec_len; + + user_ret = session->param->func(conn, &result); + + /* restore original session buffer */ + net_buf_simple_restore(&session->rec_buf->b, &buf_state); + /* + * sync session buffer data length with next record chunk not + * send to user so far + */ + net_buf_pull(session->rec_buf, rec_len); + if (user_ret == BT_SDP_DISCOVER_UUID_STOP) { + break; + } + } +} + +static int sdp_client_receive(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); + struct bt_sdp_hdr *hdr; + struct bt_sdp_pdu_cstate *cstate; + uint16_t len, tid, frame_len; + uint16_t total; + + BT_DBG("session %p buf %p", session, buf); + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small SDP PDU"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + if (hdr->op_code == BT_SDP_ERROR_RSP) { + BT_INFO("Error SDP PDU response"); + return 0; + } + + len = sys_be16_to_cpu(hdr->param_len); + tid = sys_be16_to_cpu(hdr->tid); + + BT_DBG("SDP PDU tid %u len %u", tid, len); + + if (buf->len != len) { + BT_ERR("SDP PDU length mismatch (%u != %u)", buf->len, len); + return 0; + } + + if (tid != session->tid) { + BT_ERR("Mismatch transaction ID value in SDP PDU"); + return 0; + } + + switch (hdr->op_code) { + case BT_SDP_SVC_SEARCH_ATTR_RSP: + /* Get number of attributes in this frame. */ + frame_len = net_buf_pull_be16(buf); + /* Check valid buf len for attribute list and cont state */ + if (buf->len < frame_len + SDP_CONT_STATE_LEN_SIZE) { + BT_ERR("Invalid frame payload length"); + return 0; + } + /* Check valid range of attributes length */ + if (frame_len < 2) { + BT_ERR("Invalid attributes data length"); + return 0; + } + + /* Get PDU continuation state */ + cstate = (struct bt_sdp_pdu_cstate *)(buf->data + frame_len); + + if (cstate->length > BT_SDP_MAX_PDU_CSTATE_LEN) { + BT_ERR("Invalid SDP PDU Continuation State length %u", + cstate->length); + return 0; + } + + if ((frame_len + SDP_CONT_STATE_LEN_SIZE + cstate->length) > + buf->len) { + BT_ERR("Invalid frame payload length"); + return 0; + } + + /* + * No record found for given UUID. The check catches case when + * current response frame has Continuation State shortest and + * valid and this is the first response frame as well. + */ + if (frame_len == 2U && cstate->length == 0U && + session->cstate.length == 0U) { + BT_DBG("record for UUID 0x%s not found", + bt_uuid_str(session->param->uuid)); + /* Call user UUID handler */ + sdp_client_notify_result(session, UUID_NOT_RESOLVED); + net_buf_pull(buf, frame_len + sizeof(cstate->length)); + goto iterate; + } + + /* Get total value of all attributes to be collected */ + frame_len -= sdp_client_get_total(session, buf, &total); + + if (total > net_buf_tailroom(session->rec_buf)) { + BT_WARN("Not enough room for getting records data"); + goto iterate; + } + + net_buf_add_mem(session->rec_buf, buf->data, frame_len); + net_buf_pull(buf, frame_len); + + /* + * check if current response says there's next portion to be + * fetched + */ + if (cstate->length) { + /* Cache original Continuation State in context */ + memcpy(&session->cstate, cstate, + sizeof(struct bt_sdp_pdu_cstate)); + + net_buf_pull(buf, cstate->length + + sizeof(cstate->length)); + + /* Request for next portion of attributes data */ + sdp_client_ssa_search(session); + break; + } + + net_buf_pull(buf, sizeof(cstate->length)); + + BT_DBG("UUID 0x%s resolved", bt_uuid_str(session->param->uuid)); + sdp_client_notify_result(session, UUID_RESOLVED); +iterate: + /* Get next UUID and start resolving it */ + sdp_client_params_iterator(session); + break; + default: + BT_DBG("PDU 0x%0x response not handled", hdr->op_code); + break; + } + + return 0; +} + +static int sdp_client_chan_connect(struct bt_sdp_client *session) +{ + return bt_l2cap_br_chan_connect(session->chan.chan.conn, + &session->chan.chan, SDP_PSM); +} + +static struct net_buf *sdp_client_alloc_buf(struct bt_l2cap_chan *chan) +{ + struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); + struct net_buf *buf; + + BT_DBG("session %p chan %p", session, chan); + + session->param = GET_PARAM(sys_slist_peek_head(&session->reqs)); + + buf = net_buf_alloc(session->param->pool, K_FOREVER); + __ASSERT_NO_MSG(buf); + + return buf; +} + +static void sdp_client_connected(struct bt_l2cap_chan *chan) +{ + struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); + + BT_DBG("session %p chan %p connected", session, chan); + + session->rec_buf = chan->ops->alloc_buf(chan); + + sdp_client_ssa_search(session); +} + +static void sdp_client_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_sdp_client *session = SDP_CLIENT_CHAN(chan); + + BT_DBG("session %p chan %p disconnected", session, chan); + + net_buf_unref(session->rec_buf); + + /* + * Reset session excluding L2CAP channel member. Let's the channel + * resets autonomous. + */ + (void)memset(&session->reqs, 0, + sizeof(*session) - sizeof(session->chan)); +} + +static const struct bt_l2cap_chan_ops sdp_client_chan_ops = { + .connected = sdp_client_connected, + .disconnected = sdp_client_disconnected, + .recv = sdp_client_receive, + .alloc_buf = sdp_client_alloc_buf, +}; + +static struct bt_sdp_client *sdp_client_new_session(struct bt_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_sdp_client_pool); i++) { + struct bt_sdp_client *session = &bt_sdp_client_pool[i]; + int err; + + if (session->chan.chan.conn) { + continue; + } + + sys_slist_init(&session->reqs); + + session->chan.chan.ops = &sdp_client_chan_ops; + session->chan.chan.conn = conn; + session->chan.rx.mtu = SDP_CLIENT_MTU; + + err = sdp_client_chan_connect(session); + if (err) { + (void)memset(session, 0, sizeof(*session)); + BT_ERR("Cannot connect %d", err); + return NULL; + } + + return session; + } + + BT_ERR("No available SDP client context"); + + return NULL; +} + +static struct bt_sdp_client *sdp_client_get_session(struct bt_conn *conn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bt_sdp_client_pool); i++) { + if (bt_sdp_client_pool[i].chan.chan.conn == conn) { + return &bt_sdp_client_pool[i]; + } + } + + /* + * Try to allocate session context since not found in pool and attempt + * connect to remote SDP endpoint. + */ + return sdp_client_new_session(conn); +} + +int bt_sdp_discover(struct bt_conn *conn, + const struct bt_sdp_discover_params *params) +{ + struct bt_sdp_client *session; + + if (!params || !params->uuid || !params->func || !params->pool) { + BT_WARN("Invalid user params"); + return -EINVAL; + } + + session = sdp_client_get_session(conn); + if (!session) { + return -ENOMEM; + } + + sys_slist_append(&session->reqs, (sys_snode_t *)¶ms->_node); + + return 0; +} + +/* Helper getting length of data determined by DTD for integers */ +static inline ssize_t sdp_get_int_len(const uint8_t *data, size_t len) +{ + BT_ASSERT(data); + + switch (data[0]) { + case BT_SDP_DATA_NIL: + return 1; + case BT_SDP_BOOL: + case BT_SDP_INT8: + case BT_SDP_UINT8: + if (len < 2) { + break; + } + + return 2; + case BT_SDP_INT16: + case BT_SDP_UINT16: + if (len < 3) { + break; + } + + return 3; + case BT_SDP_INT32: + case BT_SDP_UINT32: + if (len < 5) { + break; + } + + return 5; + case BT_SDP_INT64: + case BT_SDP_UINT64: + if (len < 9) { + break; + } + + return 9; + case BT_SDP_INT128: + case BT_SDP_UINT128: + default: + BT_ERR("Invalid/unhandled DTD 0x%02x", data[0]); + return -EINVAL; + } + + BT_ERR("Too short buffer length %zu", len); + return -EMSGSIZE; +} + +/* Helper getting length of data determined by DTD for UUID */ +static inline ssize_t sdp_get_uuid_len(const uint8_t *data, size_t len) +{ + BT_ASSERT(data); + + switch (data[0]) { + case BT_SDP_UUID16: + if (len < 3) { + break; + } + + return 3; + case BT_SDP_UUID32: + if (len < 5) { + break; + } + + return 5; + case BT_SDP_UUID128: + default: + BT_ERR("Invalid/unhandled DTD 0x%02x", data[0]); + return -EINVAL; + } + + BT_ERR("Too short buffer length %zu", len); + return -EMSGSIZE; +} + +/* Helper getting length of data determined by DTD for strings */ +static inline ssize_t sdp_get_str_len(const uint8_t *data, size_t len) +{ + const uint8_t *pnext; + + BT_ASSERT(data); + + /* validate len for pnext safe use to read next 8bit value */ + if (len < 2) { + goto err; + } + + pnext = data + sizeof(uint8_t); + + switch (data[0]) { + case BT_SDP_TEXT_STR8: + case BT_SDP_URL_STR8: + if (len < (2 + pnext[0])) { + break; + } + + return 2 + pnext[0]; + case BT_SDP_TEXT_STR16: + case BT_SDP_URL_STR16: + /* validate len for pnext safe use to read 16bit value */ + if (len < 3) { + break; + } + + if (len < (3 + sys_get_be16(pnext))) { + break; + } + + return 3 + sys_get_be16(pnext); + case BT_SDP_TEXT_STR32: + case BT_SDP_URL_STR32: + default: + BT_ERR("Invalid/unhandled DTD 0x%02x", data[0]); + return -EINVAL; + } +err: + BT_ERR("Too short buffer length %zu", len); + return -EMSGSIZE; +} + +/* Helper getting length of data determined by DTD for sequences */ +static inline ssize_t sdp_get_seq_len(const uint8_t *data, size_t len) +{ + const uint8_t *pnext; + + BT_ASSERT(data); + + /* validate len for pnext safe use to read 8bit bit value */ + if (len < 2) { + goto err; + } + + pnext = data + sizeof(uint8_t); + + switch (data[0]) { + case BT_SDP_SEQ8: + case BT_SDP_ALT8: + if (len < (2 + pnext[0])) { + break; + } + + return 2 + pnext[0]; + case BT_SDP_SEQ16: + case BT_SDP_ALT16: + /* validate len for pnext safe use to read 16bit value */ + if (len < 3) { + break; + } + + if (len < (3 + sys_get_be16(pnext))) { + break; + } + + return 3 + sys_get_be16(pnext); + case BT_SDP_SEQ32: + case BT_SDP_ALT32: + default: + BT_ERR("Invalid/unhandled DTD 0x%02x", data[0]); + return -EINVAL; + } +err: + BT_ERR("Too short buffer length %zu", len); + return -EMSGSIZE; +} + +/* Helper getting length of attribute value data */ +static ssize_t sdp_get_attr_value_len(const uint8_t *data, size_t len) +{ + BT_ASSERT(data); + + BT_DBG("Attr val DTD 0x%02x", data[0]); + + switch (data[0]) { + case BT_SDP_DATA_NIL: + case BT_SDP_BOOL: + case BT_SDP_UINT8: + case BT_SDP_UINT16: + case BT_SDP_UINT32: + case BT_SDP_UINT64: + case BT_SDP_UINT128: + case BT_SDP_INT8: + case BT_SDP_INT16: + case BT_SDP_INT32: + case BT_SDP_INT64: + case BT_SDP_INT128: + return sdp_get_int_len(data, len); + case BT_SDP_UUID16: + case BT_SDP_UUID32: + case BT_SDP_UUID128: + return sdp_get_uuid_len(data, len); + case BT_SDP_TEXT_STR8: + case BT_SDP_TEXT_STR16: + case BT_SDP_TEXT_STR32: + case BT_SDP_URL_STR8: + case BT_SDP_URL_STR16: + case BT_SDP_URL_STR32: + return sdp_get_str_len(data, len); + case BT_SDP_SEQ8: + case BT_SDP_SEQ16: + case BT_SDP_SEQ32: + case BT_SDP_ALT8: + case BT_SDP_ALT16: + case BT_SDP_ALT32: + return sdp_get_seq_len(data, len); + default: + BT_ERR("Unknown DTD 0x%02x", data[0]); + return -EINVAL; + } +} + +/* Type holding UUID item and related to it specific information. */ +struct bt_sdp_uuid_desc { + union { + struct bt_uuid uuid; + struct bt_uuid_16 uuid16; + struct bt_uuid_32 uuid32; + }; + uint16_t attr_id; + uint8_t *params; + uint16_t params_len; +}; + +/* Generic attribute item collector. */ +struct bt_sdp_attr_item { + /* Attribute identifier. */ + uint16_t attr_id; + /* Address of beginning attribute value taken from original buffer + * holding response from server. + */ + uint8_t *val; + /* Says about the length of attribute value. */ + uint16_t len; +}; + +static int bt_sdp_get_attr(const struct net_buf *buf, + struct bt_sdp_attr_item *attr, uint16_t attr_id) +{ + uint8_t *data; + uint16_t id; + + data = buf->data; + while (data - buf->data < buf->len) { + ssize_t dlen; + + /* data need to point to attribute id descriptor field (DTD)*/ + if (data[0] != BT_SDP_UINT16) { + BT_ERR("Invalid descriptor 0x%02x", data[0]); + return -EINVAL; + } + + data += sizeof(uint8_t); + id = sys_get_be16(data); + BT_DBG("Attribute ID 0x%04x", id); + data += sizeof(uint16_t); + + dlen = sdp_get_attr_value_len(data, + buf->len - (data - buf->data)); + if (dlen < 0) { + BT_ERR("Invalid attribute value data"); + return -EINVAL; + } + + if (id == attr_id) { + BT_DBG("Attribute ID 0x%04x Value found", id); + /* + * Initialize attribute value buffer data using selected + * data slice from original buffer. + */ + attr->val = data; + attr->len = dlen; + attr->attr_id = id; + return 0; + } + + data += dlen; + } + + return -ENOENT; +} + +/* reads SEQ item length, moves input buffer data reader forward */ +static ssize_t sdp_get_seq_len_item(uint8_t **data, size_t len) +{ + const uint8_t *pnext; + + BT_ASSERT(data); + BT_ASSERT(*data); + + /* validate len for pnext safe use to read 8bit bit value */ + if (len < 2) { + goto err; + } + + pnext = *data + sizeof(uint8_t); + + switch (*data[0]) { + case BT_SDP_SEQ8: + if (len < (2 + pnext[0])) { + break; + } + + *data += 2; + return pnext[0]; + case BT_SDP_SEQ16: + /* validate len for pnext safe use to read 16bit value */ + if (len < 3) { + break; + } + + if (len < (3 + sys_get_be16(pnext))) { + break; + } + + *data += 3; + return sys_get_be16(pnext); + case BT_SDP_SEQ32: + /* validate len for pnext safe use to read 32bit value */ + if (len < 5) { + break; + } + + if (len < (5 + sys_get_be32(pnext))) { + break; + } + + *data += 5; + return sys_get_be32(pnext); + default: + BT_ERR("Invalid/unhandled DTD 0x%02x", *data[0]); + return -EINVAL; + } +err: + BT_ERR("Too short buffer length %zu", len); + return -EMSGSIZE; +} + +static int sdp_get_uuid_data(const struct bt_sdp_attr_item *attr, + struct bt_sdp_uuid_desc *pd, + uint16_t proto_profile) +{ + /* get start address of attribute value */ + uint8_t *p = attr->val; + ssize_t slen; + + BT_ASSERT(p); + + /* Attribute value is a SEQ, get length of parent SEQ frame */ + slen = sdp_get_seq_len_item(&p, attr->len); + if (slen < 0) { + return slen; + } + + /* start reading stacked UUIDs in analyzed sequences tree */ + while (p - attr->val < attr->len) { + size_t to_end, left = 0; + + /* to_end tells how far to the end of input buffer */ + to_end = attr->len - (p - attr->val); + /* how long is current UUID's item data associated to */ + slen = sdp_get_seq_len_item(&p, to_end); + if (slen < 0) { + return slen; + } + + /* left tells how far is to the end of current UUID */ + left = slen; + + /* check if at least DTD + UUID16 can be read safely */ + if (left < 3) { + return -EMSGSIZE; + } + + /* check DTD and get stacked UUID value */ + switch (p[0]) { + case BT_SDP_UUID16: + memcpy(&pd->uuid16, + BT_UUID_DECLARE_16(sys_get_be16(++p)), + sizeof(struct bt_uuid_16)); + p += sizeof(uint16_t); + left -= sizeof(uint16_t); + break; + case BT_SDP_UUID32: + /* check if valid UUID32 can be read safely */ + if (left < 5) { + return -EMSGSIZE; + } + + memcpy(&pd->uuid32, + BT_UUID_DECLARE_32(sys_get_be32(++p)), + sizeof(struct bt_uuid_32)); + p += sizeof(uint32_t); + left -= sizeof(uint32_t); + break; + default: + BT_ERR("Invalid/unhandled DTD 0x%02x\n", p[0]); + return -EINVAL; + } + + /* include last DTD in p[0] size itself updating left */ + left -= sizeof(p[0]); + + /* + * Check if current UUID value matches input one given by user. + * If found save it's location and length and return. + */ + if ((proto_profile == BT_UUID_16(&pd->uuid)->val) || + (proto_profile == BT_UUID_32(&pd->uuid)->val)) { + pd->params = p; + pd->params_len = left; + + BT_DBG("UUID 0x%s found", bt_uuid_str(&pd->uuid)); + return 0; + } + + /* skip left octets to point beginning of next UUID in tree */ + p += left; + } + + BT_DBG("Value 0x%04x not found", proto_profile); + return -ENOENT; +} + +/* + * Helper extracting specific parameters associated with UUID node given in + * protocol descriptor list or profile descriptor list. + */ +static int sdp_get_param_item(struct bt_sdp_uuid_desc *pd_item, uint16_t *param) +{ + const uint8_t *p = pd_item->params; + bool len_err = false; + + BT_ASSERT(p); + + BT_DBG("Getting UUID's 0x%s params", bt_uuid_str(&pd_item->uuid)); + + switch (p[0]) { + case BT_SDP_UINT8: + /* check if 8bits value can be read safely */ + if (pd_item->params_len < 2) { + len_err = true; + break; + } + *param = (++p)[0]; + p += sizeof(uint8_t); + break; + case BT_SDP_UINT16: + /* check if 16bits value can be read safely */ + if (pd_item->params_len < 3) { + len_err = true; + break; + } + *param = sys_get_be16(++p); + p += sizeof(uint16_t); + break; + case BT_SDP_UINT32: + /* check if 32bits value can be read safely */ + if (pd_item->params_len < 5) { + len_err = true; + break; + } + *param = sys_get_be32(++p); + p += sizeof(uint32_t); + break; + default: + BT_ERR("Invalid/unhandled DTD 0x%02x\n", p[0]); + return -EINVAL; + } + /* + * Check if no more data than already read is associated with UUID. In + * valid case after getting parameter we should reach data buf end. + */ + if (p - pd_item->params != pd_item->params_len || len_err) { + BT_DBG("Invalid param buffer length"); + return -EMSGSIZE; + } + + return 0; +} + +int bt_sdp_get_proto_param(const struct net_buf *buf, enum bt_sdp_proto proto, + uint16_t *param) +{ + struct bt_sdp_attr_item attr; + struct bt_sdp_uuid_desc pd; + int res; + + if (proto != BT_SDP_PROTO_RFCOMM && proto != BT_SDP_PROTO_L2CAP) { + BT_ERR("Invalid protocol specifier"); + return -EINVAL; + } + + res = bt_sdp_get_attr(buf, &attr, BT_SDP_ATTR_PROTO_DESC_LIST); + if (res < 0) { + BT_WARN("Attribute 0x%04x not found, err %d", + BT_SDP_ATTR_PROTO_DESC_LIST, res); + return res; + } + + res = sdp_get_uuid_data(&attr, &pd, proto); + if (res < 0) { + BT_WARN("Protocol specifier 0x%04x not found, err %d", proto, + res); + return res; + } + + return sdp_get_param_item(&pd, param); +} + +int bt_sdp_get_profile_version(const struct net_buf *buf, uint16_t profile, + uint16_t *version) +{ + struct bt_sdp_attr_item attr; + struct bt_sdp_uuid_desc pd; + int res; + + res = bt_sdp_get_attr(buf, &attr, BT_SDP_ATTR_PROFILE_DESC_LIST); + if (res < 0) { + BT_WARN("Attribute 0x%04x not found, err %d", + BT_SDP_ATTR_PROFILE_DESC_LIST, res); + return res; + } + + res = sdp_get_uuid_data(&attr, &pd, profile); + if (res < 0) { + BT_WARN("Profile 0x%04x not found, err %d", profile, res); + return res; + } + + return sdp_get_param_item(&pd, version); +} + +int bt_sdp_get_features(const struct net_buf *buf, uint16_t *features) +{ + struct bt_sdp_attr_item attr; + const uint8_t *p; + int res; + + res = bt_sdp_get_attr(buf, &attr, BT_SDP_ATTR_SUPPORTED_FEATURES); + if (res < 0) { + BT_WARN("Attribute 0x%04x not found, err %d", + BT_SDP_ATTR_SUPPORTED_FEATURES, res); + return res; + } + + p = attr.val; + BT_ASSERT(p); + + if (p[0] != BT_SDP_UINT16) { + BT_ERR("Invalid DTD 0x%02x", p[0]); + return -EINVAL; + } + + /* assert 16bit can be read safely */ + if (attr.len < 3) { + BT_ERR("Data length too short %u", attr.len); + return -EMSGSIZE; + } + + *features = sys_get_be16(++p); + p += sizeof(uint16_t); + + if (p - attr.val != attr.len) { + BT_ERR("Invalid data length %u", attr.len); + return -EMSGSIZE; + } + + return 0; +} diff --git a/components/ble/ble_stack/host/sdp_internal.h b/components/ble/ble_stack/host/sdp_internal.h new file mode 100644 index 00000000..1524318c --- /dev/null +++ b/components/ble/ble_stack/host/sdp_internal.h @@ -0,0 +1,74 @@ +/* sdp_internal.h - Service Discovery Protocol handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The PDU identifiers of SDP packets between client and server + */ +#define BT_SDP_ERROR_RSP 0x01 +#define BT_SDP_SVC_SEARCH_REQ 0x02 +#define BT_SDP_SVC_SEARCH_RSP 0x03 +#define BT_SDP_SVC_ATTR_REQ 0x04 +#define BT_SDP_SVC_ATTR_RSP 0x05 +#define BT_SDP_SVC_SEARCH_ATTR_REQ 0x06 +#define BT_SDP_SVC_SEARCH_ATTR_RSP 0x07 + +/* + * Some additions to support service registration. + * These are outside the scope of the Bluetooth specification + */ +#define BT_SDP_SVC_REGISTER_REQ 0x75 +#define BT_SDP_SVC_REGISTER_RSP 0x76 +#define BT_SDP_SVC_UPDATE_REQ 0x77 +#define BT_SDP_SVC_UPDATE_RSP 0x78 +#define BT_SDP_SVC_REMOVE_REQ 0x79 +#define BT_SDP_SVC_REMOVE_RSP 0x80 + +/* + * SDP Error codes + */ +#define BT_SDP_INVALID_VERSION 0x0001 +#define BT_SDP_INVALID_RECORD_HANDLE 0x0002 +#define BT_SDP_INVALID_SYNTAX 0x0003 +#define BT_SDP_INVALID_PDU_SIZE 0x0004 +#define BT_SDP_INVALID_CSTATE 0x0005 + +#define BT_SDP_MAX_SERVICES 10 + +struct bt_sdp_data_elem_seq { + uint8_t type; /* Type: Will be data element sequence */ + uint16_t size; /* We only support 2 byte sizes for now */ +} __packed; + +struct bt_sdp_hdr { + uint8_t op_code; + uint16_t tid; + uint16_t param_len; +} __packed; + +struct bt_sdp_svc_rsp { + uint16_t total_recs; + uint16_t current_recs; +} __packed; + +struct bt_sdp_att_rsp { + uint16_t att_list_len; +} __packed; + +/* Allowed attributes length in SSA Request PDU to be taken from server */ +#define BT_SDP_MAX_ATTR_LEN 0xffff + +/* Max allowed length of PDU Continuation State */ +#define BT_SDP_MAX_PDU_CSTATE_LEN 16 + +/* Type mapping SDP PDU Continuation State */ +struct bt_sdp_pdu_cstate { + uint8_t length; + uint8_t data[BT_SDP_MAX_PDU_CSTATE_LEN]; +} __packed; + +void bt_sdp_init(void); diff --git a/components/ble/ble_stack/host/settings.c b/components/ble/ble_stack/host/settings.c new file mode 100644 index 00000000..5b675a0f --- /dev/null +++ b/components/ble/ble_stack/host/settings.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SETTINGS) +#define LOG_MODULE_NAME bt_settings +#include "log.h" + +#include "hci_core.h" +#include "settings.h" +#include "keys.h" +#include "gatt.h" +#if defined(BFLB_BLE) +#include +#if defined(CONFIG_BT_SETTINGS) +#include "easyflash.h" +#endif +#include +#include "portable.h" +#endif + +#if defined(CONFIG_BT_SETTINGS_USE_PRINTK) +void bt_settings_encode_key(char *path, size_t path_size, const char *subsys, + bt_addr_le_t *addr, const char *key) +{ + if (key) { + snprintk(path, path_size, + "bt/%s/%02x%02x%02x%02x%02x%02x%u/%s", subsys, + addr->a.val[5], addr->a.val[4], addr->a.val[3], + addr->a.val[2], addr->a.val[1], addr->a.val[0], + addr->type, key); + } else { + snprintk(path, path_size, + "bt/%s/%02x%02x%02x%02x%02x%02x%u", subsys, + addr->a.val[5], addr->a.val[4], addr->a.val[3], + addr->a.val[2], addr->a.val[1], addr->a.val[0], + addr->type); + } + + BT_DBG("Encoded path %s", log_strdup(path)); +} +#else +void bt_settings_encode_key(char *path, size_t path_size, const char *subsys, + bt_addr_le_t *addr, const char *key) +{ + size_t len = 3; + + /* Skip if path_size is less than 3; strlen("bt/") */ + if (len < path_size) { + /* Key format: + * "bt///", "/" is optional + */ + strcpy(path, "bt/"); + strncpy(&path[len], subsys, path_size - len); + len = strlen(path); + if (len < path_size) { + path[len] = '/'; + len++; + } + + for (s8_t i = 5; i >= 0 && len < path_size; i--) { + len += bin2hex(&addr->a.val[i], 1, &path[len], + path_size - len); + } + + if (len < path_size) { + /* Type can be either BT_ADDR_LE_PUBLIC or + * BT_ADDR_LE_RANDOM (value 0 or 1) + */ + path[len] = '0' + addr->type; + len++; + } + + if (key && len < path_size) { + path[len] = '/'; + len++; + strncpy(&path[len], key, path_size - len); + len += strlen(&path[len]); + } + + if (len >= path_size) { + /* Truncate string */ + path[path_size - 1] = '\0'; + } + } else if (path_size > 0) { + *path = '\0'; + } + + BT_DBG("Encoded path %s", log_strdup(path)); +} +#endif + +#if !defined(BFLB_BLE) +int bt_settings_decode_key(const char *key, bt_addr_le_t *addr) +{ + if (settings_name_next(key, NULL) != 13) { + return -EINVAL; + } + + if (key[12] == '0') { + addr->type = BT_ADDR_LE_PUBLIC; + } else if (key[12] == '1') { + addr->type = BT_ADDR_LE_RANDOM; + } else { + return -EINVAL; + } + + for (u8_t i = 0; i < 6; i++) { + hex2bin(&key[i * 2], 2, &addr->a.val[5 - i], 1); + } + + BT_DBG("Decoded %s as %s", log_strdup(key), bt_addr_le_str(addr)); + + return 0; +} + +static int set(const char *name, size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + ssize_t len; + const char *next; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + len = settings_name_next(name, &next); + + if (!strncmp(name, "id", len)) { + /* Any previously provided identities supersede flash */ + if (atomic_test_bit(bt_dev.flags, BT_DEV_PRESET_ID)) { + BT_WARN("Ignoring identities stored in flash"); + return 0; + } + + len = read_cb(cb_arg, &bt_dev.id_addr, sizeof(bt_dev.id_addr)); + if (len < sizeof(bt_dev.id_addr[0])) { + if (len < 0) { + BT_ERR("Failed to read ID address from storage" + " (err %zu)", len); + } else { + BT_ERR("Invalid length ID address in storage"); + BT_HEXDUMP_DBG(&bt_dev.id_addr, len, + "data read"); + } + (void)memset(bt_dev.id_addr, 0, + sizeof(bt_dev.id_addr)); + bt_dev.id_count = 0U; + } else { + int i; + + bt_dev.id_count = len / sizeof(bt_dev.id_addr[0]); + for (i = 0; i < bt_dev.id_count; i++) { + BT_DBG("ID[%d] %s", i, + bt_addr_le_str(&bt_dev.id_addr[i])); + } + } + + return 0; + } + +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + if (!strncmp(name, "name", len)) { + len = read_cb(cb_arg, &bt_dev.name, sizeof(bt_dev.name) - 1); + if (len < 0) { + BT_ERR("Failed to read device name from storage" + " (err %zu)", len); + } else { + bt_dev.name[len] = '\0'; + + BT_DBG("Name set to %s", log_strdup(bt_dev.name)); + } + return 0; + } +#endif + +#if defined(CONFIG_BT_PRIVACY) + if (!strncmp(name, "irk", len)) { + len = read_cb(cb_arg, bt_dev.irk, sizeof(bt_dev.irk)); + if (len < sizeof(bt_dev.irk[0])) { + if (len < 0) { + BT_ERR("Failed to read IRK from storage" + " (err %zu)", len); + } else { + BT_ERR("Invalid length IRK in storage"); + (void)memset(bt_dev.irk, 0, sizeof(bt_dev.irk)); + } + } else { + int i, count; + + count = len / sizeof(bt_dev.irk[0]); + for (i = 0; i < count; i++) { + BT_DBG("IRK[%d] %s", i, + bt_hex(bt_dev.irk[i], 16)); + } + } + + return 0; + } +#endif /* CONFIG_BT_PRIVACY */ + + return -ENOENT; +} + +#define ID_DATA_LEN(array) (bt_dev.id_count * sizeof(array[0])) + +static void save_id(struct k_work *work) +{ + int err; + BT_INFO("Saving ID"); + err = settings_save_one("bt/id", &bt_dev.id_addr, + ID_DATA_LEN(bt_dev.id_addr)); + if (err) { + BT_ERR("Failed to save ID (err %d)", err); + } + +#if defined(CONFIG_BT_PRIVACY) + err = settings_save_one("bt/irk", bt_dev.irk, ID_DATA_LEN(bt_dev.irk)); + if (err) { + BT_ERR("Failed to save IRK (err %d)", err); + } +#endif +} + +K_WORK_DEFINE(save_id_work, save_id); +#endif //!BFLB_BLE +#if defined (BFLB_BLE) +#if defined(CONFIG_BT_SETTINGS) +bool ef_ready_flag = false; +int bt_check_if_ef_ready() +{ + int err = 0; + + if(!ef_ready_flag){ + err = easyflash_init(); + if(!err) + ef_ready_flag = true; + } + + return err; +} + +int bt_settings_set_bin(const char *key, const uint8_t *value, size_t length) +{ + const char *lookup = "0123456789abcdef"; + char *str_value; + int err; + + err = bt_check_if_ef_ready(); + if(err) + return err; + + str_value = pvPortMalloc(length*2 + 1); + + BT_ASSERT(str_value != NULL); + + for(size_t i = 0; i < length; i++){ + str_value[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F]; + str_value[(i * 2) + 1] = lookup[value[i] & 0x0F]; + } + str_value[length * 2] = '\0'; + + err = ef_set_env(key, (const char *)str_value); + + vPortFree(str_value); + + return err; +} + +int bt_settings_get_bin(const char *key, u8_t *value, size_t exp_len, size_t *real_len) +{ + char *str_value; + size_t str_value_len; + char rand[3]; + int err; + + err = bt_check_if_ef_ready(); + if(err) + return err; + + + str_value = ef_get_env(key); + if(str_value == NULL) + { + return -1; + } + + str_value_len = strlen(str_value); + + if((str_value_len % 2) != 0 || (exp_len >0 && str_value_len > exp_len*2)) + { + return -1; + } + + if(real_len) + *real_len = str_value_len/2; + + for(size_t i = 0; i < str_value_len/2; i++){ + strncpy(rand, str_value+2*i, 2); + rand[2] = '\0'; + value[i] = strtol(rand, NULL, 16); + } + + return 0; +} + +int settings_delete(const char *key) +{ + return ef_del_env(key); +} + +int settings_save_one(const char *key, const u8_t *value, size_t length) +{ + return bt_settings_set_bin(key, value, length); +} +#endif //CONFIG_BT_SETTINGS +#endif + +void bt_settings_save_id(void) +{ +#if defined(BFLB_BLE) +#if defined(CONFIG_BT_SETTINGS) + if(bt_check_if_ef_ready()) + return; + bt_settings_set_bin(NV_LOCAL_ID_ADDR, (const u8_t *)&bt_dev.id_addr[0], sizeof(bt_addr_le_t)*CONFIG_BT_ID_MAX); +#if defined(CONFIG_BT_PRIVACY) + bt_settings_set_bin(NV_LOCAL_IRK, (const u8_t *)&bt_dev.irk[0], 16*CONFIG_BT_ID_MAX); +#endif //CONFIG_BT_PRIVACY +#endif //CONFIG_BT_SETTINGS +#else + k_work_submit(&save_id_work); +#endif +} + +#if defined(BFLB_BLE) +#if defined(CONFIG_BT_SETTINGS) +void bt_settings_save_name(void) +{ + if(bt_check_if_ef_ready()) + return; + + ef_set_env(NV_LOCAL_NAME, bt_dev.name); +} + +void bt_local_info_load(void) +{ + if(bt_check_if_ef_ready()) + return; +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + char *dev_name; + uint8_t len; + dev_name = ef_get_env(NV_LOCAL_NAME); + if(dev_name != NULL){ + len = ((strlen(dev_name)+1) < CONFIG_BT_DEVICE_NAME_MAX)? (strlen(dev_name)+1):CONFIG_BT_DEVICE_NAME_MAX; + memcpy(bt_dev.name, dev_name, len); + } +#endif + bt_settings_get_bin(NV_LOCAL_ID_ADDR, (u8_t *)&bt_dev.id_addr[0], sizeof(bt_addr_le_t)*CONFIG_BT_ID_MAX, NULL); +#if defined(CONFIG_BT_PRIVACY) + bt_settings_get_bin(NV_LOCAL_IRK, (u8_t *)&bt_dev.irk[0][0], 16*CONFIG_BT_ID_MAX, NULL); +#endif +} +#endif //CONFIG_BT_SETTINGS +#endif + +#if !defined(BFLB_BLE) +static int commit(void) +{ + BT_DBG(""); + +#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) + if (bt_dev.name[0] == '\0') { + bt_set_name(CONFIG_BT_DEVICE_NAME); + } +#endif + if (!bt_dev.id_count) { + int err; + + err = bt_setup_id_addr(); + if (err) { + BT_ERR("Unable to setup an identity address"); + return err; + } + } + + /* Make sure that the identities created by bt_id_create after + * bt_enable is saved to persistent storage. */ + if (!atomic_test_bit(bt_dev.flags, BT_DEV_PRESET_ID)) { + bt_settings_save_id(); + } + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) { + bt_finalize_init(); + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt, "bt", NULL, set, commit, NULL); + +#endif //!BFLB_BLE + +int bt_settings_init(void) +{ +#if defined(BFLB_BLE) + return 0; +#else + int err; + + BT_DBG(""); + + err = settings_subsys_init(); + if (err) { + BT_ERR("settings_subsys_init failed (err %d)", err); + return err; + } + + return 0; +#endif +} diff --git a/components/ble/ble_stack/host/settings.h b/components/ble/ble_stack/host/settings.h new file mode 100644 index 00000000..76806575 --- /dev/null +++ b/components/ble/ble_stack/host/settings.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if defined(BFLB_BLE) +#include "addr.h" +#endif + +/* Max settings key length (with all components) */ +#define BT_SETTINGS_KEY_MAX 36 + +/* Base64-encoded string buffer size of in_size bytes */ +#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) + +/* Helpers for keys containing a bdaddr */ +void bt_settings_encode_key(char *path, size_t path_size, const char *subsys, + bt_addr_le_t *addr, const char *key); +int bt_settings_decode_key(const char *key, bt_addr_le_t *addr); + +void bt_settings_save_id(void); + +int bt_settings_init(void); + +#if defined(BFLB_BLE) +#define NV_LOCAL_NAME "LOCAL_NAME" +#define NV_LOCAL_ID_ADDR "LOCAL_ID_ADDR" +#define NV_LOCAL_IRK "LOCAL_IRK" +#define NV_KEY_POOL "KEY_POOL" +#define NV_IMG_info "IMG_INFO" + +int bt_settings_get_bin(const char *key, u8_t *value, size_t exp_len, size_t *real_len); +int bt_settings_set_bin(const char *key, const u8_t *value, size_t length); +int settings_delete(const char *key); +int settings_save_one(const char *key, const u8_t *value, size_t length); +void bt_settings_save_name(void); +void bt_local_info_load(void); +#endif diff --git a/components/ble/ble_stack/host/smp.c b/components/ble/ble_stack/host/smp.c new file mode 100644 index 00000000..0bccf32e --- /dev/null +++ b/components/ble/ble_stack/host/smp.c @@ -0,0 +1,5593 @@ +/** + * @file smp.c + * Security Manager Protocol implementation + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include <../include/bluetooth/crypto.h> +#include <../include/bluetooth/buf.h> + +#include "constants.h" +#include "aes.h" +#include "utils.h" +#include "cmac_mode.h" + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SMP) +#define LOG_MODULE_NAME bt_smp +#include "log.h" + +#include "hci_core.h" +#include "ecc.h" +#include "keys.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "smp.h" + +#define SMP_TIMEOUT K_SECONDS(30) + +#if defined(CONFIG_BT_SIGNING) +#define SIGN_DIST BT_SMP_DIST_SIGN +#else +#define SIGN_DIST 0 +#endif + +#if defined(CONFIG_BT_PRIVACY) +#define ID_DIST BT_SMP_DIST_ID_KEY +#else +#define ID_DIST 0 +#endif + +#if defined(CONFIG_BT_BREDR) +#define LINK_DIST BT_SMP_DIST_LINK_KEY +#else +#define LINK_DIST 0 +#endif + +#define RECV_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_ID_KEY | SIGN_DIST |\ + LINK_DIST) +#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | ID_DIST | SIGN_DIST | LINK_DIST) + +#define RECV_KEYS_SC (RECV_KEYS & ~(BT_SMP_DIST_ENC_KEY)) +#define SEND_KEYS_SC (SEND_KEYS & ~(BT_SMP_DIST_ENC_KEY)) + +#define BR_RECV_KEYS_SC (RECV_KEYS & ~(LINK_DIST)) +#define BR_SEND_KEYS_SC (SEND_KEYS & ~(LINK_DIST)) + +#define BT_SMP_AUTH_MASK 0x07 + +#if defined(CONFIG_BT_BONDABLE) +#define BT_SMP_AUTH_BONDING_FLAGS BT_SMP_AUTH_BONDING +#else +#define BT_SMP_AUTH_BONDING_FLAGS 0 +#endif /* CONFIG_BT_BONDABLE */ + + +#if defined(CONFIG_BT_BREDR) +#define BT_SMP_AUTH_MASK_SC 0x2f +#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_SC |\ + BT_SMP_AUTH_CT2) +#else +#define BT_SMP_AUTH_MASK_SC 0x0f +#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_SC) +#endif + +enum pairing_method { + JUST_WORKS, /* JustWorks pairing */ + PASSKEY_INPUT, /* Passkey Entry input */ + PASSKEY_DISPLAY, /* Passkey Entry display */ + PASSKEY_CONFIRM, /* Passkey confirm */ + PASSKEY_ROLE, /* Passkey Entry depends on role */ + LE_SC_OOB, /* LESC Out of Band */ +}; + +enum { + SMP_FLAG_CFM_DELAYED, /* if confirm should be send when TK is valid */ + SMP_FLAG_ENC_PENDING, /* if waiting for an encryption change event */ + SMP_FLAG_KEYS_DISTR, /* if keys distribution phase is in progress */ + SMP_FLAG_PAIRING, /* if pairing is in progress */ + SMP_FLAG_TIMEOUT, /* if SMP timeout occurred */ + SMP_FLAG_SC, /* if LE Secure Connections is used */ + SMP_FLAG_PKEY_SEND, /* if should send Public Key when available */ + SMP_FLAG_DHKEY_PENDING, /* if waiting for local DHKey */ + SMP_FLAG_DHKEY_SEND, /* if should generate and send DHKey Check */ + SMP_FLAG_USER, /* if waiting for user input */ + SMP_FLAG_DISPLAY, /* if display_passkey() callback was called */ + SMP_FLAG_OOB_PENDING, /* if waiting for OOB data */ + SMP_FLAG_BOND, /* if bonding */ + SMP_FLAG_SC_DEBUG_KEY, /* if Secure Connection are using debug key */ + SMP_FLAG_SEC_REQ, /* if Security Request was sent/received */ + SMP_FLAG_DHCHECK_WAIT, /* if waiting for remote DHCheck (as slave) */ + SMP_FLAG_DERIVE_LK, /* if Link Key should be derived */ + SMP_FLAG_BR_CONNECTED, /* if BR/EDR channel is connected */ + SMP_FLAG_BR_PAIR, /* if should start BR/EDR pairing */ + SMP_FLAG_CT2, /* if should use H7 for keys derivation */ + + /* Total number of flags - must be at the end */ + SMP_NUM_FLAGS, +}; + +/* SMP channel specific context */ +struct bt_smp { + /* The channel this context is associated with */ + struct bt_l2cap_le_chan chan; + + /* Commands that remote is allowed to send */ + atomic_t allowed_cmds; + + /* Flags for SMP state machine */ + ATOMIC_DEFINE(flags, SMP_NUM_FLAGS); + + /* Type of method used for pairing */ + u8_t method; + + /* Pairing Request PDU */ + u8_t preq[7]; + + /* Pairing Response PDU */ + u8_t prsp[7]; + + /* Pairing Confirm PDU */ + u8_t pcnf[16]; + + /* Local random number */ + u8_t prnd[16]; + + /* Remote random number */ + u8_t rrnd[16]; + + /* Temporary key */ + u8_t tk[16]; + + /* Remote Public Key for LE SC */ + u8_t pkey[64]; + + /* DHKey */ + u8_t dhkey[32]; + + /* Remote DHKey check */ + u8_t e[16]; + + /* MacKey */ + u8_t mackey[16]; + + /* LE SC passkey */ + u32_t passkey; + + /* LE SC passkey round */ + u8_t passkey_round; + + /* LE SC local OOB data */ + const struct bt_le_oob_sc_data *oobd_local; + + /* LE SC remote OOB data */ + const struct bt_le_oob_sc_data *oobd_remote; + + /* Local key distribution */ + u8_t local_dist; + + /* Remote key distribution */ + u8_t remote_dist; + + /* Delayed work for timeout handling */ + struct k_delayed_work work; +}; + +static unsigned int fixed_passkey = BT_PASSKEY_INVALID; + +#define DISPLAY_FIXED(smp) (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && \ + fixed_passkey != BT_PASSKEY_INVALID && \ + (smp)->method == PASSKEY_DISPLAY) + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */ +static const u8_t gen_method_legacy[5 /* remote */][5 /* local */] = { + { JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT }, + { JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT }, + { PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS, + PASSKEY_DISPLAY }, + { JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS }, + { PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS, + PASSKEY_ROLE }, +}; +#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */ + +/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */ +static const u8_t gen_method_sc[5 /* remote */][5 /* local */] = { + { JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT }, + { JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS, + PASSKEY_CONFIRM }, + { PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS, + PASSKEY_DISPLAY }, + { JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS }, + { PASSKEY_DISPLAY, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS, + PASSKEY_CONFIRM }, +}; + +static const u8_t sc_debug_public_key[64] = { + 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, 0xdb, 0xfd, 0xf4, 0xac, + 0x11, 0x91, 0xf4, 0xef, 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, 0x8b, 0xd2, 0x89, 0x15, + 0xd0, 0x8e, 0x1c, 0x74, 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76, + 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, 0x6d, 0xeb, 0x2a, 0x65, + 0x49, 0x9c, 0x80, 0xdc +}; + +#if defined(CONFIG_BT_BREDR) +/* SMP over BR/EDR channel specific context */ +struct bt_smp_br { + /* The channel this context is associated with */ + struct bt_l2cap_br_chan chan; + + /* Commands that remote is allowed to send */ + atomic_t allowed_cmds; + + /* Flags for SMP state machine */ + ATOMIC_DEFINE(flags, SMP_NUM_FLAGS); + + /* Local key distribution */ + u8_t local_dist; + + /* Remote key distribution */ + u8_t remote_dist; + + /* Encryption Key Size used for connection */ + u8_t enc_key_size; + + /* Delayed work for timeout handling */ + struct k_delayed_work work; +}; + +static struct bt_smp_br bt_smp_br_pool[CONFIG_BT_MAX_CONN]; +#endif /* CONFIG_BT_BREDR */ + +#if defined(CONFIG_BT_STACK_PTS) +static bool mitm = IS_ENABLED(CONFIG_BT_SMP_ENFORCE_MITM); +static int smp_test_flag = 0; +#endif + +static struct bt_smp bt_smp_pool[CONFIG_BT_MAX_CONN]; +static bool bondable = IS_ENABLED(CONFIG_BT_BONDABLE); +static bool oobd_present; +static bool sc_supported; +static const u8_t *sc_public_key; + +#if defined(BFLB_BLE_SMP_LOCAL_AUTH) +#define SMP_INVALID_AUTH 0xFF +u8_t local_auth = SMP_INVALID_AUTH; +#endif + +#if defined(BFLB_BLE) +struct k_sem sc_local_pkey_ready; +#else +static K_SEM_DEFINE(sc_local_pkey_ready, 0, 1); +#endif + +#if defined(CONFIG_AUTO_PTS) +static int smp_error(struct bt_smp *smp, u8_t reason); +static u8_t legacy_pairing_confirm(struct bt_smp *smp); +static struct bt_smp *smp_chan_get(struct bt_conn *conn); + +static void legacy_user_tk_entry(struct bt_smp *smp) +{ + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) { + return; + } + + /* if confirm failed ie. due to invalid passkey, cancel pairing */ + if (legacy_pairing_confirm(smp)) { + smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED); + return; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return; + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + } +} + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +int bt_smp_le_oob_set_tk(struct bt_conn *conn, const uint8_t *tk) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp || !tk) { + return -EINVAL; + } + + BT_DBG("%s", bt_hex(tk, 16)); + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) { + return -EINVAL; + } + + memcpy(smp->tk, tk, 16*sizeof(uint8_t)); + + legacy_user_tk_entry(smp); + + return 0; +} + +int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk) +{ + return bt_smp_le_oob_set_tk(conn, tk); +} + +#endif /* !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) */ +#endif //CONFIG_AUTO_PTS + +#if defined(CONFIG_BLE_AT_CMD) +static u8_t get_io_capa(void); +static u8_t get_auth(struct bt_conn *conn, u8_t auth); + +int ble_set_smp_paramters(const struct smp_parameters *paras) +{ + if(!paras){ + return -1; + } + + memcpy(&user_smp_paras,paras,sizeof(struct smp_parameters)); + return 0; +} + +int ble_get_smp_paramters(const struct bt_conn *conn,struct smp_parameters *paras) +{ + if(!paras ){ + return -1; + } + + if(user_smp_paras.set) + paras->auth = user_smp_paras.auth; + else + paras->auth = BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_SC; + + paras->iocap = get_io_capa(); + + if(user_smp_paras.set) + paras->key_size = user_smp_paras.key_size; + else + paras->key_size = BT_SMP_MAX_ENC_KEY_SIZE; + + if(user_smp_paras.set) + paras->init_key = user_smp_paras.init_key; + else + paras->init_key = SEND_KEYS; + + if(user_smp_paras.set) + paras->rsp_key = user_smp_paras.rsp_key; + else + paras->rsp_key = RECV_KEYS; + + return 0; +} +#endif + +static u8_t get_io_capa(void) +{ +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set){ + return user_smp_paras.iocap; + }else{ +#endif + if (!bt_auth) { + goto no_callbacks; + } + + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_bit(&smp_test_flag, SMP_IO_CAP_DISPLAY_ONLY) || + atomic_test_bit(&smp_test_flag, SMP_AUTH_NO_BONDING_MITM_IO_DISPLAY_ONLY)) { + + return BT_SMP_IO_DISPLAY_ONLY; + + }else if(atomic_test_bit(&smp_test_flag, SMP_IO_KEYBOARD_ONLY)){ + if (bt_auth->passkey_entry){ + return BT_SMP_IO_KEYBOARD_ONLY; + } + }else if(atomic_test_bit(&smp_test_flag, SMP_IO_NO_INPUT_OUTPUT)){ + return BT_SMP_IO_NO_INPUT_OUTPUT; + } + #endif + + /* Passkey Confirmation is valid only for LE SC */ + if (bt_auth->passkey_display && bt_auth->passkey_entry && + (bt_auth->passkey_confirm || !sc_supported)) { + return BT_SMP_IO_KEYBOARD_DISPLAY; + } + + /* DisplayYesNo is useful only for LE SC */ + if (sc_supported && bt_auth->passkey_display && + bt_auth->passkey_confirm) { + return BT_SMP_IO_DISPLAY_YESNO; + } + + if (bt_auth->passkey_entry) { + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + return BT_SMP_IO_KEYBOARD_DISPLAY; + } else { + return BT_SMP_IO_KEYBOARD_ONLY; + } + } + + if (bt_auth->passkey_display) { + return BT_SMP_IO_DISPLAY_ONLY; + } + +no_callbacks: + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + return BT_SMP_IO_DISPLAY_ONLY; + } else { + return BT_SMP_IO_NO_INPUT_OUTPUT; + } +#if defined(CONFIG_BLE_AT_CMD) + } +#endif +} + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +static u8_t legacy_get_pair_method(struct bt_smp *smp, u8_t remote_io); +#endif + +static bool smp_keys_check(struct bt_conn *conn) +{ + if (atomic_test_bit(conn->flags, BT_CONN_FORCE_PAIR)) { + return false; + } + + if (!conn->le.keys) { + conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, + conn->id, &conn->le.dst); + if (!conn->le.keys) { + conn->le.keys = bt_keys_find(BT_KEYS_LTK, + conn->id, + &conn->le.dst); + } + } + + if (!conn->le.keys || + !(conn->le.keys->keys & (BT_KEYS_LTK | BT_KEYS_LTK_P256))) { + return false; + } + + if (conn->required_sec_level > BT_SECURITY_L2 && + !(conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) { + return false; + } + + if (conn->required_sec_level > BT_SECURITY_L3 && + !(conn->le.keys->flags & BT_KEYS_AUTHENTICATED) && + !(conn->le.keys->keys & BT_KEYS_LTK_P256) && + !(conn->le.keys->enc_size == BT_SMP_MAX_ENC_KEY_SIZE)) { + return false; + } + + return true; +} + +static u8_t get_pair_method(struct bt_smp *smp, u8_t remote_io) +{ + struct bt_smp_pairing *req, *rsp; + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + return legacy_get_pair_method(smp, remote_io); + } +#endif + + req = (struct bt_smp_pairing *)&smp->preq[1]; + rsp = (struct bt_smp_pairing *)&smp->prsp[1]; + + if ((req->auth_req & rsp->auth_req) & BT_SMP_AUTH_SC) { + /* if one side has OOB data use OOB */ + if ((req->oob_flag | rsp->oob_flag) & BT_SMP_OOB_DATA_MASK) { + return LE_SC_OOB; + } + } + + if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY) { + return JUST_WORKS; + } + + /* if none side requires MITM use JustWorks */ + if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) { + return JUST_WORKS; + } + + return gen_method_sc[remote_io][get_io_capa()]; +} + +static enum bt_security_err auth_err_get(u8_t smp_err) +{ + switch (smp_err) { + case BT_SMP_ERR_PASSKEY_ENTRY_FAILED: + case BT_SMP_ERR_DHKEY_CHECK_FAILED: + case BT_SMP_ERR_NUMERIC_COMP_FAILED: + case BT_SMP_ERR_CONFIRM_FAILED: + return BT_SECURITY_ERR_AUTH_FAIL; + case BT_SMP_ERR_OOB_NOT_AVAIL: + return BT_SECURITY_ERR_OOB_NOT_AVAILABLE; + case BT_SMP_ERR_AUTH_REQUIREMENTS: + case BT_SMP_ERR_ENC_KEY_SIZE: + return BT_SECURITY_ERR_AUTH_REQUIREMENT; + case BT_SMP_ERR_PAIRING_NOTSUPP: + case BT_SMP_ERR_CMD_NOTSUPP: + return BT_SECURITY_ERR_PAIR_NOT_SUPPORTED; + case BT_SMP_ERR_REPEATED_ATTEMPTS: + case BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS: + case BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED: + return BT_SECURITY_ERR_PAIR_NOT_ALLOWED; + case BT_SMP_ERR_INVALID_PARAMS: + return BT_SECURITY_ERR_INVALID_PARAM; + case BT_SMP_ERR_UNSPECIFIED: + default: + return BT_SECURITY_ERR_UNSPECIFIED; + } +} + +static struct net_buf *smp_create_pdu(struct bt_smp *smp, u8_t op, size_t len) +{ + struct bt_smp_hdr *hdr; + struct net_buf *buf; + s32_t timeout; + + /* Don't if session had already timed out */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + timeout = K_NO_WAIT; + } else { + timeout = SMP_TIMEOUT; + } + + /* Use smaller timeout if returning an error since that could be + * caused by lack of buffers. + */ + buf = bt_l2cap_create_pdu_timeout(NULL, 0, timeout); + if (!buf) { + /* If it was not possible to allocate a buffer within the + * timeout marked it as timed out. + */ + atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT); + return NULL; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = op; + + return buf; +} + +/* Cypher based Message Authentication Code (CMAC) with AES 128 bit + * + * Input : key ( 128-bit key ) + * : in ( message to be authenticated ) + * : len ( length of the message in octets ) + * Output : out ( message authentication code ) + */ +static int bt_smp_aes_cmac(const u8_t *key, const u8_t *in, size_t len, + u8_t *out) +{ + struct tc_aes_key_sched_struct sched; + struct tc_cmac_struct state; + + if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) { + return -EIO; + } + + if (tc_cmac_update(&state, in, len) == TC_CRYPTO_FAIL) { + return -EIO; + } + + if (tc_cmac_final(out, &state) == TC_CRYPTO_FAIL) { + return -EIO; + } + + return 0; +} + +static int smp_f4(const u8_t *u, const u8_t *v, const u8_t *x, + u8_t z, u8_t res[16]) +{ + u8_t xs[16]; + u8_t m[65]; + int err; + + BT_DBG("u %s", bt_hex(u, 32)); + BT_DBG("v %s", bt_hex(v, 32)); + BT_DBG("x %s z 0x%x", bt_hex(x, 16), z); + + /* + * U, V and Z are concatenated and used as input m to the function + * AES-CMAC and X is used as the key k. + * + * Core Spec 4.2 Vol 3 Part H 2.2.5 + * + * note: + * bt_smp_aes_cmac uses BE data and smp_f4 accept LE so we swap + */ + sys_memcpy_swap(m, u, 32); + sys_memcpy_swap(m + 32, v, 32); + m[64] = z; + + sys_memcpy_swap(xs, x, 16); + + err = bt_smp_aes_cmac(xs, m, sizeof(m), res); + if (err) { + return err; + } + + sys_mem_swap(res, 16); + + BT_DBG("res %s", bt_hex(res, 16)); + + return err; +} + +static int smp_f5(const u8_t *w, const u8_t *n1, const u8_t *n2, + const bt_addr_le_t *a1, const bt_addr_le_t *a2, u8_t *mackey, + u8_t *ltk) +{ + static const u8_t salt[16] = { 0x6c, 0x88, 0x83, 0x91, 0xaa, 0xf5, + 0xa5, 0x38, 0x60, 0x37, 0x0b, 0xdb, + 0x5a, 0x60, 0x83, 0xbe }; + u8_t m[53] = { 0x00, /* counter */ + 0x62, 0x74, 0x6c, 0x65, /* keyID */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n1*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*2*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a2 */ + 0x01, 0x00 /* length */ }; + u8_t t[16], ws[32]; + int err; + + BT_DBG("w %s", bt_hex(w, 32)); + BT_DBG("n1 %s", bt_hex(n1, 16)); + BT_DBG("n2 %s", bt_hex(n2, 16)); + + sys_memcpy_swap(ws, w, 32); + + err = bt_smp_aes_cmac(salt, ws, 32, t); + if (err) { + return err; + } + + BT_DBG("t %s", bt_hex(t, 16)); + + sys_memcpy_swap(m + 5, n1, 16); + sys_memcpy_swap(m + 21, n2, 16); + m[37] = a1->type; + sys_memcpy_swap(m + 38, a1->a.val, 6); + m[44] = a2->type; + sys_memcpy_swap(m + 45, a2->a.val, 6); + + err = bt_smp_aes_cmac(t, m, sizeof(m), mackey); + if (err) { + return err; + } + + BT_DBG("mackey %1s", bt_hex(mackey, 16)); + + sys_mem_swap(mackey, 16); + + /* counter for ltk is 1 */ + m[0] = 0x01; + + err = bt_smp_aes_cmac(t, m, sizeof(m), ltk); + if (err) { + return err; + } + + BT_DBG("ltk %s", bt_hex(ltk, 16)); + + sys_mem_swap(ltk, 16); + + return 0; +} + +static int smp_f6(const u8_t *w, const u8_t *n1, const u8_t *n2, + const u8_t *r, const u8_t *iocap, const bt_addr_le_t *a1, + const bt_addr_le_t *a2, u8_t *check) +{ + u8_t ws[16]; + u8_t m[65]; + int err; + + BT_DBG("w %s", bt_hex(w, 16)); + BT_DBG("n1 %s", bt_hex(n1, 16)); + BT_DBG("n2 %s", bt_hex(n2, 16)); + BT_DBG("r %s", bt_hex(r, 16)); + BT_DBG("io_cap %s", bt_hex(iocap, 3)); + BT_DBG("a1 %s", bt_hex(a1, 7)); + BT_DBG("a2 %s", bt_hex(a2, 7)); + + sys_memcpy_swap(m, n1, 16); + sys_memcpy_swap(m + 16, n2, 16); + sys_memcpy_swap(m + 32, r, 16); + sys_memcpy_swap(m + 48, iocap, 3); + + m[51] = a1->type; + memcpy(m + 52, a1->a.val, 6); + sys_memcpy_swap(m + 52, a1->a.val, 6); + + m[58] = a2->type; + memcpy(m + 59, a2->a.val, 6); + sys_memcpy_swap(m + 59, a2->a.val, 6); + + sys_memcpy_swap(ws, w, 16); + + err = bt_smp_aes_cmac(ws, m, sizeof(m), check); + if (err) { + return err; + } + + BT_DBG("res %s", bt_hex(check, 16)); + + sys_mem_swap(check, 16); + + return 0; +} + +static int smp_g2(const u8_t u[32], const u8_t v[32], + const u8_t x[16], const u8_t y[16], u32_t *passkey) +{ + u8_t m[80], xs[16]; + int err; + + BT_DBG("u %s", bt_hex(u, 32)); + BT_DBG("v %s", bt_hex(v, 32)); + BT_DBG("x %s", bt_hex(x, 16)); + BT_DBG("y %s", bt_hex(y, 16)); + + sys_memcpy_swap(m, u, 32); + sys_memcpy_swap(m + 32, v, 32); + sys_memcpy_swap(m + 64, y, 16); + + sys_memcpy_swap(xs, x, 16); + + /* reuse xs (key) as buffer for result */ + err = bt_smp_aes_cmac(xs, m, sizeof(m), xs); + if (err) { + return err; + } + BT_DBG("res %s", bt_hex(xs, 16)); + + memcpy(passkey, xs + 12, 4); + *passkey = sys_be32_to_cpu(*passkey) % 1000000; + + BT_DBG("passkey %u", *passkey); + + return 0; +} + +static u8_t get_encryption_key_size(struct bt_smp *smp) +{ + struct bt_smp_pairing *req, *rsp; + + req = (struct bt_smp_pairing *)&smp->preq[1]; + rsp = (struct bt_smp_pairing *)&smp->prsp[1]; + + /* + * The smaller value of the initiating and responding devices maximum + * encryption key length parameters shall be used as the encryption key + * size. + */ + return MIN(req->max_key_size, rsp->max_key_size); +} + +/* Check that if a new pairing procedure with an existing bond will not lower + * the established security level of the bond. + */ +static bool update_keys_check(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + + if (!conn->le.keys) { + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + } + + if (!conn->le.keys || + !(conn->le.keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) { + return true; + } + + if (conn->le.keys->enc_size > get_encryption_key_size(smp)) { + return false; + } + + if ((conn->le.keys->keys & BT_KEYS_LTK_P256) && + !atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + return false; + } + + if ((conn->le.keys->flags & BT_KEYS_AUTHENTICATED) && + smp->method == JUST_WORKS) { + return false; + } + + if (!IS_ENABLED(CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE) && + (!(conn->le.keys->flags & BT_KEYS_AUTHENTICATED) + && smp->method == JUST_WORKS)) { + return false; + } + + return true; +} + +static bool update_debug_keys_check(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + + if (!conn->le.keys) { + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + } + + if (!conn->le.keys || + !(conn->le.keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) { + return true; + } + + if (conn->le.keys->flags & BT_KEYS_DEBUG) { + return false; + } + + return true; +} + +#if defined(CONFIG_BT_PRIVACY) || defined(CONFIG_BT_SIGNING) || \ + !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +/* For TX callbacks */ +static void smp_pairing_complete(struct bt_smp *smp, u8_t status); +#if defined(CONFIG_BT_BREDR) +static void smp_pairing_br_complete(struct bt_smp_br *smp, u8_t status); +#endif + +static void smp_check_complete(struct bt_conn *conn, u8_t dist_complete) +{ + struct bt_l2cap_chan *chan; + + if (conn->type == BT_CONN_TYPE_LE) { + struct bt_smp *smp; + + chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_SMP); + __ASSERT(chan, "No SMP channel found"); + + smp = CONTAINER_OF(chan, struct bt_smp, chan); + smp->local_dist &= ~dist_complete; + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_complete(smp, 0); + } + + return; + } + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + struct bt_smp_br *smp; + + chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_BR_SMP); + __ASSERT(chan, "No SMP channel found"); + + smp = CONTAINER_OF(chan, struct bt_smp_br, chan); + smp->local_dist &= ~dist_complete; + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_br_complete(smp, 0); + } + } +#endif +} +#endif + +#if defined(CONFIG_BT_PRIVACY) +static void smp_id_sent(struct bt_conn *conn, void *user_data) +{ + smp_check_complete(conn, BT_SMP_DIST_ID_KEY); +} +#endif /* CONFIG_BT_PRIVACY */ + +#if defined(CONFIG_BT_SIGNING) +static void smp_sign_info_sent(struct bt_conn *conn, void *user_data) +{ + smp_check_complete(conn, BT_SMP_DIST_SIGN); +} +#endif /* CONFIG_BT_SIGNING */ + +#if defined(CONFIG_BT_BREDR) +static int smp_h6(const u8_t w[16], const u8_t key_id[4], u8_t res[16]) +{ + u8_t ws[16]; + u8_t key_id_s[4]; + int err; + + BT_DBG("w %s", bt_hex(w, 16)); + BT_DBG("key_id %s", bt_hex(key_id, 4)); + + sys_memcpy_swap(ws, w, 16); + sys_memcpy_swap(key_id_s, key_id, 4); + + err = bt_smp_aes_cmac(ws, key_id_s, 4, res); + if (err) { + return err; + } + + BT_DBG("res %s", bt_hex(res, 16)); + + sys_mem_swap(res, 16); + + return 0; +} + +static int smp_h7(const u8_t salt[16], const u8_t w[16], u8_t res[16]) +{ + u8_t ws[16]; + u8_t salt_s[16]; + int err; + + BT_DBG("w %s", bt_hex(w, 16)); + BT_DBG("salt %s", bt_hex(salt, 16)); + + sys_memcpy_swap(ws, w, 16); + sys_memcpy_swap(salt_s, salt, 16); + + err = bt_smp_aes_cmac(salt_s, ws, 16, res); + if (err) { + return err; + } + + BT_DBG("res %s", bt_hex(res, 16)); + + sys_mem_swap(res, 16); + + return 0; +} + +static void sc_derive_link_key(struct bt_smp *smp) +{ + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */ + static const u8_t lebr[4] = { 0x72, 0x62, 0x65, 0x6c }; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys_link_key *link_key; + u8_t ilk[16]; + + BT_DBG(""); + + /* TODO handle errors? */ + + /* + * At this point remote device identity is known so we can use + * destination address here + */ + link_key = bt_keys_get_link_key(&conn->le.dst.a); + if (!link_key) { + return; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_CT2)) { + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */ + static const u8_t salt[16] = { 0x31, 0x70, 0x6d, 0x74, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + + if (smp_h7(salt, conn->le.keys->ltk.val, ilk)) { + bt_keys_link_key_clear(link_key); + return; + } + } else { + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */ + static const u8_t tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 }; + + if (smp_h6(conn->le.keys->ltk.val, tmp1, ilk)) { + bt_keys_link_key_clear(link_key); + return; + } + } + + if (smp_h6(ilk, lebr, link_key->val)) { + bt_keys_link_key_clear(link_key); + } + + link_key->flags |= BT_LINK_KEY_SC; + + if (conn->le.keys->flags & BT_KEYS_AUTHENTICATED) { + link_key->flags |= BT_LINK_KEY_AUTHENTICATED; + } else { + link_key->flags &= ~BT_LINK_KEY_AUTHENTICATED; + } +} + +static void smp_br_reset(struct bt_smp_br *smp) +{ + k_delayed_work_cancel(&smp->work); + + atomic_set(smp->flags, 0); + atomic_set(&smp->allowed_cmds, 0); + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); +} + +static void smp_pairing_br_complete(struct bt_smp_br *smp, u8_t status) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + bt_addr_le_t addr; + + BT_DBG("status 0x%x", status); + + /* For dualmode devices LE address is same as BR/EDR address + * and is of public type. + */ + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + keys = bt_keys_find_addr(conn->id, &addr); + + if (status) { + if (keys) { + bt_keys_clear(keys); + } + + if (bt_auth && bt_auth->pairing_failed) { + bt_auth->pairing_failed(smp->chan.chan.conn, + auth_err_get(status)); + } + } else { + bool bond_flag = atomic_test_bit(smp->flags, SMP_FLAG_BOND); + + if (bond_flag && keys) { + bt_keys_store(keys); + } + + if (bt_auth && bt_auth->pairing_complete) { + bt_auth->pairing_complete(smp->chan.chan.conn, + bond_flag); + } + } + + smp_br_reset(smp); +} + +static void smp_br_timeout(struct k_work *work) +{ + struct bt_smp_br *smp = CONTAINER_OF(work, struct bt_smp_br, work); + + BT_ERR("SMP Timeout"); + + smp_pairing_br_complete(smp, BT_SMP_ERR_UNSPECIFIED); + atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT); +} + +static void smp_br_send(struct bt_smp_br *smp, struct net_buf *buf, + bt_conn_tx_cb_t cb) +{ + bt_l2cap_send_cb(smp->chan.chan.conn, BT_L2CAP_CID_BR_SMP, buf, cb, + NULL); + k_delayed_work_submit(&smp->work, SMP_TIMEOUT); +} + +static void bt_smp_br_connected(struct bt_l2cap_chan *chan) +{ + struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan); + + BT_DBG("chan %p cid 0x%04x", chan, + CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan)->tx.cid); + + atomic_set_bit(smp->flags, SMP_FLAG_BR_CONNECTED); + + /* + * if this flag is set it means pairing was requested before channel + * was connected + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_BR_PAIR)) { + bt_smp_br_send_pairing_req(chan->conn); + } +} + +static void bt_smp_br_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan); + + BT_DBG("chan %p cid 0x%04x", chan, + CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan)->tx.cid); + + k_delayed_work_cancel(&smp->work); + + (void)memset(smp, 0, sizeof(*smp)); +} + +static void smp_br_init(struct bt_smp_br *smp) +{ + /* Initialize SMP context without clearing L2CAP channel context */ + (void)memset((u8_t *)smp + sizeof(smp->chan), 0, + sizeof(*smp) - (sizeof(smp->chan) + sizeof(smp->work))); + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL); +} + +static void smp_br_derive_ltk(struct bt_smp_br *smp) +{ + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */ + static const u8_t brle[4] = { 0x65, 0x6c, 0x72, 0x62 }; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys_link_key *link_key = conn->br.link_key; + struct bt_keys *keys; + bt_addr_le_t addr; + u8_t ilk[16]; + + BT_DBG(""); + + if (!link_key) { + return; + } + + if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR) && conn->encrypt != 0x02) { + BT_WARN("Using P192 Link Key for P256 LTK derivation"); + } + + /* + * For dualmode devices LE address is same as BR/EDR address and is of + * public type. + */ + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + + keys = bt_keys_get_type(BT_KEYS_LTK_P256, conn->id, &addr); + if (!keys) { + BT_ERR("No keys space for %s", bt_addr_le_str(&addr)); + return; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_CT2)) { + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */ + static const u8_t salt[16] = { 0x32, 0x70, 0x6d, 0x74, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + + if (smp_h7(salt, link_key->val, ilk)) { + bt_keys_link_key_clear(link_key); + return; + } + } else { + /* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */ + static const u8_t tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 }; + + if (smp_h6(link_key->val, tmp2, ilk)) { + bt_keys_clear(keys); + return; + } + } + + if (smp_h6(ilk, brle, keys->ltk.val)) { + bt_keys_clear(keys); + return; + } + + (void)memset(keys->ltk.ediv, 0, sizeof(keys->ltk.ediv)); + (void)memset(keys->ltk.rand, 0, sizeof(keys->ltk.rand)); + keys->enc_size = smp->enc_key_size; + + if (link_key->flags & BT_LINK_KEY_AUTHENTICATED) { + keys->flags |= BT_KEYS_AUTHENTICATED; + } else { + keys->flags &= ~BT_KEYS_AUTHENTICATED; + } + + BT_DBG("LTK derived from LinkKey"); +} + +static struct net_buf *smp_br_create_pdu(struct bt_smp_br *smp, u8_t op, + size_t len) +{ + struct bt_smp_hdr *hdr; + struct net_buf *buf; + s32_t timeout; + + /* Don't if session had already timed out */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + timeout = K_NO_WAIT; + } else { + timeout = SMP_TIMEOUT; + } + + /* Use smaller timeout if returning an error since that could be + * caused by lack of buffers. + */ + buf = bt_l2cap_create_pdu_timeout(NULL, 0, timeout); + if (!buf) { + /* If it was not possible to allocate a buffer within the + * timeout marked it as timed out. + */ + atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT); + return NULL; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = op; + + return buf; +} + +static void smp_br_distribute_keys(struct bt_smp_br *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + bt_addr_le_t addr; + + /* + * For dualmode devices LE address is same as BR/EDR address and is of + * public type. + */ + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + + keys = bt_keys_get_addr(conn->id, &addr); + if (!keys) { + BT_ERR("No keys space for %s", bt_addr_le_str(&addr)); + return; + } + +#if defined(CONFIG_BT_PRIVACY) + if (smp->local_dist & BT_SMP_DIST_ID_KEY) { + struct bt_smp_ident_info *id_info; + struct bt_smp_ident_addr_info *id_addr_info; + struct net_buf *buf; + + smp->local_dist &= ~BT_SMP_DIST_ID_KEY; + + buf = smp_br_create_pdu(smp, BT_SMP_CMD_IDENT_INFO, + sizeof(*id_info)); + if (!buf) { + BT_ERR("Unable to allocate Ident Info buffer"); + return; + } + + id_info = net_buf_add(buf, sizeof(*id_info)); + memcpy(id_info->irk, bt_dev.irk[conn->id], 16); + + smp_br_send(smp, buf, NULL); + + buf = smp_br_create_pdu(smp, BT_SMP_CMD_IDENT_ADDR_INFO, + sizeof(*id_addr_info)); + if (!buf) { + BT_ERR("Unable to allocate Ident Addr Info buffer"); + return; + } + + id_addr_info = net_buf_add(buf, sizeof(*id_addr_info)); + bt_addr_le_copy(&id_addr_info->addr, &bt_dev.id_addr[conn->id]); + + smp_br_send(smp, buf, smp_id_sent); + } +#endif /* CONFIG_BT_PRIVACY */ + +#if defined(CONFIG_BT_SIGNING) + if (smp->local_dist & BT_SMP_DIST_SIGN) { + struct bt_smp_signing_info *info; + struct net_buf *buf; + + smp->local_dist &= ~BT_SMP_DIST_SIGN; + + buf = smp_br_create_pdu(smp, BT_SMP_CMD_SIGNING_INFO, + sizeof(*info)); + if (!buf) { + BT_ERR("Unable to allocate Signing Info buffer"); + return; + } + + info = net_buf_add(buf, sizeof(*info)); + + bt_rand(info->csrk, sizeof(info->csrk)); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + bt_keys_add_type(keys, BT_KEYS_LOCAL_CSRK); + memcpy(keys->local_csrk.val, info->csrk, 16); + keys->local_csrk.cnt = 0U; + } + + smp_br_send(smp, buf, smp_sign_info_sent); + } +#endif /* CONFIG_BT_SIGNING */ +} + +static bool smp_br_pairing_allowed(struct bt_smp_br *smp) +{ + if (smp->chan.chan.conn->encrypt == 0x02) { + return true; + } + + if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR) && + smp->chan.chan.conn->encrypt == 0x01) { + BT_WARN("Allowing BR/EDR SMP with P-192 key"); + return true; + } + + return false; +} + +static u8_t smp_br_pairing_req(struct bt_smp_br *smp, struct net_buf *buf) +{ + struct bt_smp_pairing *req = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_pairing *rsp; + struct net_buf *rsp_buf; + u8_t max_key_size; + + BT_DBG(""); + + /* + * If a Pairing Request is received over the BR/EDR transport when + * either cross-transport key derivation/generation is not supported or + * the BR/EDR transport is not encrypted using a Link Key generated + * using P256, a Pairing Failed shall be sent with the error code + * "Cross-transport Key Derivation/Generation not allowed" (0x0E)." + */ + if (!smp_br_pairing_allowed(smp)) { + return BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED; + } + + max_key_size = bt_conn_enc_key_size(conn); + if (!max_key_size) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (req->max_key_size != max_key_size) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + rsp_buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp)); + if (!rsp_buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + smp_br_init(smp); + smp->enc_key_size = max_key_size; + + /* + * If Secure Connections pairing has been initiated over BR/EDR, the IO + * Capability, OOB data flag and Auth Req fields of the SM Pairing + * Request/Response PDU shall be set to zero on transmission, and + * ignored on reception. + */ + rsp = net_buf_add(rsp_buf, sizeof(*rsp)); + + rsp->auth_req = 0x00; + rsp->io_capability = 0x00; + rsp->oob_flag = 0x00; + rsp->max_key_size = max_key_size; + rsp->init_key_dist = (req->init_key_dist & BR_RECV_KEYS_SC); + rsp->resp_key_dist = (req->resp_key_dist & BR_RECV_KEYS_SC); + + smp->local_dist = rsp->resp_key_dist; + smp->remote_dist = rsp->init_key_dist; + + smp_br_send(smp, rsp_buf, NULL); + + atomic_set_bit(smp->flags, SMP_FLAG_PAIRING); + + /* derive LTK if requested and clear distribution bits */ + if ((smp->local_dist & BT_SMP_DIST_ENC_KEY) && + (smp->remote_dist & BT_SMP_DIST_ENC_KEY)) { + smp_br_derive_ltk(smp); + } + smp->local_dist &= ~BT_SMP_DIST_ENC_KEY; + smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY; + + /* BR/EDR acceptor is like LE Slave and distributes keys first */ + smp_br_distribute_keys(smp); + + if (smp->remote_dist & BT_SMP_DIST_ID_KEY) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } else if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_br_complete(smp, 0); + } + + return 0; +} + +static u8_t smp_br_pairing_rsp(struct bt_smp_br *smp, struct net_buf *buf) +{ + struct bt_smp_pairing *rsp = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + u8_t max_key_size; + + BT_DBG(""); + + max_key_size = bt_conn_enc_key_size(conn); + if (!max_key_size) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (rsp->max_key_size != max_key_size) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + smp->local_dist &= rsp->init_key_dist; + smp->remote_dist &= rsp->resp_key_dist; + + smp->local_dist &= SEND_KEYS_SC; + smp->remote_dist &= RECV_KEYS_SC; + + /* slave distributes its keys first */ + + if (smp->remote_dist & BT_SMP_DIST_ID_KEY) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } else if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + /* derive LTK if requested and clear distribution bits */ + if ((smp->local_dist & BT_SMP_DIST_ENC_KEY) && + (smp->remote_dist & BT_SMP_DIST_ENC_KEY)) { + smp_br_derive_ltk(smp); + } + smp->local_dist &= ~BT_SMP_DIST_ENC_KEY; + smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY; + + /* Pairing acceptor distributes it's keys first */ + if (smp->remote_dist) { + return 0; + } + + smp_br_distribute_keys(smp); + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_br_complete(smp, 0); + } + + return 0; +} + +static u8_t smp_br_pairing_failed(struct bt_smp_br *smp, struct net_buf *buf) +{ + struct bt_smp_pairing_fail *req = (void *)buf->data; + + BT_ERR("reason 0x%x", req->reason); + + smp_pairing_br_complete(smp, req->reason); + smp_br_reset(smp); + + /* return no error to avoid sending Pairing Failed in response */ + return 0; +} + +static u8_t smp_br_ident_info(struct bt_smp_br *smp, struct net_buf *buf) +{ + struct bt_smp_ident_info *req = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + bt_addr_le_t addr; + + BT_DBG(""); + + /* TODO should we resolve LE address if matching RPA is connected? */ + + /* + * For dualmode devices LE address is same as BR/EDR address and is of + * public type. + */ + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + + keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &addr); + if (!keys) { + BT_ERR("Unable to get keys for %s", bt_addr_le_str(&addr)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->irk.val, req->irk, sizeof(keys->irk.val)); + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO); + + return 0; +} + +static u8_t smp_br_ident_addr_info(struct bt_smp_br *smp, + struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_ident_addr_info *req = (void *)buf->data; + bt_addr_le_t addr; + + BT_DBG("identity %s", bt_addr_le_str(&req->addr)); + + /* + * For dual mode device identity address must be same as BR/EDR address + * and be of public type. So if received one doesn't match BR/EDR + * address we fail. + */ + + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + + if (bt_addr_le_cmp(&addr, &req->addr)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + smp->remote_dist &= ~BT_SMP_DIST_ID_KEY; + + if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + if (conn->role == BT_CONN_ROLE_MASTER && !smp->remote_dist) { + smp_br_distribute_keys(smp); + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_br_complete(smp, 0); + } + + return 0; +} + +#if defined(CONFIG_BT_SIGNING) +static u8_t smp_br_signing_info(struct bt_smp_br *smp, struct net_buf *buf) +{ + struct bt_smp_signing_info *req = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + bt_addr_le_t addr; + + BT_DBG(""); + + /* + * For dualmode devices LE address is same as BR/EDR address and is of + * public type. + */ + bt_addr_copy(&addr.a, &conn->br.dst); + addr.type = BT_ADDR_LE_PUBLIC; + + keys = bt_keys_get_type(BT_KEYS_REMOTE_CSRK, conn->id, &addr); + if (!keys) { + BT_ERR("Unable to get keys for %s", bt_addr_le_str(&addr)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->remote_csrk.val, req->csrk, sizeof(keys->remote_csrk.val)); + + smp->remote_dist &= ~BT_SMP_DIST_SIGN; + + if (conn->role == BT_CONN_ROLE_MASTER && !smp->remote_dist) { + smp_br_distribute_keys(smp); + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_br_complete(smp, 0); + } + + return 0; +} +#else +static u8_t smp_br_signing_info(struct bt_smp_br *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* CONFIG_BT_SIGNING */ + +static const struct { + u8_t (*func)(struct bt_smp_br *smp, struct net_buf *buf); + u8_t expect_len; +} br_handlers[] = { + { }, /* No op-code defined for 0x00 */ + { smp_br_pairing_req, sizeof(struct bt_smp_pairing) }, + { smp_br_pairing_rsp, sizeof(struct bt_smp_pairing) }, + { }, /* pairing confirm not used over BR/EDR */ + { }, /* pairing random not used over BR/EDR */ + { smp_br_pairing_failed, sizeof(struct bt_smp_pairing_fail) }, + { }, /* encrypt info not used over BR/EDR */ + { }, /* master ident not used over BR/EDR */ + { smp_br_ident_info, sizeof(struct bt_smp_ident_info) }, + { smp_br_ident_addr_info, sizeof(struct bt_smp_ident_addr_info) }, + { smp_br_signing_info, sizeof(struct bt_smp_signing_info) }, + /* security request not used over BR/EDR */ + /* public key not used over BR/EDR */ + /* DHKey check not used over BR/EDR */ +}; + +static int smp_br_error(struct bt_smp_br *smp, u8_t reason) +{ + struct bt_smp_pairing_fail *rsp; + struct net_buf *buf; + + /* reset context and report */ + smp_br_reset(smp); + + buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp)); + if (!buf) { + return -ENOBUFS; + } + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->reason = reason; + + /* + * SMP timer is not restarted for PairingFailed so don't use + * smp_br_send + */ + bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf); + + return 0; +} + +static int bt_smp_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan); + struct bt_smp_hdr *hdr; + u8_t err; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small SMP PDU received"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_DBG("Received SMP code 0x%02x len %u", hdr->code, buf->len); + + /* + * If SMP timeout occurred "no further SMP commands shall be sent over + * the L2CAP Security Manager Channel. A new SM procedure shall only be + * performed when a new physical link has been established." + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + BT_WARN("SMP command (code 0x%02x) received after timeout", + hdr->code); + return 0; + } + + if (hdr->code >= ARRAY_SIZE(br_handlers) || + !br_handlers[hdr->code].func) { + BT_WARN("Unhandled SMP code 0x%02x", hdr->code); + smp_br_error(smp, BT_SMP_ERR_CMD_NOTSUPP); + return 0; + } + + if (!atomic_test_and_clear_bit(&smp->allowed_cmds, hdr->code)) { + BT_WARN("Unexpected SMP code 0x%02x", hdr->code); + smp_br_error(smp, BT_SMP_ERR_UNSPECIFIED); + return 0; + } + + if (buf->len != br_handlers[hdr->code].expect_len) { + BT_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code); + smp_br_error(smp, BT_SMP_ERR_INVALID_PARAMS); + return 0; + } + + err = br_handlers[hdr->code].func(smp, buf); + if (err) { + smp_br_error(smp, err); + } + + return 0; +} + +static bool br_sc_supported(void) +{ + if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR)) { + BT_WARN("Enabling BR/EDR SMP without BR/EDR SC support"); + return true; + } + + return BT_FEAT_SC(bt_dev.features); +} + +static int bt_smp_br_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + static struct bt_l2cap_chan_ops ops = { + .connected = bt_smp_br_connected, + .disconnected = bt_smp_br_disconnected, + .recv = bt_smp_br_recv, + }; + int i; + + /* Check BR/EDR SC is supported */ + if (!br_sc_supported()) { + return -ENOTSUP; + } + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) { + struct bt_smp_br *smp = &bt_smp_br_pool[i]; + + if (smp->chan.chan.conn) { + continue; + } + + smp->chan.chan.ops = &ops; + + *chan = &smp->chan.chan; + + k_delayed_work_init(&smp->work, smp_br_timeout); + smp_br_reset(smp); + + return 0; + } + + BT_ERR("No available SMP context for conn %p", conn); + + return -ENOMEM; +} + +static struct bt_smp_br *smp_br_chan_get(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan; + + chan = bt_l2cap_br_lookup_rx_cid(conn, BT_L2CAP_CID_BR_SMP); + if (!chan) { + BT_ERR("Unable to find SMP channel"); + return NULL; + } + + return CONTAINER_OF(chan, struct bt_smp_br, chan); +} + +int bt_smp_br_send_pairing_req(struct bt_conn *conn) +{ + struct bt_smp_pairing *req; + struct net_buf *req_buf; + u8_t max_key_size; + struct bt_smp_br *smp; + + smp = smp_br_chan_get(conn); + if (!smp) { + return -ENOTCONN; + } + + /* SMP Timeout */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + return -EIO; + } + + /* pairing is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + return -EBUSY; + } + + /* check if we are allowed to start SMP over BR/EDR */ + if (!smp_br_pairing_allowed(smp)) { + return 0; + } + + /* Channel not yet connected, will start pairing once connected */ + if (!atomic_test_bit(smp->flags, SMP_FLAG_BR_CONNECTED)) { + atomic_set_bit(smp->flags, SMP_FLAG_BR_PAIR); + return 0; + } + + max_key_size = bt_conn_enc_key_size(conn); + if (!max_key_size) { + return -EIO; + } + + smp_br_init(smp); + smp->enc_key_size = max_key_size; + + req_buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_REQ, sizeof(*req)); + if (!req_buf) { + return -ENOBUFS; + } + + req = net_buf_add(req_buf, sizeof(*req)); + + /* + * If Secure Connections pairing has been initiated over BR/EDR, the IO + * Capability, OOB data flag and Auth Req fields of the SM Pairing + * Request/Response PDU shall be set to zero on transmission, and + * ignored on reception. + */ + + req->auth_req = 0x00; + req->io_capability = 0x00; + req->oob_flag = 0x00; + req->max_key_size = max_key_size; + req->init_key_dist = BR_SEND_KEYS_SC; + req->resp_key_dist = BR_RECV_KEYS_SC; + + smp_br_send(smp, req_buf, NULL); + + smp->local_dist = BR_SEND_KEYS_SC; + smp->remote_dist = BR_RECV_KEYS_SC; + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP); + + atomic_set_bit(smp->flags, SMP_FLAG_PAIRING); + + return 0; +} +#endif /* CONFIG_BT_BREDR */ + +static void smp_reset(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + + k_delayed_work_cancel(&smp->work); + + smp->method = JUST_WORKS; + atomic_set(&smp->allowed_cmds, 0); + atomic_set(smp->flags, 0); + + if (conn->required_sec_level != conn->sec_level) { + /* TODO report error */ + /* reset required security level in case of error */ + conn->required_sec_level = conn->sec_level; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + return; + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); + } +} + +static void smp_pairing_complete(struct bt_smp *smp, u8_t status) +{ + BT_DBG("status 0x%x", status); + + if (!status) { +#if defined(CONFIG_BT_BREDR) + /* + * Don't derive if Debug Keys are used. + * TODO should we allow this if BR/EDR is already connected? + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DERIVE_LK) && + (!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY) || + IS_ENABLED(CONFIG_BT_STORE_DEBUG_KEYS))) { + sc_derive_link_key(smp); + } +#endif /* CONFIG_BT_BREDR */ + bool bond_flag = atomic_test_bit(smp->flags, SMP_FLAG_BOND); + +#if defined(CONFIG_BT_SETTINGS) + if (bond_flag) { + bt_keys_store(smp->chan.chan.conn->le.keys); + } +#endif + if (bt_auth && bt_auth->pairing_complete) { + bt_auth->pairing_complete(smp->chan.chan.conn, + bond_flag); + } + } else { + u8_t auth_err = auth_err_get(status); + + if (!atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR)) { + bt_conn_security_changed(smp->chan.chan.conn, auth_err); + } + + if (bt_auth && bt_auth->pairing_failed) { + bt_auth->pairing_failed(smp->chan.chan.conn, auth_err); + } + } + + smp_reset(smp); +} + +static void smp_timeout(struct k_work *work) +{ + struct bt_smp *smp = CONTAINER_OF(work, struct bt_smp, work); + + BT_ERR("SMP Timeout"); + + /* + * If SMP timeout occurred during key distribution we should assume + * pairing failed and don't store any keys from this pairing. + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) && + smp->chan.chan.conn->le.keys) { + bt_keys_clear(smp->chan.chan.conn->le.keys); + } + + atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT); + //case SM/MAS/PROT/BV-01-C + //If smp timeout does not respond to it or close the connection. + /*If the Security Manager Timer reaches 30 seconds, the procedure shall be + considered to have failed, and the local higher layer shall be notified. No further + SMP commands shall be sent over the L2CAP Security Manager Channel. A + new Pairing process shall only be performed when a new physical link has + been established*/ + + //smp_pairing_complete(smp, BT_SMP_ERR_UNSPECIFIED); +} + +static void smp_send(struct bt_smp *smp, struct net_buf *buf, + bt_conn_tx_cb_t cb, void *user_data) +{ + bt_l2cap_send_cb(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf, cb, NULL); + k_delayed_work_submit(&smp->work, SMP_TIMEOUT); +} + +static int smp_error(struct bt_smp *smp, u8_t reason) +{ + struct bt_smp_pairing_fail *rsp; + struct net_buf *buf; + + /* reset context and report */ + smp_pairing_complete(smp, reason); + + buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp)); + if (!buf) { + return -ENOBUFS; + } + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->reason = reason; + + /* SMP timer is not restarted for PairingFailed so don't use smp_send */ + bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf); + + return 0; +} + +static u8_t smp_send_pairing_random(struct bt_smp *smp) +{ + struct bt_smp_pairing_random *req; + struct net_buf *rsp_buf; + + rsp_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_RANDOM, sizeof(*req)); + if (!rsp_buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = net_buf_add(rsp_buf, sizeof(*req)); + memcpy(req->val, smp->prnd, sizeof(req->val)); + + smp_send(smp, rsp_buf, NULL, NULL); + + return 0; +} + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +static void xor_128(const u8_t p[16], const u8_t q[16], u8_t r[16]) +{ + size_t len = 16; + + while (len--) { + *r++ = *p++ ^ *q++; + } +} + +static int smp_c1(const u8_t k[16], const u8_t r[16], + const u8_t preq[7], const u8_t pres[7], + const bt_addr_le_t *ia, const bt_addr_le_t *ra, + u8_t enc_data[16]) +{ + u8_t p1[16], p2[16]; + int err; + + BT_DBG("k %s", bt_hex(k, 16)); + BT_DBG("r %s", bt_hex(r, 16)); + BT_DBG("ia %s", bt_addr_le_str(ia)); + BT_DBG("ra %s", bt_addr_le_str(ra)); + BT_DBG("preq %s", bt_hex(preq, 7)); + BT_DBG("pres %s", bt_hex(pres, 7)); + + /* pres, preq, rat and iat are concatenated to generate p1 */ + p1[0] = ia->type; + p1[1] = ra->type; + memcpy(p1 + 2, preq, 7); + memcpy(p1 + 9, pres, 7); + + BT_DBG("p1 %s", bt_hex(p1, 16)); + + /* c1 = e(k, e(k, r XOR p1) XOR p2) */ + + /* Using enc_data as temporary output buffer */ + xor_128(r, p1, enc_data); + + err = bt_encrypt_le(k, enc_data, enc_data); + if (err) { + return err; + } + + /* ra is concatenated with ia and padding to generate p2 */ + memcpy(p2, ra->a.val, 6); + memcpy(p2 + 6, ia->a.val, 6); + (void)memset(p2 + 12, 0, 4); + + BT_DBG("p2 %s", bt_hex(p2, 16)); + + xor_128(enc_data, p2, enc_data); + + return bt_encrypt_le(k, enc_data, enc_data); +} +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +static u8_t smp_send_pairing_confirm(struct bt_smp *smp) +{ + struct bt_smp_pairing_confirm *req; + struct net_buf *buf; + u8_t r; + + switch (smp->method) { + case PASSKEY_CONFIRM: + case JUST_WORKS: + r = 0U; + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + /* + * In the Passkey Entry protocol, the most significant + * bit of Z is set equal to one and the least + * significant bit is made up from one bit of the + * passkey e.g. if the passkey bit is 1, then Z = 0x81 + * and if the passkey bit is 0, then Z = 0x80. + */ + r = (smp->passkey >> smp->passkey_round) & 0x01; + r |= 0x80; + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req)); + if (!buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = net_buf_add(buf, sizeof(*req)); + + if (smp_f4(sc_public_key, smp->pkey, smp->prnd, r, req->val)) { + net_buf_unref(buf); + return BT_SMP_ERR_UNSPECIFIED; + } + + smp_send(smp, buf, NULL, NULL); + + atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED); + + return 0; +} + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +static void smp_ident_sent(struct bt_conn *conn, void *user_data) +{ + smp_check_complete(conn, BT_SMP_DIST_ENC_KEY); +} + +static void legacy_distribute_keys(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys = conn->le.keys; + + if (smp->local_dist & BT_SMP_DIST_ENC_KEY) { + struct bt_smp_encrypt_info *info; + struct bt_smp_master_ident *ident; + struct net_buf *buf; + /* Use struct to get randomness in single call to bt_rand */ + struct { + u8_t key[16]; + u8_t rand[8]; + u8_t ediv[2]; + } rand; + + bt_rand((void *)&rand, sizeof(rand)); + + buf = smp_create_pdu(smp, BT_SMP_CMD_ENCRYPT_INFO, + sizeof(*info)); + if (!buf) { + BT_ERR("Unable to allocate Encrypt Info buffer"); + return; + } + + info = net_buf_add(buf, sizeof(*info)); + + /* distributed only enc_size bytes of key */ + memcpy(info->ltk, rand.key, keys->enc_size); + if (keys->enc_size < sizeof(info->ltk)) { + (void)memset(info->ltk + keys->enc_size, 0, + sizeof(info->ltk) - keys->enc_size); + } + + smp_send(smp, buf, NULL, NULL); + + buf = smp_create_pdu(smp, BT_SMP_CMD_MASTER_IDENT, + sizeof(*ident)); + if (!buf) { + BT_ERR("Unable to allocate Master Ident buffer"); + return; + } + + ident = net_buf_add(buf, sizeof(*ident)); + memcpy(ident->rand, rand.rand, sizeof(ident->rand)); + memcpy(ident->ediv, rand.ediv, sizeof(ident->ediv)); + + smp_send(smp, buf, smp_ident_sent, NULL); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + bt_keys_add_type(keys, BT_KEYS_SLAVE_LTK); + + memcpy(keys->slave_ltk.val, rand.key, + sizeof(keys->slave_ltk.val)); + memcpy(keys->slave_ltk.rand, rand.rand, + sizeof(keys->slave_ltk.rand)); + memcpy(keys->slave_ltk.ediv, rand.ediv, + sizeof(keys->slave_ltk.ediv)); + } + } +} +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +static u8_t bt_smp_distribute_keys(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys = conn->le.keys; + + if (!keys) { + BT_ERR("No keys space for %s", bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + /* Distribute legacy pairing specific keys */ + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + legacy_distribute_keys(smp); + } +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +#if defined(CONFIG_BT_PRIVACY) + if (smp->local_dist & BT_SMP_DIST_ID_KEY) { + struct bt_smp_ident_info *id_info; + struct bt_smp_ident_addr_info *id_addr_info; + struct net_buf *buf; + + buf = smp_create_pdu(smp, BT_SMP_CMD_IDENT_INFO, + sizeof(*id_info)); + if (!buf) { + BT_ERR("Unable to allocate Ident Info buffer"); + return BT_SMP_ERR_UNSPECIFIED; + } + + id_info = net_buf_add(buf, sizeof(*id_info)); + memcpy(id_info->irk, bt_dev.irk[conn->id], 16); + + smp_send(smp, buf, NULL, NULL); + + buf = smp_create_pdu(smp, BT_SMP_CMD_IDENT_ADDR_INFO, + sizeof(*id_addr_info)); + if (!buf) { + BT_ERR("Unable to allocate Ident Addr Info buffer"); + return BT_SMP_ERR_UNSPECIFIED; + } + + id_addr_info = net_buf_add(buf, sizeof(*id_addr_info)); + bt_addr_le_copy(&id_addr_info->addr, &bt_dev.id_addr[conn->id]); + + smp_send(smp, buf, smp_id_sent, NULL); + } +#endif /* CONFIG_BT_PRIVACY */ + +#if defined(CONFIG_BT_SIGNING) + if (smp->local_dist & BT_SMP_DIST_SIGN) { + struct bt_smp_signing_info *info; + struct net_buf *buf; + + buf = smp_create_pdu(smp, BT_SMP_CMD_SIGNING_INFO, + sizeof(*info)); + if (!buf) { + BT_ERR("Unable to allocate Signing Info buffer"); + return BT_SMP_ERR_UNSPECIFIED; + } + + info = net_buf_add(buf, sizeof(*info)); + + bt_rand(info->csrk, sizeof(info->csrk)); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + bt_keys_add_type(keys, BT_KEYS_LOCAL_CSRK); + memcpy(keys->local_csrk.val, info->csrk, 16); + keys->local_csrk.cnt = 0U; + } + + smp_send(smp, buf, smp_sign_info_sent, NULL); + } +#endif /* CONFIG_BT_SIGNING */ + + return 0; +} + +#if defined(CONFIG_BT_PERIPHERAL) +static u8_t send_pairing_rsp(struct bt_smp *smp) +{ + struct bt_smp_pairing *rsp; + struct net_buf *rsp_buf; + + rsp_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp)); + if (!rsp_buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + rsp = net_buf_add(rsp_buf, sizeof(*rsp)); + memcpy(rsp, smp->prsp + 1, sizeof(*rsp)); + + smp_send(smp, rsp_buf, NULL, NULL); + + return 0; +} +#endif /* CONFIG_BT_PERIPHERAL */ + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +static int smp_s1(const u8_t k[16], const u8_t r1[16], + const u8_t r2[16], u8_t out[16]) +{ + /* The most significant 64-bits of r1 are discarded to generate + * r1' and the most significant 64-bits of r2 are discarded to + * generate r2'. + * r1' is concatenated with r2' to generate r' which is used as + * the 128-bit input parameter plaintextData to security function e: + * + * r' = r1' || r2' + */ + memcpy(out, r2, 8); + memcpy(out + 8, r1, 8); + + /* s1(k, r1 , r2) = e(k, r') */ + return bt_encrypt_le(k, out, out); +} + +static u8_t legacy_get_pair_method(struct bt_smp *smp, u8_t remote_io) +{ + struct bt_smp_pairing *req, *rsp; + u8_t method; + + if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY) { + return JUST_WORKS; + } + + req = (struct bt_smp_pairing *)&smp->preq[1]; + rsp = (struct bt_smp_pairing *)&smp->prsp[1]; + + /* if none side requires MITM use JustWorks */ + if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) { + return JUST_WORKS; + } + + method = gen_method_legacy[remote_io][get_io_capa()]; + + /* if both sides have KeyboardDisplay capabilities, initiator displays + * and responder inputs + */ + if (method == PASSKEY_ROLE) { + if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + method = PASSKEY_DISPLAY; + } else { + method = PASSKEY_INPUT; + } + } + + return method; +} + +static u8_t legacy_request_tk(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + u32_t passkey; + + /* + * Fail if we have keys that are stronger than keys that will be + * distributed in new pairing. This is to avoid replacing authenticated + * keys with unauthenticated ones. + */ + keys = bt_keys_find_addr(conn->id, &conn->le.dst); + if (keys && (keys->flags & BT_KEYS_AUTHENTICATED) && + smp->method == JUST_WORKS) { + BT_ERR("JustWorks failed, authenticated keys present"); + return BT_SMP_ERR_UNSPECIFIED; + } + + switch (smp->method) { + case PASSKEY_DISPLAY: + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + passkey = fixed_passkey; + } else { + if (bt_rand(&passkey, sizeof(passkey))) { + return BT_SMP_ERR_UNSPECIFIED; + } + + passkey %= 1000000; + } + + if (bt_auth && bt_auth->passkey_display) { + atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); + bt_auth->passkey_display(conn, passkey); + } + + sys_put_le32(passkey, smp->tk); + + break; + case PASSKEY_INPUT: + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->passkey_entry(conn); + break; + case JUST_WORKS: + break; + default: + BT_ERR("Unknown pairing method (%u)", smp->method); + return BT_SMP_ERR_UNSPECIFIED; + } + + return 0; +} + +static u8_t legacy_send_pairing_confirm(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_pairing_confirm *req; + struct net_buf *buf; + + buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req)); + if (!buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = net_buf_add(buf, sizeof(*req)); + + if (smp_c1(smp->tk, smp->prnd, smp->preq, smp->prsp, + &conn->le.init_addr, &conn->le.resp_addr, req->val)) { + net_buf_unref(buf); + return BT_SMP_ERR_UNSPECIFIED; + } + + smp_send(smp, buf, NULL, NULL); + + atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED); + + return 0; +} + +#if defined(CONFIG_BT_PERIPHERAL) +static u8_t legacy_pairing_req(struct bt_smp *smp) +{ + u8_t ret; + + BT_DBG(""); + + /* ask for consent if pairing is not due to sending SecReq*/ + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + + return 0; + } + + ret = send_pairing_rsp(smp); + if (ret) { + return ret; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + + return legacy_request_tk(smp); +} +#endif /* CONFIG_BT_PERIPHERAL */ + +static u8_t legacy_pairing_random(struct bt_smp *smp) +{ + struct bt_conn *conn = smp->chan.chan.conn; + u8_t tmp[16]; + int err; + + BT_DBG(""); + + /* calculate confirmation */ + err = smp_c1(smp->tk, smp->rrnd, smp->preq, smp->prsp, + &conn->le.init_addr, &conn->le.resp_addr, tmp); + if (err) { + return BT_SMP_ERR_UNSPECIFIED; + } + + BT_DBG("pcnf %s", bt_hex(smp->pcnf, 16)); + BT_DBG("cfm %s", bt_hex(tmp, 16)); + + if (memcmp(smp->pcnf, tmp, sizeof(smp->pcnf))) { + return BT_SMP_ERR_CONFIRM_FAILED; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER) { + u8_t ediv[2], rand[8]; + + /* No need to store master STK */ + err = smp_s1(smp->tk, smp->rrnd, smp->prnd, tmp); + if (err) { + return BT_SMP_ERR_UNSPECIFIED; + } + + /* Rand and EDiv are 0 for the STK */ + (void)memset(ediv, 0, sizeof(ediv)); + (void)memset(rand, 0, sizeof(rand)); + if (bt_conn_le_start_encryption(conn, rand, ediv, tmp, + get_encryption_key_size(smp))) { + BT_ERR("Failed to start encryption"); + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + + return 0; + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + err = smp_s1(smp->tk, smp->prnd, smp->rrnd, tmp); + if (err) { + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(smp->tk, tmp, sizeof(smp->tk)); + BT_DBG("generated STK %s", bt_hex(smp->tk, 16)); + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + + return smp_send_pairing_random(smp); + } + + return 0; +} + +static u8_t legacy_pairing_confirm(struct bt_smp *smp) +{ + BT_DBG(""); + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return legacy_send_pairing_confirm(smp); + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) { + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_RANDOM); + return legacy_send_pairing_confirm(smp); + } + + atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED); + } + + return 0; +} + +static void legacy_passkey_entry(struct bt_smp *smp, unsigned int passkey) +{ + passkey = sys_cpu_to_le32(passkey); + memcpy(smp->tk, &passkey, sizeof(passkey)); + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) { + return; + } + + /* if confirm failed ie. due to invalid passkey, cancel pairing */ + if (legacy_pairing_confirm(smp)) { + smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED); + return; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return; + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + } +} + +static u8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf) +{ + BT_DBG(""); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + struct bt_smp_encrypt_info *req = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + + keys = bt_keys_get_type(BT_KEYS_LTK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->ltk.val, req->ltk, 16); + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT); + + return 0; +} + +static u8_t smp_master_ident(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + u8_t err; + + BT_DBG(""); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + struct bt_smp_master_ident *req = (void *)buf->data; + struct bt_keys *keys; + + keys = bt_keys_get_type(BT_KEYS_LTK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->ltk.ediv, req->ediv, sizeof(keys->ltk.ediv)); + memcpy(keys->ltk.rand, req->rand, sizeof(req->rand)); + + #if !defined(BFLB_BLE_PATCH_CLEAR_REMOTE_KEY_BIT) + smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY; + #endif + } + + #if defined(BFLB_BLE_PATCH_CLEAR_REMOTE_KEY_BIT) + smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY; + #endif + + if (smp->remote_dist & BT_SMP_DIST_ID_KEY) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } else if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) { + err = bt_smp_distribute_keys(smp); + if (err) { + return err; + } + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_complete(smp, 0); + } + + return 0; +} + +#if defined(CONFIG_BT_CENTRAL) +static u8_t legacy_pairing_rsp(struct bt_smp *smp) +{ + u8_t ret; + + BT_DBG(""); + + /* ask for consent if this is due to received SecReq */ + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + return 0; + } + + ret = legacy_request_tk(smp); + if (ret) { + return ret; + } + + if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return legacy_send_pairing_confirm(smp); + } + + atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED); + + return 0; +} +#endif /* CONFIG_BT_CENTRAL */ +#else +static u8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} + +static u8_t smp_master_ident(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +static int smp_init(struct bt_smp *smp) +{ + /* Initialize SMP context without clearing L2CAP channel context */ + (void)memset((u8_t *)smp + sizeof(smp->chan), 0, + sizeof(*smp) - (sizeof(smp->chan) + sizeof(smp->work))); + + /* Generate local random number */ + if (bt_rand(smp->prnd, 16)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + BT_DBG("prnd %s", bt_hex(smp->prnd, 16)); + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL); + + sc_public_key = bt_pub_key_get(); + #if defined(BFLB_BLE) + k_sem_init(&sc_local_pkey_ready, 0, 1); + #endif + return 0; +} + +void bt_set_bondable(bool enable) +{ + bondable = enable; +} + +#if defined(CONFIG_BT_STACK_PTS) +void bt_set_mitm(bool enable) +{ + mitm = enable; +} + +void bt_set_smpflag(smp_test_id id) +{ + atomic_set_bit(&smp_test_flag, id); +} + +void bt_clear_smpflag(smp_test_id id) +{ + atomic_clear_bit(&smp_test_flag, id); +} +#endif + +void bt_set_oob_data_flag(bool enable) +{ + oobd_present = enable; +} + +#if defined(BFLB_BLE_SMP_LOCAL_AUTH) +void smp_set_auth(u8_t auth) +{ + local_auth = auth; +} +#endif + +static u8_t get_auth(struct bt_conn *conn, u8_t auth) +{ +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set) + return user_smp_paras.auth; + else{ +#endif + #if defined(BFLB_BLE_SMP_LOCAL_AUTH) + if(local_auth != SMP_INVALID_AUTH) + { + return local_auth; + } + #endif + + if (sc_supported) { + auth &= BT_SMP_AUTH_MASK_SC; + } else { + auth &= BT_SMP_AUTH_MASK; + } + + #if defined(CONFIG_BT_STACK_PTS) + if((get_io_capa() == BT_SMP_IO_NO_INPUT_OUTPUT) || + (!mitm && + (conn->required_sec_level < BT_SECURITY_L3))) { + #else + if ((get_io_capa() == BT_SMP_IO_NO_INPUT_OUTPUT) || + (!IS_ENABLED(CONFIG_BT_SMP_ENFORCE_MITM) && + (conn->required_sec_level < BT_SECURITY_L3))) { + #endif + auth &= ~(BT_SMP_AUTH_MITM); + } else { + auth |= BT_SMP_AUTH_MITM; + } + + if (bondable) { + auth |= BT_SMP_AUTH_BONDING; + } else { + auth &= ~BT_SMP_AUTH_BONDING; + } + + return auth; + +#if defined(CONFIG_BLE_AT_CMD) + } +#endif + +} + +static bool sec_level_reachable(struct bt_conn *conn) +{ + switch (conn->required_sec_level) { + case BT_SECURITY_L1: + case BT_SECURITY_L2: + return true; + case BT_SECURITY_L3: + return get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT || + (bt_auth && bt_auth->oob_data_request && oobd_present); + case BT_SECURITY_L4: + return (get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT || + (bt_auth && bt_auth->oob_data_request && + oobd_present)) && + sc_supported; + default: + return false; + } +} + +static struct bt_smp *smp_chan_get(struct bt_conn *conn) +{ + struct bt_l2cap_chan *chan; + + chan = bt_l2cap_le_lookup_rx_cid(conn, BT_L2CAP_CID_SMP); + if (!chan) { + BT_ERR("Unable to find SMP channel"); + return NULL; + } + + return CONTAINER_OF(chan, struct bt_smp, chan); +} + +bool bt_smp_request_ltk(struct bt_conn *conn, u64_t rand, u16_t ediv, u8_t *ltk) +{ + struct bt_smp *smp; + u8_t enc_size; + + smp = smp_chan_get(conn); + if (!smp) { + return false; + } + + /* + * Both legacy STK and LE SC LTK have rand and ediv equal to zero. + * If pairing is in progress use the TK for encryption. + */ + if (ediv == 0U && rand == 0U && + atomic_test_bit(smp->flags, SMP_FLAG_PAIRING) && + atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) { + enc_size = get_encryption_key_size(smp); + + /* + * We keep both legacy STK and LE SC LTK in TK. + * Also use only enc_size bytes of key for encryption. + */ + memcpy(ltk, smp->tk, enc_size); + if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) { + (void)memset(ltk + enc_size, 0, + BT_SMP_MAX_ENC_KEY_SIZE - enc_size); + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + return true; + } + + if (!conn->le.keys) { + conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, conn->id, + &conn->le.dst); + if (!conn->le.keys) { + conn->le.keys = bt_keys_find(BT_KEYS_SLAVE_LTK, + conn->id, &conn->le.dst); + } + } + + if (ediv == 0U && rand == 0U && + conn->le.keys && (conn->le.keys->keys & BT_KEYS_LTK_P256)) { + enc_size = conn->le.keys->enc_size; + + memcpy(ltk, conn->le.keys->ltk.val, enc_size); + if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) { + (void)memset(ltk + enc_size, 0, + BT_SMP_MAX_ENC_KEY_SIZE - enc_size); + } + + return true; + } + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + if (conn->le.keys && (conn->le.keys->keys & BT_KEYS_SLAVE_LTK) && + !memcmp(conn->le.keys->slave_ltk.rand, &rand, 8) && + !memcmp(conn->le.keys->slave_ltk.ediv, &ediv, 2)) { + enc_size = conn->le.keys->enc_size; + + memcpy(ltk, conn->le.keys->slave_ltk.val, enc_size); + if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) { + (void)memset(ltk + enc_size, 0, + BT_SMP_MAX_ENC_KEY_SIZE - enc_size); + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + return true; + } +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + + if (atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) { + /* Notify higher level that security failed if security was + * initiated by slave. + */ + bt_conn_security_changed(smp->chan.chan.conn, + BT_SECURITY_ERR_PIN_OR_KEY_MISSING); + + } + + smp_reset(smp); + return false; +} + +#if defined(CONFIG_BT_PERIPHERAL) +static int smp_send_security_req(struct bt_conn *conn) +{ + struct bt_smp *smp; + struct bt_smp_security_request *req; + struct net_buf *req_buf; + + BT_DBG(""); + smp = smp_chan_get(conn); + if (!smp) { + return -ENOTCONN; + } + + /* SMP Timeout */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + return -EIO; + } + + /* pairing is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + return -EBUSY; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) { + return -EBUSY; + } + + /* early verify if required sec level if reachable */ + if (!(sec_level_reachable(conn) || smp_keys_check(conn))) { + return -EINVAL; + } + + if (!conn->le.keys) { + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + if (!conn->le.keys) { + return -ENOMEM; + } + } + + if (smp_init(smp) != 0) { + return -ENOBUFS; + } + + req_buf = smp_create_pdu(smp, BT_SMP_CMD_SECURITY_REQUEST, + sizeof(*req)); + if (!req_buf) { + return -ENOBUFS; + } + + req = net_buf_add(req_buf, sizeof(*req)); + req->auth_req = get_auth(conn,BT_SMP_AUTH_DEFAULT); + + /* SMP timer is not restarted for SecRequest so don't use smp_send */ + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf); + + atomic_set_bit(smp->flags, SMP_FLAG_SEC_REQ); + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ); + + return 0; +} + +static u8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_pairing *req = (void *)buf->data; + struct bt_smp_pairing *rsp; + + BT_DBG(""); + + if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) || + (req->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + if (!conn->le.keys) { + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + if (!conn->le.keys) { + return BT_SMP_ERR_UNSPECIFIED; + } + } + + /* If we already sent a security request then the SMP context + * is already initialized. + */ + if (!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) { + int ret = smp_init(smp); + + if (ret) { + return ret; + } + } + + /* Store req for later use */ + smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; + memcpy(smp->preq + 1, req, sizeof(*req)); + + /* create rsp, it will be used later on */ + smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP; + rsp = (struct bt_smp_pairing *)&smp->prsp[1]; + + rsp->auth_req = get_auth(conn, req->auth_req); + rsp->io_capability = get_io_capa(); + rsp->oob_flag = oobd_present ? BT_SMP_OOB_PRESENT : + BT_SMP_OOB_NOT_PRESENT; +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set) + rsp->max_key_size = user_smp_paras.key_size; + else +#endif + rsp->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE; + + +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set){ + rsp->init_key_dist = (req->init_key_dist & user_smp_paras.rsp_key); + rsp->resp_key_dist = (req->resp_key_dist & user_smp_paras.init_key); + }else{ +#endif + + rsp->init_key_dist = (req->init_key_dist & RECV_KEYS); + rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS); + +#if defined(CONFIG_BLE_AT_CMD) + } +#endif + + if ((rsp->auth_req & BT_SMP_AUTH_SC) && + (req->auth_req & BT_SMP_AUTH_SC)) { + atomic_set_bit(smp->flags, SMP_FLAG_SC); + + rsp->init_key_dist &= RECV_KEYS_SC; + rsp->resp_key_dist &= SEND_KEYS_SC; + } + + if ((rsp->auth_req & BT_SMP_AUTH_CT2) && + (req->auth_req & BT_SMP_AUTH_CT2)) { + atomic_set_bit(smp->flags, SMP_FLAG_CT2); + } + + smp->local_dist = rsp->resp_key_dist; + smp->remote_dist = rsp->init_key_dist; + + if ((rsp->auth_req & BT_SMP_AUTH_BONDING) && + (req->auth_req & BT_SMP_AUTH_BONDING)) { + atomic_set_bit(smp->flags, SMP_FLAG_BOND); + } + + atomic_set_bit(smp->flags, SMP_FLAG_PAIRING); + + smp->method = get_pair_method(smp, req->io_capability); + + if (!update_keys_check(smp)) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { +#if defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + return BT_SMP_ERR_AUTH_REQUIREMENTS; +#else + return legacy_pairing_req(smp); +#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */ + } + + if ((IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) || + conn->required_sec_level == BT_SECURITY_L4) && + smp->method == JUST_WORKS) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + + if ((IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) || + conn->required_sec_level == BT_SECURITY_L4) && + get_encryption_key_size(smp) != BT_SMP_MAX_ENC_KEY_SIZE) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + + return 0; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); + return send_pairing_rsp(smp); +} +#else +static u8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* CONFIG_BT_PERIPHERAL */ + +static u8_t sc_send_public_key(struct bt_smp *smp) +{ + struct bt_smp_public_key *req; + struct net_buf *req_buf; + + req_buf = smp_create_pdu(smp, BT_SMP_CMD_PUBLIC_KEY, sizeof(*req)); + if (!req_buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = net_buf_add(req_buf, sizeof(*req)); + + memcpy(req->x, sc_public_key, sizeof(req->x)); + memcpy(req->y, &sc_public_key[32], sizeof(req->y)); + + smp_send(smp, req_buf, NULL, NULL); + + if (IS_ENABLED(CONFIG_BT_USE_DEBUG_KEYS)) { + atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY); + } + + return 0; +} + +#if defined(CONFIG_BT_CENTRAL) +static int smp_send_pairing_req(struct bt_conn *conn) +{ + struct bt_smp *smp; + struct bt_smp_pairing *req; + struct net_buf *req_buf; + + BT_DBG(""); + + smp = smp_chan_get(conn); + if (!smp) { + return -ENOTCONN; + } + + /* SMP Timeout */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + return -EIO; + } + + /* pairing is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + return -EBUSY; + } + + /* Encryption is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) { + return -EBUSY; + } + + /* early verify if required sec level if reachable */ + if (!sec_level_reachable(conn)) { + return -EINVAL; + } + + if (!conn->le.keys) { + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + if (!conn->le.keys) { + return -ENOMEM; + } + } + + if (smp_init(smp)) { + return -ENOBUFS; + } + + req_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_REQ, sizeof(*req)); + if (!req_buf) { + return -ENOBUFS; + } + + req = net_buf_add(req_buf, sizeof(*req)); + + req->auth_req = get_auth(conn, BT_SMP_AUTH_DEFAULT); + + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_and_clear_bit(&smp_test_flag, SMP_AUTH_NO_BONDING_MITM) || + atomic_test_bit(&smp_test_flag, SMP_AUTH_NO_BONDING_MITM_IO_DISPLAY_ONLY)){ + req->auth_req &= ~(BT_SMP_AUTH_BONDING | BT_SMP_AUTH_MITM); + }else if(atomic_test_and_clear_bit(&smp_test_flag, SMP_AUTH_NO_MITM)){ + req->auth_req &= ~(BT_SMP_AUTH_MITM); + } + #endif + + req->io_capability = get_io_capa(); + req->oob_flag = oobd_present ? BT_SMP_OOB_PRESENT : + BT_SMP_OOB_NOT_PRESENT; +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set) + req->max_key_size = user_smp_paras.key_size; + else +#endif + req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE; + +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set) + req->init_key_dist = user_smp_paras.init_key; + else +#endif + req->init_key_dist = SEND_KEYS; + +#if defined(CONFIG_BLE_AT_CMD) + if(user_smp_paras.set) + req->resp_key_dist = user_smp_paras.rsp_key; + else +#endif + req->resp_key_dist = RECV_KEYS; + + smp->local_dist = SEND_KEYS; + smp->remote_dist = RECV_KEYS; + + /* Store req for later use */ + smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; + memcpy(smp->preq + 1, req, sizeof(*req)); + + smp_send(smp, req_buf, NULL, NULL); + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP); + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + atomic_set_bit(smp->flags, SMP_FLAG_PAIRING); + + return 0; +} + +static u8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_pairing *rsp = (void *)buf->data; + struct bt_smp_pairing *req = (struct bt_smp_pairing *)&smp->preq[1]; + + BT_DBG(""); + + if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) || + (rsp->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + smp->local_dist &= rsp->init_key_dist; + smp->remote_dist &= rsp->resp_key_dist; + + /* Store rsp for later use */ + smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP; + memcpy(smp->prsp + 1, rsp, sizeof(*rsp)); + + if ((rsp->auth_req & BT_SMP_AUTH_SC) && + (req->auth_req & BT_SMP_AUTH_SC)) { + atomic_set_bit(smp->flags, SMP_FLAG_SC); + } + + if ((rsp->auth_req & BT_SMP_AUTH_CT2) && + (req->auth_req & BT_SMP_AUTH_CT2)) { + atomic_set_bit(smp->flags, SMP_FLAG_CT2); + } + + if ((rsp->auth_req & BT_SMP_AUTH_BONDING) && + (req->auth_req & BT_SMP_AUTH_BONDING)) { + atomic_set_bit(smp->flags, SMP_FLAG_BOND); + } + + smp->method = get_pair_method(smp, rsp->io_capability); + + if (!update_keys_check(smp)) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { +#if defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + return BT_SMP_ERR_AUTH_REQUIREMENTS; +#else + return legacy_pairing_rsp(smp); +#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */ + } + + if ((IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) || + conn->required_sec_level == BT_SECURITY_L4) && + smp->method == JUST_WORKS) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + + if ((IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) || + conn->required_sec_level == BT_SECURITY_L4) && + get_encryption_key_size(smp) != BT_SMP_MAX_ENC_KEY_SIZE) { + return BT_SMP_ERR_ENC_KEY_SIZE; + } + + smp->local_dist &= SEND_KEYS_SC; + smp->remote_dist &= RECV_KEYS_SC; + + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + return 0; + } + + if (!sc_public_key) { + atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND); + return 0; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); + atomic_clear_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + + return sc_send_public_key(smp); +} +#else +static u8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* CONFIG_BT_CENTRAL */ + +static u8_t smp_pairing_confirm(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_smp_pairing_confirm *req = (void *)buf->data; + + BT_DBG(""); + + atomic_clear_bit(smp->flags, SMP_FLAG_DISPLAY); + + memcpy(smp->pcnf, req->val, sizeof(smp->pcnf)); + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + return smp_send_pairing_random(smp); + } + + if (!IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + return 0; + } + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + return legacy_pairing_confirm(smp); + } +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + + switch (smp->method) { + case PASSKEY_DISPLAY: + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + return smp_send_pairing_confirm(smp); + case PASSKEY_INPUT: + if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) { + atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED); + return 0; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + return smp_send_pairing_confirm(smp); + case JUST_WORKS: + case PASSKEY_CONFIRM: + default: + return BT_SMP_ERR_UNSPECIFIED; + } +} + +static u8_t sc_smp_send_dhkey_check(struct bt_smp *smp, const u8_t *e) +{ + struct bt_smp_dhkey_check *req; + struct net_buf *buf; + + BT_DBG(""); + + buf = smp_create_pdu(smp, BT_SMP_DHKEY_CHECK, sizeof(*req)); + if (!buf) { + return BT_SMP_ERR_UNSPECIFIED; + } + + req = net_buf_add(buf, sizeof(*req)); + memcpy(req->e, e, sizeof(req->e)); + + smp_send(smp, buf, NULL, NULL); + + return 0; +} + +#if defined(CONFIG_BT_CENTRAL) +static u8_t compute_and_send_master_dhcheck(struct bt_smp *smp) +{ + u8_t e[16], r[16]; + + (void)memset(r, 0, sizeof(r)); + + switch (smp->method) { + case JUST_WORKS: + case PASSKEY_CONFIRM: + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + memcpy(r, &smp->passkey, sizeof(smp->passkey)); + break; + case LE_SC_OOB: + if (smp->oobd_remote) { + memcpy(r, smp->oobd_remote->r, sizeof(r)); + } + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + /* calculate LTK and mackey */ + if (smp_f5(smp->dhkey, smp->prnd, smp->rrnd, + &smp->chan.chan.conn->le.init_addr, + &smp->chan.chan.conn->le.resp_addr, smp->mackey, + smp->tk)) { + return BT_SMP_ERR_UNSPECIFIED; + } + /* calculate local DHKey check */ + if (smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->preq[1], + &smp->chan.chan.conn->le.init_addr, + &smp->chan.chan.conn->le.resp_addr, e)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK); + return sc_smp_send_dhkey_check(smp, e); +} +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) +static u8_t compute_and_check_and_send_slave_dhcheck(struct bt_smp *smp) +{ + u8_t re[16], e[16], r[16]; + u8_t err; + + (void)memset(r, 0, sizeof(r)); + + switch (smp->method) { + case JUST_WORKS: + case PASSKEY_CONFIRM: + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + memcpy(r, &smp->passkey, sizeof(smp->passkey)); + break; + case LE_SC_OOB: + if (smp->oobd_remote) { + memcpy(r, smp->oobd_remote->r, sizeof(r)); + } + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + /* calculate LTK and mackey */ + if (smp_f5(smp->dhkey, smp->rrnd, smp->prnd, + &smp->chan.chan.conn->le.init_addr, + &smp->chan.chan.conn->le.resp_addr, smp->mackey, + smp->tk)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + /* calculate local DHKey check */ + if (smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->prsp[1], + &smp->chan.chan.conn->le.resp_addr, + &smp->chan.chan.conn->le.init_addr, e)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (smp->method == LE_SC_OOB) { + if (smp->oobd_local) { + memcpy(r, smp->oobd_local->r, sizeof(r)); + } else { + memset(r, 0, sizeof(r)); + } + } + + /* calculate remote DHKey check */ + if (smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->preq[1], + &smp->chan.chan.conn->le.init_addr, + &smp->chan.chan.conn->le.resp_addr, re)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + /* compare received E with calculated remote */ + if (memcmp(smp->e, re, 16)) { + return BT_SMP_ERR_DHKEY_CHECK_FAILED; + } + + /* send local e */ + err = sc_smp_send_dhkey_check(smp, e); + if (err) { + return err; + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + return 0; +} +#endif /* CONFIG_BT_PERIPHERAL */ + +static void bt_smp_dhkey_ready(const u8_t *dhkey) +{ + struct bt_smp *smp = NULL; + int i; + + BT_DBG("%p", dhkey); + + for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) { + if (atomic_test_and_clear_bit(bt_smp_pool[i].flags, + SMP_FLAG_DHKEY_PENDING)) { + smp = &bt_smp_pool[i]; + break; + } + } + + if (!smp) { + return; + } + + if (!dhkey) { + smp_error(smp, BT_SMP_ERR_DHKEY_CHECK_FAILED); + return; + } + + memcpy(smp->dhkey, dhkey, 32); + + /* wait for user passkey confirmation */ + if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return; + } + + /* wait for remote DHKey Check */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_SEND)) { + u8_t err; + +#if defined(CONFIG_BT_CENTRAL) + if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + err = compute_and_send_master_dhcheck(smp); + if (err) { + smp_error(smp, err); + } + + return; + } +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) + err = compute_and_check_and_send_slave_dhcheck(smp); + if (err) { + smp_error(smp, err); + } +#endif /* CONFIG_BT_PERIPHERAL */ + } +} + +static u8_t sc_smp_check_confirm(struct bt_smp *smp) +{ + u8_t cfm[16]; + u8_t r; + + switch (smp->method) { + case LE_SC_OOB: + return 0; + case PASSKEY_CONFIRM: + case JUST_WORKS: + r = 0U; + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + /* + * In the Passkey Entry protocol, the most significant + * bit of Z is set equal to one and the least + * significant bit is made up from one bit of the + * passkey e.g. if the passkey bit is 1, then Z = 0x81 + * and if the passkey bit is 0, then Z = 0x80. + */ + r = (smp->passkey >> smp->passkey_round) & 0x01; + r |= 0x80; + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + if (smp_f4(smp->pkey, sc_public_key, smp->rrnd, r, cfm)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + BT_DBG("pcnf %s", bt_hex(smp->pcnf, 16)); + BT_DBG("cfm %s", bt_hex(cfm, 16)); + + if (memcmp(smp->pcnf, cfm, 16)) { + return BT_SMP_ERR_CONFIRM_FAILED; + } + + return 0; +} + +static bool le_sc_oob_data_req_check(struct bt_smp *smp) +{ + struct bt_smp_pairing *req = (struct bt_smp_pairing *)&smp->preq[1]; + + return ((req->oob_flag & BT_SMP_OOB_DATA_MASK) == BT_SMP_OOB_PRESENT); +} + +static bool le_sc_oob_data_rsp_check(struct bt_smp *smp) +{ + struct bt_smp_pairing *rsp = (struct bt_smp_pairing *)&smp->prsp[1]; + + return ((rsp->oob_flag & BT_SMP_OOB_DATA_MASK) == BT_SMP_OOB_PRESENT); +} + +static void le_sc_oob_config_set(struct bt_smp *smp, + struct bt_conn_oob_info *info) +{ + bool req_oob_present = le_sc_oob_data_req_check(smp); + bool rsp_oob_present = le_sc_oob_data_rsp_check(smp); + int oob_config = BT_CONN_OOB_NO_DATA; + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + oob_config = req_oob_present ? BT_CONN_OOB_REMOTE_ONLY : + BT_CONN_OOB_NO_DATA; + + if (rsp_oob_present) { + oob_config = (oob_config == BT_CONN_OOB_REMOTE_ONLY) ? + BT_CONN_OOB_BOTH_PEERS : + BT_CONN_OOB_LOCAL_ONLY; + } + } else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + oob_config = req_oob_present ? BT_CONN_OOB_LOCAL_ONLY : + BT_CONN_OOB_NO_DATA; + + if (rsp_oob_present) { + oob_config = (oob_config == BT_CONN_OOB_LOCAL_ONLY) ? + BT_CONN_OOB_BOTH_PEERS : + BT_CONN_OOB_REMOTE_ONLY; + } + } + + info->lesc.oob_config = oob_config; +} + +static u8_t smp_pairing_random(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_smp_pairing_random *req = (void *)buf->data; + u32_t passkey; + u8_t err; + + BT_DBG(""); + + memcpy(smp->rrnd, req->val, sizeof(smp->rrnd)); + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + return legacy_pairing_random(smp); + } +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +#if defined(CONFIG_BT_CENTRAL) + if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + err = sc_smp_check_confirm(smp); + if (err) { + return err; + } + + switch (smp->method) { + case PASSKEY_CONFIRM: + /* compare passkey before calculating LTK */ + if (smp_g2(sc_public_key, smp->pkey, smp->prnd, + smp->rrnd, &passkey)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_USER); + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + bt_auth->passkey_confirm(smp->chan.chan.conn, passkey); + return 0; + case JUST_WORKS: + break; + case LE_SC_OOB: + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + smp->passkey_round++; + if (smp->passkey_round == 20U) { + break; + } + + if (bt_rand(smp->prnd, 16)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + return smp_send_pairing_confirm(smp); + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + /* wait for DHKey being generated */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return 0; + } + + return compute_and_send_master_dhcheck(smp); + } +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) + switch (smp->method) { + case PASSKEY_CONFIRM: + if (smp_g2(smp->pkey, sc_public_key, smp->rrnd, smp->prnd, + &passkey)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->passkey_confirm(smp->chan.chan.conn, passkey); + break; + case JUST_WORKS: + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + err = sc_smp_check_confirm(smp); + if (err) { + return err; + } + + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + err = smp_send_pairing_random(smp); + if (err) { + return err; + } + + smp->passkey_round++; + if (smp->passkey_round == 20U) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK); + atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT); + return 0; + } + + if (bt_rand(smp->prnd, 16)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + return 0; + case LE_SC_OOB: + /* Step 6: Select random N */ + if (bt_rand(smp->prnd, 16)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (bt_auth && bt_auth->oob_data_request) { + struct bt_conn_oob_info info = { + .type = BT_CONN_OOB_LE_SC, + .lesc.oob_config = BT_CONN_OOB_NO_DATA, + }; + + le_sc_oob_config_set(smp, &info); + + smp->oobd_local = NULL; + smp->oobd_remote = NULL; + + atomic_set_bit(smp->flags, SMP_FLAG_OOB_PENDING); + bt_auth->oob_data_request(smp->chan.chan.conn, &info); + + return 0; + } else { + return BT_SMP_ERR_OOB_NOT_AVAIL; + } + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK); + atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT); + return smp_send_pairing_random(smp); +#else + return BT_SMP_ERR_PAIRING_NOTSUPP; +#endif /* CONFIG_BT_PERIPHERAL */ +} + +static u8_t smp_pairing_failed(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_pairing_fail *req = (void *)buf->data; + + BT_ERR("reason 0x%x", req->reason); + + if (atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER) || + atomic_test_and_clear_bit(smp->flags, SMP_FLAG_DISPLAY)) { + if (bt_auth && bt_auth->cancel) { + bt_auth->cancel(conn); + } + } + + /* + * Pairing Failed command may be sent at any time during the pairing, + * so if there are any keys distributed, shall be cleared. + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) && + smp->chan.chan.conn->le.keys) { + bt_keys_clear(smp->chan.chan.conn->le.keys); + } + + smp_pairing_complete(smp, req->reason); + + /* return no error to avoid sending Pairing Failed in response */ + return 0; +} + +static u8_t smp_ident_info(struct bt_smp *smp, struct net_buf *buf) +{ + BT_DBG(""); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + struct bt_smp_ident_info *req = (void *)buf->data; + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_keys *keys; + + keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->irk.val, req->irk, 16); + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO); + + return 0; +} + +static u8_t smp_ident_addr_info(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_ident_addr_info *req = (void *)buf->data; + u8_t err; + + BT_DBG("identity %s", bt_addr_le_str(&req->addr)); + + if (!bt_addr_le_is_identity(&req->addr)) { + BT_ERR("Invalid identity %s", bt_addr_le_str(&req->addr)); + BT_ERR(" for %s", bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_INVALID_PARAMS; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + const bt_addr_le_t *dst; + struct bt_keys *keys; + + keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + /* + * We can't use conn->dst here as this might already contain + * identity address known from previous pairing. Since all keys + * are cleared on re-pairing we wouldn't store IRK distributed + * in new pairing. + */ + if (conn->role == BT_HCI_ROLE_MASTER) { + dst = &conn->le.resp_addr; + } else { + dst = &conn->le.init_addr; + } + + if (bt_addr_le_is_rpa(dst)) { + /* always update last use RPA */ + bt_addr_copy(&keys->irk.rpa, &dst->a); + + /* + * Update connection address and notify about identity + * resolved only if connection wasn't already reported + * with identity address. This may happen if IRK was + * present before ie. due to re-pairing. + */ + if (!bt_addr_le_is_identity(&conn->le.dst)) { + bt_addr_le_copy(&keys->addr, &req->addr); + bt_addr_le_copy(&conn->le.dst, &req->addr); + + bt_conn_identity_resolved(conn); + } + } + + bt_id_add(keys); + } + + smp->remote_dist &= ~BT_SMP_DIST_ID_KEY; + + if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) { + err = bt_smp_distribute_keys(smp); + if (err) { + return err; + } + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_complete(smp, 0); + } + + return 0; +} + +#if defined(CONFIG_BT_SIGNING) +static u8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + u8_t err; + + BT_DBG(""); + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + struct bt_smp_signing_info *req = (void *)buf->data; + struct bt_keys *keys; + + keys = bt_keys_get_type(BT_KEYS_REMOTE_CSRK, conn->id, + &conn->le.dst); + if (!keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + return BT_SMP_ERR_UNSPECIFIED; + } + + memcpy(keys->remote_csrk.val, req->csrk, + sizeof(keys->remote_csrk.val)); + } + + smp->remote_dist &= ~BT_SMP_DIST_SIGN; + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) { + err = bt_smp_distribute_keys(smp); + if (err) { + return err; + } + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_complete(smp, 0); + } + + return 0; +} +#else +static u8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* CONFIG_BT_SIGNING */ + +#if defined(CONFIG_BT_CENTRAL) +static u8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_conn *conn = smp->chan.chan.conn; + struct bt_smp_security_request *req = (void *)buf->data; + u8_t auth; + + BT_DBG(""); + + if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + /* We have already started pairing process */ + return 0; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) { + /* We have already started encryption procedure */ + return 0; + } + + if (sc_supported) { + auth = req->auth_req & BT_SMP_AUTH_MASK_SC; + } else { + auth = req->auth_req & BT_SMP_AUTH_MASK; + } + + if (conn->le.keys) { + /* Make sure we have an LTK to encrypt with */ + if (!(conn->le.keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) { + goto pair; + } + } else { + conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, conn->id, + &conn->le.dst); + if (!conn->le.keys) { + conn->le.keys = bt_keys_find(BT_KEYS_LTK, conn->id, + &conn->le.dst); + } + } + + if (!conn->le.keys) { + goto pair; + } + + /* if MITM required key must be authenticated */ + if ((auth & BT_SMP_AUTH_MITM) && + !(conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) { + if (get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT) { + BT_INFO("New auth requirements: 0x%x, repairing", + auth); + goto pair; + } + + BT_WARN("Unsupported auth requirements: 0x%x, repairing", + auth); + goto pair; + } + + /* if LE SC required and no p256 key present repair */ + if ((auth & BT_SMP_AUTH_SC) && + !(conn->le.keys->keys & BT_KEYS_LTK_P256)) { + BT_INFO("New auth requirements: 0x%x, repairing", auth); + goto pair; + } + + if (bt_conn_le_start_encryption(conn, conn->le.keys->ltk.rand, + conn->le.keys->ltk.ediv, + conn->le.keys->ltk.val, + conn->le.keys->enc_size) < 0) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + + return 0; +pair: + if (smp_send_pairing_req(conn) < 0) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_SEC_REQ); + + return 0; +} +#else +static u8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf) +{ + return BT_SMP_ERR_CMD_NOTSUPP; +} +#endif /* CONFIG_BT_CENTRAL */ + +static u8_t generate_dhkey(struct bt_smp *smp) +{ + if (bt_dh_key_gen(smp->pkey, bt_smp_dhkey_ready)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_PENDING); + return 0; +} + +static u8_t display_passkey(struct bt_smp *smp) +{ + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + smp->passkey = fixed_passkey; + } else { + if (bt_rand(&smp->passkey, sizeof(smp->passkey))) { + return BT_SMP_ERR_UNSPECIFIED; + } + + smp->passkey %= 1000000; + } + + smp->passkey_round = 0U; + + if (bt_auth && bt_auth->passkey_display) { + atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); + bt_auth->passkey_display(smp->chan.chan.conn, smp->passkey); + } + + smp->passkey = sys_cpu_to_le32(smp->passkey); + + return 0; +} + +#if defined(CONFIG_BT_PERIPHERAL) +static u8_t smp_public_key_slave(struct bt_smp *smp) +{ + u8_t err; + + err = sc_send_public_key(smp); + if (err) { + return err; + } + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_bit(&smp_test_flag, SMP_PARING_INVALID_PUBLIC_KEY)){ + return 1; + } + #endif + + switch (smp->method) { + case PASSKEY_CONFIRM: + case JUST_WORKS: + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + + err = smp_send_pairing_confirm(smp); + if (err) { + return err; + } + break; + case PASSKEY_DISPLAY: + err = display_passkey(smp); + if (err) { + return err; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + break; + case PASSKEY_INPUT: + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->passkey_entry(smp->chan.chan.conn); + break; + case LE_SC_OOB: + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + return generate_dhkey(smp); +} +#endif /* CONFIG_BT_PERIPHERAL */ + +static u8_t smp_public_key(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_smp_public_key *req = (void *)buf->data; + u8_t err; + + BT_DBG(""); + + memcpy(smp->pkey, req->x, 32); + memcpy(&smp->pkey[32], req->y, 32); + + /* mark key as debug if remote is using it */ + if (memcmp(smp->pkey, sc_debug_public_key, 64) == 0) { + BT_INFO("Remote is using Debug Public key"); + atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY); + + /* Don't allow a bond established without debug key to be + * updated using LTK generated from debug key. + */ + if (!update_debug_keys_check(smp)) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_bit(&smp_test_flag, SMP_PARING_INVALID_PUBLIC_KEY)){ + return 1; + } + #endif + switch (smp->method) { + case PASSKEY_CONFIRM: + case JUST_WORKS: + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + break; + case PASSKEY_DISPLAY: + err = display_passkey(smp); + if (err) { + return err; + } + + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + + err = smp_send_pairing_confirm(smp); + if (err) { + return err; + } + break; + case PASSKEY_INPUT: + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->passkey_entry(smp->chan.chan.conn); + break; + case LE_SC_OOB: + /* Step 6: Select random N */ + if (bt_rand(smp->prnd, 16)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (bt_auth && bt_auth->oob_data_request) { + struct bt_conn_oob_info info = { + .type = BT_CONN_OOB_LE_SC, + .lesc.oob_config = BT_CONN_OOB_NO_DATA, + }; + + le_sc_oob_config_set(smp, &info); + + smp->oobd_local = NULL; + smp->oobd_remote = NULL; + + atomic_set_bit(smp->flags, + SMP_FLAG_OOB_PENDING); + bt_auth->oob_data_request(smp->chan.chan.conn, + &info); + } else { + return BT_SMP_ERR_OOB_NOT_AVAIL; + } + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + return generate_dhkey(smp); + } + +#if defined(CONFIG_BT_PERIPHERAL) + if (!sc_public_key) { + atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND); + return 0; + } + + err = smp_public_key_slave(smp); + if (err) { + return err; + } +#endif /* CONFIG_BT_PERIPHERAL */ + + return 0; +} + +static u8_t smp_dhkey_check(struct bt_smp *smp, struct net_buf *buf) +{ + struct bt_smp_dhkey_check *req = (void *)buf->data; + + BT_DBG(""); + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + u8_t e[16], r[16], enc_size; + u8_t ediv[2], rand[8]; + + (void)memset(r, 0, sizeof(r)); + + switch (smp->method) { + case JUST_WORKS: + case PASSKEY_CONFIRM: + break; + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + memcpy(r, &smp->passkey, sizeof(smp->passkey)); + break; + case LE_SC_OOB: + if (smp->oobd_local) { + memcpy(r, smp->oobd_local->r, sizeof(r)); + } + break; + default: + return BT_SMP_ERR_UNSPECIFIED; + } + + /* calculate remote DHKey check for comparison */ + if (smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->prsp[1], + &smp->chan.chan.conn->le.resp_addr, + &smp->chan.chan.conn->le.init_addr, e)) { + return BT_SMP_ERR_UNSPECIFIED; + } + + if (memcmp(e, req->e, 16)) { + return BT_SMP_ERR_DHKEY_CHECK_FAILED; + } + + enc_size = get_encryption_key_size(smp); + + /* Rand and EDiv are 0 */ + (void)memset(ediv, 0, sizeof(ediv)); + (void)memset(rand, 0, sizeof(rand)); + if (bt_conn_le_start_encryption(smp->chan.chan.conn, rand, ediv, + smp->tk, enc_size) < 0) { + return BT_SMP_ERR_UNSPECIFIED; + } + + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + return 0; + } + +#if defined(CONFIG_BT_PERIPHERAL) + if (smp->chan.chan.conn->role == BT_HCI_ROLE_SLAVE) { + atomic_clear_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT); + memcpy(smp->e, req->e, sizeof(smp->e)); + + /* wait for DHKey being generated */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return 0; + } + + /* waiting for user to confirm passkey */ + if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return 0; + } + + return compute_and_check_and_send_slave_dhcheck(smp); + } +#endif /* CONFIG_BT_PERIPHERAL */ + + return 0; +} + +static const struct { + u8_t (*func)(struct bt_smp *smp, struct net_buf *buf); + u8_t expect_len; +} handlers[] = { + { }, /* No op-code defined for 0x00 */ + { smp_pairing_req, sizeof(struct bt_smp_pairing) }, + { smp_pairing_rsp, sizeof(struct bt_smp_pairing) }, + { smp_pairing_confirm, sizeof(struct bt_smp_pairing_confirm) }, + { smp_pairing_random, sizeof(struct bt_smp_pairing_random) }, + { smp_pairing_failed, sizeof(struct bt_smp_pairing_fail) }, + { smp_encrypt_info, sizeof(struct bt_smp_encrypt_info) }, + { smp_master_ident, sizeof(struct bt_smp_master_ident) }, + { smp_ident_info, sizeof(struct bt_smp_ident_info) }, + { smp_ident_addr_info, sizeof(struct bt_smp_ident_addr_info) }, + { smp_signing_info, sizeof(struct bt_smp_signing_info) }, + { smp_security_request, sizeof(struct bt_smp_security_request) }, + { smp_public_key, sizeof(struct bt_smp_public_key) }, + { smp_dhkey_check, sizeof(struct bt_smp_dhkey_check) }, +}; + +static int bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan); + struct bt_smp_hdr *hdr; + u8_t err; + + if (buf->len < sizeof(*hdr)) { + BT_ERR("Too small SMP PDU received"); + return 0; + } + + hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + BT_DBG("Received SMP code 0x%02x len %u", hdr->code, buf->len); + + /* + * If SMP timeout occurred "no further SMP commands shall be sent over + * the L2CAP Security Manager Channel. A new SM procedure shall only be + * performed when a new physical link has been established." + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) { + BT_WARN("SMP command (code 0x%02x) received after timeout", + hdr->code); + return 0; + } + + if (hdr->code >= ARRAY_SIZE(handlers) || !handlers[hdr->code].func) { + BT_WARN("Unhandled SMP code 0x%02x", hdr->code); + smp_error(smp, BT_SMP_ERR_CMD_NOTSUPP); + return 0; + } + + if (!atomic_test_and_clear_bit(&smp->allowed_cmds, hdr->code)) { + BT_WARN("Unexpected SMP code 0x%02x", hdr->code); + /* Don't send error responses to error PDUs */ + if (hdr->code != BT_SMP_CMD_PAIRING_FAIL) { + + /*Don't send error responses when smp is timeout*/ + #if defined(CONFIG_BT_STACK_PTS) + if(atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) + #endif + smp_error(smp, BT_SMP_ERR_UNSPECIFIED); + + } + return 0; + } + + if (buf->len != handlers[hdr->code].expect_len) { + BT_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code); + smp_error(smp, BT_SMP_ERR_INVALID_PARAMS); + return 0; + } + + err = handlers[hdr->code].func(smp, buf); + if (err) { + smp_error(smp, err); + } + + return 0; +} + +static void bt_smp_pkey_ready(const u8_t *pkey) +{ + int i; + + BT_DBG(""); + + sc_public_key = pkey; + + if (!pkey) { + BT_WARN("Public key not available"); + return; + } + + k_sem_give(&sc_local_pkey_ready); + + for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) { + struct bt_smp *smp = &bt_smp_pool[i]; + u8_t err; + + if (!atomic_test_bit(smp->flags, SMP_FLAG_PKEY_SEND)) { + continue; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + err = sc_send_public_key(smp); + if (err) { + smp_error(smp, err); + } + + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PUBLIC_KEY); + continue; + } + +#if defined(CONFIG_BT_PERIPHERAL) + err = smp_public_key_slave(smp); + if (err) { + smp_error(smp, err); + } +#endif /* CONFIG_BT_PERIPHERAL */ + } +} + +static void bt_smp_connected(struct bt_l2cap_chan *chan) +{ + struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan); + + BT_DBG("chan %p cid 0x%04x", chan, + CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan)->tx.cid); + + k_delayed_work_init(&smp->work, smp_timeout); + smp_reset(smp); +} + +static void bt_smp_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan); + struct bt_keys *keys = chan->conn->le.keys; + + BT_DBG("chan %p cid 0x%04x", chan, + CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan)->tx.cid); + + k_delayed_work_cancel(&smp->work); + + #ifdef BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS + if(smp->work.timer.timer.hdl) + k_delayed_work_del_timer(&smp->work); + + if(chan->rtx_work.timer.timer.hdl) + k_delayed_work_del_timer(&chan->rtx_work); + #endif + + if (keys) { + /* + * If debug keys were used for pairing remove them. + * No keys indicate no bonding so free keys storage. + */ + if (!keys->keys || (!IS_ENABLED(CONFIG_BT_STORE_DEBUG_KEYS) && + (keys->flags & BT_KEYS_DEBUG))) { + bt_keys_clear(keys); + } + } + + (void)memset(smp, 0, sizeof(*smp)); +} + +static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan, + u8_t hci_status) +{ + struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan); + struct bt_conn *conn = chan->conn; + + BT_DBG("chan %p conn %p handle %u encrypt 0x%02x hci status 0x%02x", + chan, conn, conn->handle, conn->encrypt, hci_status); + + atomic_clear_bit(smp->flags, SMP_FLAG_ENC_PENDING); + + if (hci_status) { + return; + } + + if (!conn->encrypt) { + return; + } + + /* We were waiting for encryption but with no pairing in progress. + * This can happen if paired slave sent Security Request and we + * enabled encryption. + */ + if (!atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + smp_reset(smp); + return; + } + + /* derive BR/EDR LinkKey if supported by both sides */ + if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + if ((smp->local_dist & BT_SMP_DIST_LINK_KEY) && + (smp->remote_dist & BT_SMP_DIST_LINK_KEY)) { + /* + * Link Key will be derived after key distribution to + * make sure remote device identity is known + */ + atomic_set_bit(smp->flags, SMP_FLAG_DERIVE_LK); + } + /* + * Those are used as pairing finished indicator so generated + * but not distributed keys must be cleared here. + */ + smp->local_dist &= ~BT_SMP_DIST_LINK_KEY; + smp->remote_dist &= ~BT_SMP_DIST_LINK_KEY; + } + + if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_ENCRYPT_INFO); + } else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO); + } else if (smp->remote_dist & BT_SMP_DIST_SIGN) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO); + } + + atomic_set_bit(smp->flags, SMP_FLAG_KEYS_DISTR); + + /* Slave distributes it's keys first */ + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_HCI_ROLE_MASTER && smp->remote_dist) { + return; + } + + if (bt_smp_distribute_keys(smp)) { + return; + } + + /* if all keys were distributed, pairing is done */ + if (!smp->local_dist && !smp->remote_dist) { + smp_pairing_complete(smp, 0); + } +} + +#if defined(CONFIG_BT_SIGNING) || defined(CONFIG_BT_SMP_SELFTEST) +/* Sign message using msg as a buffer, len is a size of the message, + * msg buffer contains message itself, 32 bit count and signature, + * so total buffer size is len + 4 + 8 octets. + * API is Little Endian to make it suitable for Bluetooth. + */ +static int smp_sign_buf(const u8_t *key, u8_t *msg, u16_t len) +{ + u8_t *m = msg; + u32_t cnt = UNALIGNED_GET((u32_t *)&msg[len]); + u8_t *sig = msg + len; + u8_t key_s[16], tmp[16]; + int err; + + BT_DBG("Signing msg %s len %u key %s", bt_hex(msg, len), len, + bt_hex(key, 16)); + + sys_mem_swap(m, len + sizeof(cnt)); + sys_memcpy_swap(key_s, key, 16); + + err = bt_smp_aes_cmac(key_s, m, len + sizeof(cnt), tmp); + if (err) { + BT_ERR("Data signing failed"); + return err; + } + + sys_mem_swap(tmp, sizeof(tmp)); + memcpy(tmp + 4, &cnt, sizeof(cnt)); + + /* Swap original message back */ + sys_mem_swap(m, len + sizeof(cnt)); + + memcpy(sig, tmp + 4, 12); + + BT_DBG("sig %s", bt_hex(sig, 12)); + + return 0; +} +#endif + +#if defined(CONFIG_BT_SIGNING) +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf) +{ + struct bt_keys *keys; + u8_t sig[12]; + u32_t cnt; + int err; + + /* Store signature incl. count */ + memcpy(sig, net_buf_tail(buf) - sizeof(sig), sizeof(sig)); + + keys = bt_keys_find(BT_KEYS_REMOTE_CSRK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to find Remote CSRK for %s", + bt_addr_le_str(&conn->le.dst)); + return -ENOENT; + } + + /* Copy signing count */ + cnt = sys_cpu_to_le32(keys->remote_csrk.cnt); + memcpy(net_buf_tail(buf) - sizeof(sig), &cnt, sizeof(cnt)); + + BT_DBG("Sign data len %zu key %s count %u", buf->len - sizeof(sig), + bt_hex(keys->remote_csrk.val, 16), keys->remote_csrk.cnt); + + err = smp_sign_buf(keys->remote_csrk.val, buf->data, + buf->len - sizeof(sig)); + if (err) { + BT_ERR("Unable to create signature for %s", + bt_addr_le_str(&conn->le.dst)); + return -EIO; + }; + + if (memcmp(sig, net_buf_tail(buf) - sizeof(sig), sizeof(sig))) { + BT_ERR("Unable to verify signature for %s", + bt_addr_le_str(&conn->le.dst)); + return -EBADMSG; + }; + + keys->remote_csrk.cnt++; + + return 0; +} + +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf) +{ + struct bt_keys *keys; + u32_t cnt; + int err; + + keys = bt_keys_find(BT_KEYS_LOCAL_CSRK, conn->id, &conn->le.dst); + if (!keys) { + BT_ERR("Unable to find local CSRK for %s", + bt_addr_le_str(&conn->le.dst)); + return -ENOENT; + } + + /* Reserve space for data signature */ + net_buf_add(buf, 12); + + /* Copy signing count */ + cnt = sys_cpu_to_le32(keys->local_csrk.cnt); + memcpy(net_buf_tail(buf) - 12, &cnt, sizeof(cnt)); + + BT_DBG("Sign data len %u key %s count %u", buf->len, + bt_hex(keys->local_csrk.val, 16), keys->local_csrk.cnt); + + err = smp_sign_buf(keys->local_csrk.val, buf->data, buf->len - 12); + if (err) { + BT_ERR("Unable to create signature for %s", + bt_addr_le_str(&conn->le.dst)); + return -EIO; + } + + keys->local_csrk.cnt++; + + return 0; +} +#else +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} + +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} +#endif /* CONFIG_BT_SIGNING */ + +#if defined(CONFIG_BT_SMP_SELFTEST) +/* Test vectors are taken from RFC 4493 + * https://tools.ietf.org/html/rfc4493 + * Same mentioned in the Bluetooth Spec. + */ +static const u8_t key[] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c +}; + +static const u8_t M[] = { + 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 +}; + +static int aes_test(const char *prefix, const u8_t *key, const u8_t *m, + u16_t len, const u8_t *mac) +{ + u8_t out[16]; + + BT_DBG("%s: AES CMAC of message with len %u", prefix, len); + + bt_smp_aes_cmac(key, m, len, out); + if (!memcmp(out, mac, 16)) { + BT_DBG("%s: Success", prefix); + } else { + BT_ERR("%s: Failed", prefix); + return -1; + } + + return 0; +} + +static int smp_aes_cmac_test(void) +{ + u8_t mac1[] = { + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 + }; + u8_t mac2[] = { + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + }; + u8_t mac3[] = { + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 + }; + u8_t mac4[] = { + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe + }; + int err; + + err = aes_test("Test aes-cmac0", key, M, 0, mac1); + if (err) { + return err; + } + + err = aes_test("Test aes-cmac16", key, M, 16, mac2); + if (err) { + return err; + } + + err = aes_test("Test aes-cmac40", key, M, 40, mac3); + if (err) { + return err; + } + + err = aes_test("Test aes-cmac64", key, M, 64, mac4); + if (err) { + return err; + } + + return 0; +} + +static int sign_test(const char *prefix, const u8_t *key, const u8_t *m, + u16_t len, const u8_t *sig) +{ + u8_t msg[len + sizeof(u32_t) + 8]; + u8_t orig[len + sizeof(u32_t) + 8]; + u8_t *out = msg + len; + int err; + + BT_DBG("%s: Sign message with len %u", prefix, len); + + (void)memset(msg, 0, sizeof(msg)); + memcpy(msg, m, len); + (void)memset(msg + len, 0, sizeof(u32_t)); + + memcpy(orig, msg, sizeof(msg)); + + err = smp_sign_buf(key, msg, len); + if (err) { + return err; + } + + /* Check original message */ + if (!memcmp(msg, orig, len + sizeof(u32_t))) { + BT_DBG("%s: Original message intact", prefix); + } else { + BT_ERR("%s: Original message modified", prefix); + BT_DBG("%s: orig %s", prefix, bt_hex(orig, sizeof(orig))); + BT_DBG("%s: msg %s", prefix, bt_hex(msg, sizeof(msg))); + return -1; + } + + if (!memcmp(out, sig, 12)) { + BT_DBG("%s: Success", prefix); + } else { + BT_ERR("%s: Failed", prefix); + return -1; + } + + return 0; +} + +static int smp_sign_test(void) +{ + const u8_t sig1[] = { + 0x00, 0x00, 0x00, 0x00, 0xb3, 0xa8, 0x59, 0x41, + 0x27, 0xeb, 0xc2, 0xc0 + }; + const u8_t sig2[] = { + 0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x74, 0xf4, + 0x39, 0x2a, 0x23, 0x2a + }; + const u8_t sig3[] = { + 0x00, 0x00, 0x00, 0x00, 0xb7, 0xca, 0x94, 0xab, + 0x87, 0xc7, 0x82, 0x18 + }; + const u8_t sig4[] = { + 0x00, 0x00, 0x00, 0x00, 0x44, 0xe1, 0xe6, 0xce, + 0x1d, 0xf5, 0x13, 0x68 + }; + u8_t key_s[16]; + int err; + + /* Use the same key as aes-cmac but swap bytes */ + sys_memcpy_swap(key_s, key, 16); + + err = sign_test("Test sign0", key_s, M, 0, sig1); + if (err) { + return err; + } + + err = sign_test("Test sign16", key_s, M, 16, sig2); + if (err) { + return err; + } + + err = sign_test("Test sign40", key_s, M, 40, sig3); + if (err) { + return err; + } + + err = sign_test("Test sign64", key_s, M, 64, sig4); + if (err) { + return err; + } + + return 0; +} + +static int smp_f4_test(void) +{ + u8_t u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 }; + u8_t v[32] = { 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b, + 0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59, + 0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90, + 0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 }; + u8_t x[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + u8_t z = 0x00; + u8_t exp[16] = { 0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1, + 0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 }; + u8_t res[16]; + int err; + + err = smp_f4(u, v, x, z, res); + if (err) { + return err; + } + + if (memcmp(res, exp, 16)) { + return -EINVAL; + } + + return 0; +} + +static int smp_f5_test(void) +{ + u8_t w[32] = { 0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86, + 0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99, + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + u8_t n1[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + u8_t n2[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + bt_addr_le_t a1 = { .type = 0x00, + .a.val = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56 } }; + bt_addr_le_t a2 = { .type = 0x00, + .a.val = {0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7 } }; + u8_t exp_ltk[16] = { 0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05, + 0x98, 0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79, + 0x86, 0x69 }; + u8_t exp_mackey[16] = { 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, + 0xfd, 0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, + 0x65, 0x29 }; + u8_t mackey[16], ltk[16]; + int err; + + err = smp_f5(w, n1, n2, &a1, &a2, mackey, ltk); + if (err) { + return err; + } + + if (memcmp(mackey, exp_mackey, 16) || memcmp(ltk, exp_ltk, 16)) { + return -EINVAL; + } + + return 0; +} + +static int smp_f6_test(void) +{ + u8_t w[16] = { 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd, + 0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 }; + u8_t n1[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + u8_t n2[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + u8_t r[16] = { 0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08, + 0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 }; + u8_t io_cap[3] = { 0x02, 0x01, 0x01 }; + bt_addr_le_t a1 = { .type = 0x00, + .a.val = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56 } }; + bt_addr_le_t a2 = { .type = 0x00, + .a.val = {0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7 } }; + u8_t exp[16] = { 0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2, + 0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 }; + u8_t res[16]; + int err; + + err = smp_f6(w, n1, n2, r, io_cap, &a1, &a2, res); + if (err) + return err; + + if (memcmp(res, exp, 16)) + return -EINVAL; + + return 0; +} + +static int smp_g2_test(void) +{ + u8_t u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, + 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef, + 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e, + 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 }; + u8_t v[32] = { 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b, + 0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59, + 0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90, + 0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 }; + u8_t x[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff, + 0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 }; + u8_t y[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21, + 0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 }; + u32_t exp_val = 0x2f9ed5ba % 1000000; + u32_t val; + int err; + + err = smp_g2(u, v, x, y, &val); + if (err) { + return err; + } + + if (val != exp_val) { + return -EINVAL; + } + + return 0; +} + +#if defined(CONFIG_BT_BREDR) +static int smp_h6_test(void) +{ + u8_t w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + u8_t key_id[4] = { 0x72, 0x62, 0x65, 0x6c }; + u8_t exp_res[16] = { 0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8, + 0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d}; + u8_t res[16]; + int err; + + err = smp_h6(w, key_id, res); + if (err) { + return err; + } + + if (memcmp(res, exp_res, 16)) { + return -EINVAL; + } + + return 0; +} + +static int smp_h7_test(void) +{ + u8_t salt[16] = { 0x31, 0x70, 0x6d, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + u8_t w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + u8_t exp_res[16] = { 0x11, 0x70, 0xa5, 0x75, 0x2a, 0x8c, 0x99, 0xd2, + 0xec, 0xc0, 0xa3, 0xc6, 0x97, 0x35, 0x17, 0xfb}; + u8_t res[16]; + int err; + + err = smp_h7(salt, w, res); + if (err) { + return err; + } + + if (memcmp(res, exp_res, 16)) { + return -EINVAL; + } + + return 0; +} +#endif /* CONFIG_BT_BREDR */ + +static int smp_self_test(void) +{ + int err; + + err = smp_aes_cmac_test(); + if (err) { + BT_ERR("SMP AES-CMAC self tests failed"); + return err; + } + + err = smp_sign_test(); + if (err) { + BT_ERR("SMP signing self tests failed"); + return err; + } + + err = smp_f4_test(); + if (err) { + BT_ERR("SMP f4 self test failed"); + return err; + } + + err = smp_f5_test(); + if (err) { + BT_ERR("SMP f5 self test failed"); + return err; + } + + err = smp_f6_test(); + if (err) { + BT_ERR("SMP f6 self test failed"); + return err; + } + + err = smp_g2_test(); + if (err) { + BT_ERR("SMP g2 self test failed"); + return err; + } + +#if defined(CONFIG_BT_BREDR) + err = smp_h6_test(); + if (err) { + BT_ERR("SMP h6 self test failed"); + return err; + } + + err = smp_h7_test(); + if (err) { + BT_ERR("SMP h7 self test failed"); + return err; + } +#endif /* CONFIG_BT_BREDR */ + + return 0; +} +#else +static inline int smp_self_test(void) +{ + return 0; +} +#endif + +int bt_smp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey) +{ + struct bt_smp *smp; + u8_t err; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) { + return -EINVAL; + } + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + legacy_passkey_entry(smp, passkey); + return 0; + } +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + + smp->passkey = sys_cpu_to_le32(passkey); + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + err = smp_send_pairing_confirm(smp); + if (err) { + smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED); + return 0; + } + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM); + return 0; + } + + if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + atomic_test_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) { + err = smp_send_pairing_confirm(smp); + if (err) { + smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED); + return 0; + } + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + } + + return 0; +} + +int bt_smp_auth_passkey_confirm(struct bt_conn *conn) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) { + return -EINVAL; + } + + /* wait for DHKey being generated */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return 0; + } + + /* wait for remote DHKey Check */ + if (atomic_test_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT)) { + atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND); + return 0; + } + + if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_SEND)) { + u8_t err; + +#if defined(CONFIG_BT_CENTRAL) + if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + err = compute_and_send_master_dhcheck(smp); + if (err) { + smp_error(smp, err); + } + return 0; + } +#endif /* CONFIG_BT_CENTRAL */ + +#if defined(CONFIG_BT_PERIPHERAL) + err = compute_and_check_and_send_slave_dhcheck(smp); + if (err) { + smp_error(smp, err); + } +#endif /* CONFIG_BT_PERIPHERAL */ + } + + return 0; +} + +int bt_smp_le_oob_generate_sc_data(struct bt_le_oob_sc_data *le_sc_oob) +{ + int err; + + if (!sc_public_key) { + err = k_sem_take(&sc_local_pkey_ready, K_FOREVER); + if (err) { + return err; + } + } + + if (IS_ENABLED(CONFIG_BT_OOB_DATA_FIXED)) { + u8_t rand_num[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }; + + memcpy(le_sc_oob->r, rand_num, sizeof(le_sc_oob->r)); + } else { + err = bt_rand(le_sc_oob->r, 16); + if (err) { + return err; + } + } + + err = smp_f4(sc_public_key, sc_public_key, le_sc_oob->r, 0, + le_sc_oob->c); + if (err) { + return err; + } + + return 0; +} + +static bool le_sc_oob_data_check(struct bt_smp *smp, bool oobd_local_present, + bool oobd_remote_present) +{ + bool req_oob_present = le_sc_oob_data_req_check(smp); + bool rsp_oob_present = le_sc_oob_data_rsp_check(smp); + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + if ((req_oob_present != oobd_remote_present) && + (rsp_oob_present != oobd_local_present)) { + return false; + } + } else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + if ((req_oob_present != oobd_local_present) && + (rsp_oob_present != oobd_remote_present)) { + return false; + } + } + + return true; +} + +static int le_sc_oob_pairing_continue(struct bt_smp *smp) +{ + if (smp->oobd_remote) { + int err; + u8_t c[16]; + + err = smp_f4(smp->pkey, smp->pkey, smp->oobd_remote->r, 0, c); + if (err) { + return err; + } + + bool match = (memcmp(c, smp->oobd_remote->c, sizeof(c)) == 0); + + if (!match) { + smp_error(smp, BT_SMP_ERR_CONFIRM_FAILED); + return 0; + } + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM); + } else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) { + atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK); + atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT); + } + + return smp_send_pairing_random(smp); +} + +int bt_smp_le_oob_set_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data *oobd_local, + const struct bt_le_oob_sc_data *oobd_remote) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!le_sc_oob_data_check(smp, (oobd_local != NULL), + (oobd_remote != NULL))) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_OOB_PENDING)) { + return -EINVAL; + } + + smp->oobd_local = oobd_local; + smp->oobd_remote = oobd_remote; + + return le_sc_oob_pairing_continue(smp); +} + +int bt_smp_le_oob_get_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data **oobd_local, + const struct bt_le_oob_sc_data **oobd_remote) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!smp->oobd_local && !smp->oobd_remote) { + return -ESRCH; + } + + if (oobd_local) { + *oobd_local = smp->oobd_local; + } + + if (oobd_remote) { + *oobd_remote = smp->oobd_remote; + } + + return 0; +} + +int bt_smp_auth_cancel(struct bt_conn *conn) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) { + return -EINVAL; + } + + switch (smp->method) { + case PASSKEY_INPUT: + case PASSKEY_DISPLAY: + return smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED); + case PASSKEY_CONFIRM: + return smp_error(smp, BT_SMP_ERR_CONFIRM_FAILED); + case LE_SC_OOB: + return smp_error(smp, BT_SMP_ERR_OOB_NOT_AVAIL); + case JUST_WORKS: + return smp_error(smp, BT_SMP_ERR_UNSPECIFIED); + default: + return 0; + } +} + +#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) +int bt_smp_auth_pairing_confirm(struct bt_conn *conn) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) { + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_CENTRAL) && + conn->role == BT_CONN_ROLE_MASTER) { + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + return legacy_send_pairing_confirm(smp); + } + + if (!sc_public_key) { + atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND); + return 0; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); + return sc_send_public_key(smp); + } + +#if defined(CONFIG_BT_PERIPHERAL) + if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + atomic_set_bit(&smp->allowed_cmds, + BT_SMP_CMD_PAIRING_CONFIRM); + return send_pairing_rsp(smp); + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); + if (send_pairing_rsp(smp)) { + return -EIO; + } +#endif /* CONFIG_BT_PERIPHERAL */ + + return 0; +} +#else +int bt_smp_auth_pairing_confirm(struct bt_conn *conn) +{ + /* confirm_pairing will never be called in LE SC only mode */ + return -EINVAL; +} +#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */ + +#if defined(CONFIG_BT_FIXED_PASSKEY) +int bt_passkey_set(unsigned int passkey) +{ + if (passkey == BT_PASSKEY_INVALID) { + passkey = BT_PASSKEY_INVALID; + return 0; + } + + if (passkey > 999999) { + return -EINVAL; + } + + fixed_passkey = passkey; + return 0; +} +#endif /* CONFIG_BT_FIXED_PASSKEY */ + +int bt_smp_start_security(struct bt_conn *conn) +{ + switch (conn->role) { +#if defined(CONFIG_BT_CENTRAL) + case BT_HCI_ROLE_MASTER: + { + int err; + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return -ENOTCONN; + } + + if (!smp_keys_check(conn)) { + return smp_send_pairing_req(conn); + } + + /* pairing is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + return -EBUSY; + } + + /* Encryption is in progress */ + if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) { + return -EBUSY; + } + + /* LE SC LTK and legacy master LTK are stored in same place */ + err = bt_conn_le_start_encryption(conn, + conn->le.keys->ltk.rand, + conn->le.keys->ltk.ediv, + conn->le.keys->ltk.val, + conn->le.keys->enc_size); + if (err) { + return err; + } + + atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST); + atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING); + return 0; + } +#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_SMP */ +#if defined(CONFIG_BT_PERIPHERAL) + case BT_HCI_ROLE_SLAVE: + return smp_send_security_req(conn); +#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_SMP */ + default: + return -EINVAL; + } +} + +void bt_smp_update_keys(struct bt_conn *conn) +{ + struct bt_smp *smp; + + smp = smp_chan_get(conn); + if (!smp) { + return; + } + + if (!atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) { + return; + } + + /* + * If link was successfully encrypted cleanup old keys as from now on + * only keys distributed in this pairing or LTK from LE SC will be used. + */ + if (conn->le.keys) { + bt_keys_clear(conn->le.keys); + } + + conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst); + if (!conn->le.keys) { + BT_ERR("Unable to get keys for %s", + bt_addr_le_str(&conn->le.dst)); + smp_error(smp, BT_SMP_ERR_UNSPECIFIED); + return; + } + + /* mark keys as debug */ + if (atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY)) { + conn->le.keys->flags |= BT_KEYS_DEBUG; + } + + /* + * store key type deducted from pairing method used + * it is important to store it since type is used to determine + * security level upon encryption + */ + switch (smp->method) { + case PASSKEY_DISPLAY: + case PASSKEY_INPUT: + case PASSKEY_CONFIRM: + case LE_SC_OOB: + conn->le.keys->flags |= BT_KEYS_AUTHENTICATED; + break; + case JUST_WORKS: + default: + /* unauthenticated key, clear it */ + conn->le.keys->flags &= ~BT_KEYS_AUTHENTICATED; + break; + } + + conn->le.keys->enc_size = get_encryption_key_size(smp); + + /* + * Store LTK if LE SC is used, this is safe since LE SC is mutually + * exclusive with legacy pairing. Other keys are added on keys + * distribution. + */ + if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) { + conn->le.keys->flags |= BT_KEYS_SC; + + if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) { + bt_keys_add_type(conn->le.keys, BT_KEYS_LTK_P256); + memcpy(conn->le.keys->ltk.val, smp->tk, + sizeof(conn->le.keys->ltk.val)); + (void)memset(conn->le.keys->ltk.rand, 0, + sizeof(conn->le.keys->ltk.rand)); + (void)memset(conn->le.keys->ltk.ediv, 0, + sizeof(conn->le.keys->ltk.ediv)); + } + } else { + conn->le.keys->flags &= ~BT_KEYS_SC; + } +} + +static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + int i; + static struct bt_l2cap_chan_ops ops = { + .connected = bt_smp_connected, + .disconnected = bt_smp_disconnected, + .encrypt_change = bt_smp_encrypt_change, + .recv = bt_smp_recv, + }; + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) { + struct bt_smp *smp = &bt_smp_pool[i]; + + if (smp->chan.chan.conn) { + continue; + } + + smp->chan.chan.ops = &ops; + + *chan = &smp->chan.chan; + + return 0; + } + + BT_ERR("No available SMP context for conn %p", conn); + + return -ENOMEM; +} + +static bool le_sc_supported(void) +{ + #ifdef CONFIG_BT_REMOTE_CONTROL + return false; + #endif + /* + * If controller based ECC is to be used it must support + * "LE Read Local P-256 Public Key" and "LE Generate DH Key" commands. + * Otherwise LE SC are not supported. + */ + return BT_CMD_TEST(bt_dev.supported_commands, 34, 1) && + BT_CMD_TEST(bt_dev.supported_commands, 34, 2); +} + +BT_L2CAP_CHANNEL_DEFINE(smp_fixed_chan, BT_L2CAP_CID_SMP, bt_smp_accept); +#if defined(CONFIG_BT_BREDR) +BT_L2CAP_CHANNEL_DEFINE(smp_br_fixed_chan, BT_L2CAP_CID_BR_SMP, + bt_smp_br_accept); +#endif /* CONFIG_BT_BREDR */ + +int bt_smp_init(void) +{ + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + static struct bt_l2cap_fixed_chan chan = { + .cid = BT_L2CAP_CID_SMP, + .accept = bt_smp_accept, + }; + #endif + static struct bt_pub_key_cb pub_key_cb = { + .func = bt_smp_pkey_ready, + }; + #if defined(BFLB_BLE) + k_sem_init(&sc_local_pkey_ready, 0, 1); + #endif + + sc_supported = le_sc_supported(); + if (IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && !sc_supported) { + BT_ERR("SC Pair Only Mode selected but LE SC not supported"); + return -ENOENT; + } + + #if defined(BFLB_BLE_DISABLE_STATIC_CHANNEL) + bt_l2cap_le_fixed_chan_register(&chan); + #endif + + BT_DBG("LE SC %s", sc_supported ? "enabled" : "disabled"); + + bt_pub_key_gen(&pub_key_cb); + + return smp_self_test(); +} diff --git a/components/ble/ble_stack/host/smp.h b/components/ble/ble_stack/host/smp.h new file mode 100644 index 00000000..9a33469a --- /dev/null +++ b/components/ble/ble_stack/host/smp.h @@ -0,0 +1,187 @@ +/** + * @file smp.h + * Security Manager Protocol implementation header + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +struct bt_smp_hdr { + u8_t code; +} __packed; + +#define BT_SMP_ERR_PASSKEY_ENTRY_FAILED 0x01 +#define BT_SMP_ERR_OOB_NOT_AVAIL 0x02 +#define BT_SMP_ERR_AUTH_REQUIREMENTS 0x03 +#define BT_SMP_ERR_CONFIRM_FAILED 0x04 +#define BT_SMP_ERR_PAIRING_NOTSUPP 0x05 +#define BT_SMP_ERR_ENC_KEY_SIZE 0x06 +#define BT_SMP_ERR_CMD_NOTSUPP 0x07 +#define BT_SMP_ERR_UNSPECIFIED 0x08 +#define BT_SMP_ERR_REPEATED_ATTEMPTS 0x09 +#define BT_SMP_ERR_INVALID_PARAMS 0x0a +#define BT_SMP_ERR_DHKEY_CHECK_FAILED 0x0b +#define BT_SMP_ERR_NUMERIC_COMP_FAILED 0x0c +#define BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS 0x0d +#define BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED 0x0e + +#define BT_SMP_IO_DISPLAY_ONLY 0x00 +#define BT_SMP_IO_DISPLAY_YESNO 0x01 +#define BT_SMP_IO_KEYBOARD_ONLY 0x02 +#define BT_SMP_IO_NO_INPUT_OUTPUT 0x03 +#define BT_SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define BT_SMP_OOB_DATA_MASK 0x01 +#define BT_SMP_OOB_NOT_PRESENT 0x00 +#define BT_SMP_OOB_PRESENT 0x01 + +#define BT_SMP_MIN_ENC_KEY_SIZE 7 +#define BT_SMP_MAX_ENC_KEY_SIZE 16 + +#define BT_SMP_DIST_ENC_KEY 0x01 +#define BT_SMP_DIST_ID_KEY 0x02 +#define BT_SMP_DIST_SIGN 0x04 +#define BT_SMP_DIST_LINK_KEY 0x08 + +#define BT_SMP_DIST_MASK 0x0f + +#define BT_SMP_AUTH_NONE 0x00 +#define BT_SMP_AUTH_BONDING 0x01 +#define BT_SMP_AUTH_MITM 0x04 +#define BT_SMP_AUTH_SC 0x08 +#define BT_SMP_AUTH_KEYPRESS 0x10 +#define BT_SMP_AUTH_CT2 0x20 + +#define BT_SMP_CMD_PAIRING_REQ 0x01 +#define BT_SMP_CMD_PAIRING_RSP 0x02 +struct bt_smp_pairing { + u8_t io_capability; + u8_t oob_flag; + u8_t auth_req; + u8_t max_key_size; + u8_t init_key_dist; + u8_t resp_key_dist; +} __packed; + +#define BT_SMP_CMD_PAIRING_CONFIRM 0x03 +struct bt_smp_pairing_confirm { + u8_t val[16]; +} __packed; + +#define BT_SMP_CMD_PAIRING_RANDOM 0x04 +struct bt_smp_pairing_random { + u8_t val[16]; +} __packed; + +#define BT_SMP_CMD_PAIRING_FAIL 0x05 +struct bt_smp_pairing_fail { + u8_t reason; +} __packed; + +#define BT_SMP_CMD_ENCRYPT_INFO 0x06 +struct bt_smp_encrypt_info { + u8_t ltk[16]; +} __packed; + +#define BT_SMP_CMD_MASTER_IDENT 0x07 +struct bt_smp_master_ident { + u8_t ediv[2]; + u8_t rand[8]; +} __packed; + +#define BT_SMP_CMD_IDENT_INFO 0x08 +struct bt_smp_ident_info { + u8_t irk[16]; +} __packed; + +#define BT_SMP_CMD_IDENT_ADDR_INFO 0x09 +struct bt_smp_ident_addr_info { + bt_addr_le_t addr; +} __packed; + +#define BT_SMP_CMD_SIGNING_INFO 0x0a +struct bt_smp_signing_info { + u8_t csrk[16]; +} __packed; + +#define BT_SMP_CMD_SECURITY_REQUEST 0x0b +struct bt_smp_security_request { + u8_t auth_req; +} __packed; + +#define BT_SMP_CMD_PUBLIC_KEY 0x0c +struct bt_smp_public_key { + u8_t x[32]; + u8_t y[32]; +} __packed; + +#define BT_SMP_DHKEY_CHECK 0x0d +struct bt_smp_dhkey_check { + u8_t e[16]; +} __packed; + +int bt_smp_start_security(struct bt_conn *conn); +bool bt_smp_request_ltk(struct bt_conn *conn, u64_t rand, u16_t ediv, + u8_t *ltk); + +void bt_smp_update_keys(struct bt_conn *conn); + +int bt_smp_br_send_pairing_req(struct bt_conn *conn); + +int bt_smp_init(void); + +int bt_smp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey); +int bt_smp_auth_passkey_confirm(struct bt_conn *conn); +int bt_smp_auth_pairing_confirm(struct bt_conn *conn); +int bt_smp_auth_cancel(struct bt_conn *conn); + +int bt_smp_le_oob_generate_sc_data(struct bt_le_oob_sc_data *le_sc_oob); +int bt_smp_le_oob_set_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data *oobd_local, + const struct bt_le_oob_sc_data *oobd_remote); +int bt_smp_le_oob_get_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data **oobd_local, + const struct bt_le_oob_sc_data **oobd_remote); + +/** brief Verify signed message + * + * @param conn Bluetooth connection + * @param buf received packet buffer with message and signature + * + * @return 0 in success, error code otherwise + */ +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf); + +/** brief Sign message + * + * @param conn Bluetooth connection + * @param buf message buffer + * + * @return 0 in success, error code otherwise + */ +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf); + +#if defined(CONFIG_AUTO_PTS) +int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk); +#endif + +#if defined(CONFIG_BLE_AT_CMD) +struct smp_parameters{ + u8_t auth; + u8_t iocap; + u16_t key_size; + u8_t init_key; + u8_t rsp_key; + u8_t set; +}; + +struct smp_parameters user_smp_paras; +int ble_set_smp_paramters(const struct smp_parameters *paras); +int ble_get_smp_paramters(const struct bt_conn *conn,struct smp_parameters *paras); +#endif +#if defined(BFLB_BLE_SMP_LOCAL_AUTH) +void smp_set_auth(u8_t auth); +#endif diff --git a/components/ble/ble_stack/host/smp_null.c b/components/ble/ble_stack/host/smp_null.c new file mode 100644 index 00000000..2dbd473e --- /dev/null +++ b/components/ble/ble_stack/host/smp_null.c @@ -0,0 +1,101 @@ +/** + * @file smp_null.c + * Security Manager Protocol stub + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include <../include/bluetooth/buf.h> + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE) +#define LOG_MODULE_NAME bt_smp +#include "log.h" + +#include "hci_core.h" +#include "conn_internal.h" +#include "l2cap_internal.h" +#include "smp.h" + +static struct bt_l2cap_le_chan bt_smp_pool[CONFIG_BT_MAX_CONN]; + +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} + +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} + +static int bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + struct bt_conn *conn = chan->conn; + struct bt_smp_pairing_fail *rsp; + struct bt_smp_hdr *hdr; + + /* If a device does not support pairing then it shall respond with + * a Pairing Failed command with the reason set to "Pairing Not + * Supported" when any command is received. + * Core Specification Vol. 3, Part H, 3.3 + */ + + buf = bt_l2cap_create_pdu(NULL, 0); + /* NULL is not a possible return due to K_FOREVER */ + + hdr = net_buf_add(buf, sizeof(*hdr)); + hdr->code = BT_SMP_CMD_PAIRING_FAIL; + + rsp = net_buf_add(buf, sizeof(*rsp)); + rsp->reason = BT_SMP_ERR_PAIRING_NOTSUPP; + + bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); + + return 0; +} + +static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) +{ + int i; + static struct bt_l2cap_chan_ops ops = { + .recv = bt_smp_recv, + }; + + BT_DBG("conn %p handle %u", conn, conn->handle); + + for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) { + struct bt_l2cap_le_chan *smp = &bt_smp_pool[i]; + + if (smp->chan.conn) { + continue; + } + + smp->chan.ops = &ops; + + *chan = &smp->chan; + + return 0; + } + + BT_ERR("No available SMP context for conn %p", conn); + + return -ENOMEM; +} + +BT_L2CAP_CHANNEL_DEFINE(smp_fixed_chan, BT_L2CAP_CID_SMP, bt_smp_accept); + +int bt_smp_init(void) +{ + return 0; +} diff --git a/components/ble/ble_stack/host/uuid.c b/components/ble/ble_stack/host/uuid.c new file mode 100644 index 00000000..2d5fb5b4 --- /dev/null +++ b/components/ble/ble_stack/host/uuid.c @@ -0,0 +1,139 @@ +/* uuid.c - Bluetooth UUID handling */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +#define UUID_16_BASE_OFFSET 12 + +/* TODO: Decide whether to continue using BLE format or switch to RFC 4122 */ + +/* Base UUID : 0000[0000]-0000-1000-8000-00805F9B34FB + * 0x2800 : 0000[2800]-0000-1000-8000-00805F9B34FB + * little endian 0x2800 : [00 28] -> no swapping required + * big endian 0x2800 : [28 00] -> swapping required + */ +static const struct bt_uuid_128 uuid128_base = { + .uuid = { BT_UUID_TYPE_128 }, + .val = { BT_UUID_128_ENCODE( + 0x00000000, 0x0000, 0x1000, 0x8000, 0x00805F9B34FB) } +}; + +static void uuid_to_uuid128(const struct bt_uuid *src, struct bt_uuid_128 *dst) +{ + switch (src->type) { + case BT_UUID_TYPE_16: + *dst = uuid128_base; + sys_put_le16(BT_UUID_16(src)->val, + &dst->val[UUID_16_BASE_OFFSET]); + return; + case BT_UUID_TYPE_32: + *dst = uuid128_base; + sys_put_le32(BT_UUID_32(src)->val, + &dst->val[UUID_16_BASE_OFFSET]); + return; + case BT_UUID_TYPE_128: + memcpy(dst, src, sizeof(*dst)); + return; + } +} + +static int uuid128_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) +{ + struct bt_uuid_128 uuid1, uuid2; + + uuid_to_uuid128(u1, &uuid1); + uuid_to_uuid128(u2, &uuid2); + + return memcmp(uuid1.val, uuid2.val, 16); +} + +int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) +{ + /* Convert to 128 bit if types don't match */ + if (u1->type != u2->type) { + return uuid128_cmp(u1, u2); + } + + switch (u1->type) { + case BT_UUID_TYPE_16: + return (int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val; + case BT_UUID_TYPE_32: + return (int)BT_UUID_32(u1)->val - (int)BT_UUID_32(u2)->val; + case BT_UUID_TYPE_128: + return memcmp(BT_UUID_128(u1)->val, BT_UUID_128(u2)->val, 16); + } + + return -EINVAL; +} + +bool bt_uuid_create(struct bt_uuid *uuid, const u8_t *data, u8_t data_len) +{ + /* Copy UUID from packet data/internal variable to internal bt_uuid */ + switch (data_len) { + case 2: + uuid->type = BT_UUID_TYPE_16; + BT_UUID_16(uuid)->val = sys_get_le16(data); + break; + case 4: + uuid->type = BT_UUID_TYPE_32; + BT_UUID_32(uuid)->val = sys_get_le32(data); + break; + case 16: + uuid->type = BT_UUID_TYPE_128; + memcpy(&BT_UUID_128(uuid)->val, data, 16); + break; + default: + return false; + } + return true; +} + +#if defined(CONFIG_BT_DEBUG) +void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len) +{ + u32_t tmp1, tmp5; + u16_t tmp0, tmp2, tmp3, tmp4; + + switch (uuid->type) { + case BT_UUID_TYPE_16: + snprintk(str, len, "%04x", BT_UUID_16(uuid)->val); + break; + case BT_UUID_TYPE_32: + snprintk(str, len, "%04x", BT_UUID_32(uuid)->val); + break; + case BT_UUID_TYPE_128: + memcpy(&tmp0, &BT_UUID_128(uuid)->val[0], sizeof(tmp0)); + memcpy(&tmp1, &BT_UUID_128(uuid)->val[2], sizeof(tmp1)); + memcpy(&tmp2, &BT_UUID_128(uuid)->val[6], sizeof(tmp2)); + memcpy(&tmp3, &BT_UUID_128(uuid)->val[8], sizeof(tmp3)); + memcpy(&tmp4, &BT_UUID_128(uuid)->val[10], sizeof(tmp4)); + memcpy(&tmp5, &BT_UUID_128(uuid)->val[12], sizeof(tmp5)); + + snprintk(str, len, "%08x-%04x-%04x-%04x-%08x%04x", + tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); + break; + default: + (void)memset(str, 0, len); + return; + } +} + +const char *bt_uuid_str_real(const struct bt_uuid *uuid) +{ + static char str[37]; + + bt_uuid_to_str(uuid, str, sizeof(str)); + + return str; +} +#endif /* CONFIG_BT_DEBUG */ diff --git a/components/ble/ble_stack/include/bluetooth/a2dp-codec.h b/components/ble/ble_stack/include/bluetooth/a2dp-codec.h new file mode 100644 index 00000000..3c8022d6 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/a2dp-codec.h @@ -0,0 +1,73 @@ +/** @file + * @brief Advance Audio Distribution Profile - SBC Codec header. + */ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2015-2016 Intel 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 ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Sampling Frequency */ +#define A2DP_SBC_SAMP_FREQ_16000 BIT(7) +#define A2DP_SBC_SAMP_FREQ_32000 BIT(6) +#define A2DP_SBC_SAMP_FREQ_44100 BIT(5) +#define A2DP_SBC_SAMP_FREQ_48000 BIT(4) + +/* Channel Mode */ +#define A2DP_SBC_CH_MODE_MONO BIT(3) +#define A2DP_SBC_CH_MODE_DUAL BIT(2) +#define A2DP_SBC_CH_MODE_STREO BIT(1) +#define A2DP_SBC_CH_MODE_JOINT BIT(0) + +/* Block Length */ +#define A2DP_SBC_BLK_LEN_4 BIT(7) +#define A2DP_SBC_BLK_LEN_8 BIT(6) +#define A2DP_SBC_BLK_LEN_12 BIT(5) +#define A2DP_SBC_BLK_LEN_16 BIT(4) + +/* Subbands */ +#define A2DP_SBC_SUBBAND_4 BIT(3) +#define A2DP_SBC_SUBBAND_8 BIT(2) + +/* Allocation Method */ +#define A2DP_SBC_ALLOC_MTHD_SNR BIT(1) +#define A2DP_SBC_ALLOC_MTHD_LOUDNESS BIT(0) + +#define BT_A2DP_SBC_SAMP_FREQ(preset) ((preset->config[0] >> 4) & 0x0f) +#define BT_A2DP_SBC_CHAN_MODE(preset) ((preset->config[0]) & 0x0f) +#define BT_A2DP_SBC_BLK_LEN(preset) ((preset->config[1] >> 4) & 0x0f) +#define BT_A2DP_SBC_SUB_BAND(preset) ((preset->config[1] >> 2) & 0x03) +#define BT_A2DP_SBC_ALLOC_MTHD(preset) ((preset->config[1]) & 0x03) + +/** @brief SBC Codec */ +struct bt_a2dp_codec_sbc_params { + /** First two octets of configuration */ + uint8_t config[2]; + /** Minimum Bitpool Value */ + uint8_t min_bitpool; + /** Maximum Bitpool Value */ + uint8_t max_bitpool; +} __packed; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/a2dp.h b/components/ble/ble_stack/include/bluetooth/a2dp.h new file mode 100644 index 00000000..db2bed32 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/a2dp.h @@ -0,0 +1,133 @@ +/** @file + * @brief Advance Audio Distribution Profile header. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Stream Structure */ +struct bt_a2dp_stream { + /* TODO */ +}; + +/** @brief Codec ID */ +enum bt_a2dp_codec_id { + /** Codec SBC */ + BT_A2DP_SBC = 0x00, + /** Codec MPEG-1 */ + BT_A2DP_MPEG1 = 0x01, + /** Codec MPEG-2 */ + BT_A2DP_MPEG2 = 0x02, + /** Codec ATRAC */ + BT_A2DP_ATRAC = 0x04, + /** Codec Non-A2DP */ + BT_A2DP_VENDOR = 0xff +}; + +/** @brief Media Codec Type */ +enum MEDIA_CODEC_TYPE { + /** SBC codec type */ + BT_A2DP_CODEC_TYPE_SBC = 0x00, + /** AAC codec type */ + BT_A2DP_CODEC_TYPE_AAC = 0x02, + /** AAC codec type */ + BT_A2DP_CODEC_TYPE_VENDOR = 0xff, +}; + +/** @brief Preset for the endpoint */ +struct bt_a2dp_preset { + /** Length of preset */ + uint8_t len; + /** Preset */ + uint8_t preset[0]; +}; + +/** @brief Stream End Point */ +struct bt_a2dp_endpoint { + /** Code ID */ + uint8_t codec_id; + /** Stream End Point Information */ + struct bt_avdtp_seid_lsep info; + /** Pointer to preset codec chosen */ + struct bt_a2dp_preset *preset; + /** Capabilities */ + struct bt_a2dp_preset *caps; +}; + +/** @brief Stream End Point Media Type */ +enum MEDIA_TYPE { + /** Audio Media Type */ + BT_A2DP_AUDIO = 0x00, + /** Video Media Type */ + BT_A2DP_VIDEO = 0x01, + /** Multimedia Media Type */ + BT_A2DP_MULTIMEDIA = 0x02 +}; + +/** @brief Stream End Point Role */ +enum ROLE_TYPE { + /** Source Role */ + BT_A2DP_SOURCE = 0, + /** Sink Role */ + BT_A2DP_SINK = 1 +}; + +/** @brief A2DP structure */ +struct bt_a2dp; + +/** @brief A2DP Connect. + * + * This function is to be called after the conn parameter is obtained by + * performing a GAP procedure. The API is to be used to establish A2DP + * connection between devices. + * + * @param conn Pointer to bt_conn structure. + * + * @return pointer to struct bt_a2dp in case of success or NULL in case + * of error. + */ +struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn); + +/** @brief Endpoint Registration. + * + * This function is used for registering the stream end points. The user has + * to take care of allocating the memory, the preset pointer and then pass the + * required arguments. Also, only one sep can be registered at a time. + * + * @param endpoint Pointer to bt_a2dp_endpoint structure. + * @param media_type Media type that the Endpoint is. + * @param role Role of Endpoint. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint, + uint8_t media_type, uint8_t role); + +/** @brief SBC decode init. + * + * @return 0 in case of success and error code in case of error. + */ +int a2dp_sbc_decode_init(); + +/** @brief SBC decode process. + * + * @return 0 in case of success and error code in case of error. + */ +int a2dp_sbc_decode_process(uint8_t* media_data, uint16_t data_len); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_A2DP_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/addr.h b/components/ble/ble_stack/include/bluetooth/addr.h new file mode 100644 index 00000000..1e1abd30 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/addr.h @@ -0,0 +1,101 @@ +/** @file + * @brief Bluetooth device address definitions and utilities. + */ + +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BT_ADDR_LE_PUBLIC 0x00 +#define BT_ADDR_LE_RANDOM 0x01 +#define BT_ADDR_LE_PUBLIC_ID 0x02 +#define BT_ADDR_LE_RANDOM_ID 0x03 + +#if defined(CONFIG_BT_STACK_PTS) +//for app layer to deliver the address type:non rpa ,rpa +#define BT_ADDR_TYPE_NON_RPA 0x01 +#define BT_ADDR_TYPE_RPA 0x02 +#endif + +/** Bluetooth Device Address */ +typedef struct { + u8_t val[6]; +} bt_addr_t; + +/** Bluetooth LE Device Address */ +typedef struct { + u8_t type; + bt_addr_t a; +} bt_addr_le_t; + +#define BT_ADDR_ANY (&(bt_addr_t) { { 0, 0, 0, 0, 0, 0 } }) +#define BT_ADDR_NONE (&(bt_addr_t) { \ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }) +#define BT_ADDR_LE_ANY (&(bt_addr_le_t) { 0, { { 0, 0, 0, 0, 0, 0 } } }) +#define BT_ADDR_LE_NONE (&(bt_addr_le_t) { 0, \ + { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }) + +static inline int bt_addr_cmp(const bt_addr_t *a, const bt_addr_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline int bt_addr_le_cmp(const bt_addr_le_t *a, const bt_addr_le_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline void bt_addr_copy(bt_addr_t *dst, const bt_addr_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline void bt_addr_le_copy(bt_addr_le_t *dst, const bt_addr_le_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +#define BT_ADDR_IS_RPA(a) (((a)->val[5] & 0xc0) == 0x40) +#define BT_ADDR_IS_NRPA(a) (((a)->val[5] & 0xc0) == 0x00) +#define BT_ADDR_IS_STATIC(a) (((a)->val[5] & 0xc0) == 0xc0) + +#define BT_ADDR_SET_RPA(a) ((a)->val[5] = (((a)->val[5] & 0x3f) | 0x40)) +#define BT_ADDR_SET_NRPA(a) ((a)->val[5] &= 0x3f) +#define BT_ADDR_SET_STATIC(a) ((a)->val[5] |= 0xc0) + +int bt_addr_le_create_nrpa(bt_addr_le_t *addr); +int bt_addr_le_create_static(bt_addr_le_t *addr); + +static inline bool bt_addr_le_is_rpa(const bt_addr_le_t *addr) +{ + if (addr->type != BT_ADDR_LE_RANDOM) { + return false; + } + + return BT_ADDR_IS_RPA(&addr->a); +} + +static inline bool bt_addr_le_is_identity(const bt_addr_le_t *addr) +{ + if (addr->type == BT_ADDR_LE_PUBLIC) { + return true; + } + + return BT_ADDR_IS_STATIC(&addr->a); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ADDR_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/att.h b/components/ble/ble_stack/include/bluetooth/att.h new file mode 100644 index 00000000..f2ee968b --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/att.h @@ -0,0 +1,68 @@ +/** @file + * @brief Attribute Protocol handling. + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Error codes for Error response PDU */ +#define BT_ATT_ERR_INVALID_HANDLE 0x01 +#define BT_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BT_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BT_ATT_ERR_INVALID_PDU 0x04 +#define BT_ATT_ERR_AUTHENTICATION 0x05 +#define BT_ATT_ERR_NOT_SUPPORTED 0x06 +#define BT_ATT_ERR_INVALID_OFFSET 0x07 +#define BT_ATT_ERR_AUTHORIZATION 0x08 +#define BT_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BT_ATT_ERR_ATTRIBUTE_NOT_FOUND 0x0a +#define BT_ATT_ERR_ATTRIBUTE_NOT_LONG 0x0b +#define BT_ATT_ERR_ENCRYPTION_KEY_SIZE 0x0c +#define BT_ATT_ERR_INVALID_ATTRIBUTE_LEN 0x0d +#define BT_ATT_ERR_UNLIKELY 0x0e +#define BT_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f +#define BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE 0x10 +#define BT_ATT_ERR_INSUFFICIENT_RESOURCES 0x11 +#define BT_ATT_ERR_DB_OUT_OF_SYNC 0x12 +#define BT_ATT_ERR_VALUE_NOT_ALLOWED 0x13 + +/* Common Profile Error Codes (from CSS) */ +#define BT_ATT_ERR_WRITE_REQ_REJECTED 0xfc +#define BT_ATT_ERR_CCC_IMPROPER_CONF 0xfd +#define BT_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe +#define BT_ATT_ERR_OUT_OF_RANGE 0xff + +typedef void (*bt_att_func_t)(struct bt_conn *conn, u8_t err, + const void *pdu, u16_t length, + void *user_data); +typedef void (*bt_att_destroy_t)(void *user_data); + +/* ATT request context */ +struct bt_att_req { + sys_snode_t node; + bt_att_func_t func; + bt_att_destroy_t destroy; + struct net_buf_simple_state state; + struct net_buf *buf; +#if defined(CONFIG_BT_SMP) + bool retrying; +#endif /* CONFIG_BT_SMP */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ATT_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/avdtp.h b/components/ble/ble_stack/include/bluetooth/avdtp.h new file mode 100644 index 00000000..574b2cd0 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/avdtp.h @@ -0,0 +1,54 @@ +/** @file + * @brief Audio/Video Distribution Transport Protocol header. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_AVDTP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_AVDTP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief AVDTP SEID Information */ +struct bt_avdtp_seid_info { + /** Stream End Point ID */ + uint8_t id:6; + /** End Point usage status */ + uint8_t inuse:1; + /** Reserved */ + uint8_t rfa0:1; + /** Media-type of the End Point */ + uint8_t media_type:4; + /** TSEP of the End Point */ + uint8_t tsep:1; + /** Reserved */ + uint8_t rfa1:3; +} __packed; + +/** @brief AVDTP Local SEP*/ +struct bt_avdtp_seid_lsep { + /** Stream End Point information */ + struct bt_avdtp_seid_info sep; + /** Pointer to next local Stream End Point structure */ + struct bt_avdtp_seid_lsep *next; +}; + +/** @brief AVDTP Stream */ +struct bt_avdtp_stream { + struct bt_l2cap_br_chan chan; /* Transport Channel*/ + struct bt_avdtp_seid_info lsep; /* Configured Local SEP */ + struct bt_avdtp_seid_info rsep; /* Configured Remote SEP*/ + uint8_t state; /* current state of the stream */ + struct bt_avdtp_stream *next; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AVDTP_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/bluetooth.h b/components/ble/ble_stack/include/bluetooth/bluetooth.h new file mode 100644 index 00000000..38239d1c --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/bluetooth.h @@ -0,0 +1,870 @@ +/** @file + * @brief Bluetooth subsystem core APIs. + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_BLUETOOTH_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_BLUETOOTH_H_ + +/** + * @brief Bluetooth APIs + * @defgroup bluetooth Bluetooth APIs + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generic Access Profile + * @defgroup bt_gap Generic Access Profile + * @ingroup bluetooth + * @{ + */ + +/** @def BT_ID_DEFAULT + * + * Convenience macro for specifying the default identity. This helps + * make the code more readable, especially when only one identity is + * supported. + */ +#define BT_ID_DEFAULT 0 + +/** + * @typedef bt_ready_cb_t + * @brief Callback for notifying that Bluetooth has been enabled. + * + * @param err zero on success or (negative) error code otherwise. + */ +typedef void (*bt_ready_cb_t)(int err); + +/** @brief Enable Bluetooth + * + * Enable Bluetooth. Must be the called before any calls that + * require communication with the local Bluetooth hardware. + * + * @param cb Callback to notify completion or NULL to perform the + * enabling synchronously. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_enable(bt_ready_cb_t cb); + +/** @brief Set Bluetooth Device Name + * + * Set Bluetooth GAP Device Name. + * + * @param name New name + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_set_name(const char *name); + +/** @brief Get Bluetooth Device Name + * + * Get Bluetooth GAP Device Name. + * + * @return Bluetooth Device Name + */ +const char *bt_get_name(void); + +/** @brief Set the local Identity Address + * + * Allows setting the local Identity Address from the application. + * This API must be called before calling bt_enable(). Calling it at any + * other time will cause it to fail. In most cases the application doesn't + * need to use this API, however there are a few valid cases where + * it can be useful (such as for testing). + * + * At the moment, the given address must be a static random address. In the + * future support for public addresses may be added. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_set_id_addr(const bt_addr_le_t *addr); + +/** @brief Get the currently configured identities. + * + * Returns an array of the currently configured identity addresses. To + * make sure all available identities can be retrieved, the number of + * elements in the @a addrs array should be CONFIG_BT_ID_MAX. The identity + * identifier that some APIs expect (such as advertising parameters) is + * simply the index of the identity in the @a addrs array. + * + * Note: Deleted identities may show up as BT_LE_ADDR_ANY in the returned + * array. + * + * @param addrs Array where to store the configured identities. + * @param count Should be initialized to the array size. Once the function + * returns it will contain the number of returned identities. + */ +void bt_id_get(bt_addr_le_t *addrs, size_t *count); + +/** @brief Create a new identity. + * + * Create a new identity using the given address and IRK. This function + * can be called before calling bt_enable(), in which case it can be used + * to override the controller's public address (in case it has one). However, + * the new identity will only be stored persistently in flash when this API + * is used after bt_enable(). The reason is that the persistent settings + * are loaded after bt_enable() and would therefore cause potential conflicts + * with the stack blindly overwriting what's stored in flash. The identity + * will also not be written to flash in case a pre-defined address is + * provided, since in such a situation the app clearly has some place it got + * the address from and will be able to repeat the procedure on every power + * cycle, i.e. it would be redundant to also store the information in flash. + * + * If the application wants to have the stack randomly generate identities + * and store them in flash for later recovery, the way to do it would be + * to first initialize the stack (using bt_enable), then call settings_load(), + * and after that check with bt_id_get() how many identities were recovered. + * If an insufficient amount of identities were recovered the app may then + * call bt_id_create() to create new ones. + * + * @param addr Address to use for the new identity. If NULL or initialized + * to BT_ADDR_LE_ANY the stack will generate a new static + * random address for the identity and copy it to the given + * parameter upon return from this function (in case the + * parameter was non-NULL). + * @param irk Identity Resolving Key (16 bytes) to be used with this + * identity. If set to all zeroes or NULL, the stack will + * generate a random IRK for the identity and copy it back + * to the parameter upon return from this function (in case + * the parameter was non-NULL). If privacy support + * (CONFIG_BT_PRIVACY) is not enabled this parameter must + * be NULL. + * + * @return Identity identifier (>= 0) in case of success, or a negative + * error code on failure. + */ +int bt_id_create(bt_addr_le_t *addr, u8_t *irk); + +/** @brief Reset/reclaim an identity for reuse. + * + * The semantics of the @a addr and @a irk parameters of this function + * are the same as with bt_id_create(). The difference is the first + * @a id parameter that needs to be an existing identity (if it doesn't + * exist this function will return an error). When given an existing + * identity this function will disconnect any connections created using it, + * remove any pairing keys or other data associated with it, and then create + * a new identity in the same slot, based on the @a addr and @a irk + * parameters. + * + * Note: the default identity (BT_ID_DEFAULT) cannot be reset, i.e. this + * API will return an error if asked to do that. + * + * @param id Existing identity identifier. + * @param addr Address to use for the new identity. If NULL or initialized + * to BT_ADDR_LE_ANY the stack will generate a new static + * random address for the identity and copy it to the given + * parameter upon return from this function (in case the + * parameter was non-NULL). + * @param irk Identity Resolving Key (16 bytes) to be used with this + * identity. If set to all zeroes or NULL, the stack will + * generate a random IRK for the identity and copy it back + * to the parameter upon return from this function (in case + * the parameter was non-NULL). If privacy support + * (CONFIG_BT_PRIVACY) is not enabled this parameter must + * be NULL. + * + * @return Identity identifier (>= 0) in case of success, or a negative + * error code on failure. + */ +int bt_id_reset(u8_t id, bt_addr_le_t *addr, u8_t *irk); + +/** @brief Delete an identity. + * + * When given a valid identity this function will disconnect any connections + * created using it, remove any pairing keys or other data associated with + * it, and then flag is as deleted, so that it can not be used for any + * operations. To take back into use the slot the identity was occupying the + * bt_id_reset() API needs to be used. + * + * Note: the default identity (BT_ID_DEFAULT) cannot be deleted, i.e. this + * API will return an error if asked to do that. + * + * @param id Existing identity identifier. + * + * @return 0 in case of success, or a negative error code on failure. + */ +int bt_id_delete(u8_t id); + +/* Advertising API */ + +/** Description of different data types that can be encoded into + * advertising data. Used to form arrays that are passed to the + * bt_le_adv_start() function. + */ +struct bt_data { + u8_t type; + u8_t data_len; + const u8_t *data; +}; + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _data Pointer to the data field payload + * @param _data_len Number of bytes behind the _data pointer + */ +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (const u8_t *)(_data), \ + } + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _bytes Variable number of single-byte parameters + */ +#define BT_DATA_BYTES(_type, _bytes...) \ + BT_DATA(_type, ((u8_t []) { _bytes }), \ + sizeof((u8_t []) { _bytes })) + +/** Advertising options */ +enum { + /** Convenience value when no options are specified. */ + BT_LE_ADV_OPT_NONE = 0, + + /** Advertise as connectable. Type of advertising is determined by + * providing SCAN_RSP data and/or enabling local privacy support. + */ + BT_LE_ADV_OPT_CONNECTABLE = BIT(0), + + /** Don't try to resume connectable advertising after a connection. + * This option is only meaningful when used together with + * BT_LE_ADV_OPT_CONNECTABLE. If set the advertising will be stopped + * when bt_le_adv_stop() is called or when an incoming (slave) + * connection happens. If this option is not set the stack will + * take care of keeping advertising enabled even as connections + * occur. + */ + /* if defined CONFIG_BLE_MULTI_ADV , Only support adv one time.*/ + BT_LE_ADV_OPT_ONE_TIME = BIT(1), + + /** Advertise using the identity address as the own address. + * @warning This will compromise the privacy of the device, so care + * must be taken when using this option. + */ + BT_LE_ADV_OPT_USE_IDENTITY = BIT(2), + + /** Advertise using GAP device name */ + BT_LE_ADV_OPT_USE_NAME = BIT(3), + + /** Use low duty directed advertising mode, otherwise high duty mode + * will be used. This option is only effective when used with + * bt_conn_create_slave_le(). + */ + BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY = BIT(4), + + /** Enable use of Resolvable Private Address (RPA) as the target address + * in directed advertisements when CONFIG_BT_PRIVACY is not enabled. + * This is required if the remote device is privacy-enabled and + * supports address resolution of the target address in directed + * advertisement. + * It is the responsibility of the application to check that the remote + * device supports address resolution of directed advertisements by + * reading its Central Address Resolution characteristic. + */ + BT_LE_ADV_OPT_DIR_ADDR_RPA = BIT(5), + + /** Use whitelist to filter devices that can request scan response + * data. + */ + BT_LE_ADV_OPT_FILTER_SCAN_REQ = BIT(6), + + /** Use whitelist to filter devices that can connect. */ + BT_LE_ADV_OPT_FILTER_CONN = BIT(7), +}; + +/** LE Advertising Parameters. */ +struct bt_le_adv_param { + /** Local identity */ + u8_t id; + + /** Bit-field of advertising options */ + u8_t options; + + /** Minimum Advertising Interval (N * 0.625) */ + u16_t interval_min; + + /** Maximum Advertising Interval (N * 0.625) */ + u16_t interval_max; + + #if defined(CONFIG_BT_STACK_PTS) + u8_t addr_type; + #endif +}; + +/** Helper to declare advertising parameters inline + * + * @param _options Advertising Options + * @param _int_min Minimum advertising interval + * @param _int_max Maximum advertising interval + */ +#define BT_LE_ADV_PARAM(_options, _int_min, _int_max) \ + (&(struct bt_le_adv_param) { \ + .options = (_options), \ + .interval_min = (_int_min), \ + .interval_max = (_int_max), \ + }) + +#define BT_LE_ADV_CONN BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2) + +#define BT_LE_ADV_CONN_NAME BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ + BT_LE_ADV_OPT_USE_NAME, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2) + +#define BT_LE_ADV_CONN_DIR_LOW_DUTY \ + BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME | \ + BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY, \ + BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2) + +#define BT_LE_ADV_CONN_DIR BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | \ + BT_LE_ADV_OPT_ONE_TIME, 0, 0) + +#define BT_LE_ADV_NCONN BT_LE_ADV_PARAM(0, BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2) + +#define BT_LE_ADV_NCONN_NAME BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_NAME, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2) + +/** @brief Start advertising + * + * Set advertisement data, scan response data, advertisement parameters + * and start advertising. + * + * @param param Advertising parameters. + * @param ad Data to be used in advertisement packets. + * @param ad_len Number of elements in ad + * @param sd Data to be used in scan response packets. + * @param sd_len Number of elements in sd + * + * @return Zero on success or (negative) error code otherwise. + * @return -ECONNREFUSED When connectable advertising is requested and there + * is already maximum number of connections established. + * This error code is only guaranteed when using Zephyr + * controller, for other controllers code returned in + * this case may be -EIO. + */ +int bt_le_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len); + +/** @brief Update advertising + * + * Update advertisement and scan response data. + * + * @param ad Data to be used in advertisement packets. + * @param ad_len Number of elements in ad + * @param sd Data to be used in scan response packets. + * @param sd_len Number of elements in sd + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len); + +/** @brief Stop advertising + * + * Stops ongoing advertising. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_le_adv_stop(void); + +/** @typedef bt_le_scan_cb_t + * @brief Callback type for reporting LE scan results. + * + * A function of this type is given to the bt_le_scan_start() function + * and will be called for any discovered LE device. + * + * @param addr Advertiser LE address and type. + * @param rssi Strength of advertiser signal. + * @param adv_type Type of advertising response from advertiser. + * @param buf Buffer containing advertiser data. + */ +typedef void bt_le_scan_cb_t(const bt_addr_le_t *addr, s8_t rssi, + u8_t adv_type, struct net_buf_simple *buf); + +enum { + /* Filter duplicates. */ + BT_LE_SCAN_FILTER_DUPLICATE = BIT(0), + + /* Filter using whitelist. */ + BT_LE_SCAN_FILTER_WHITELIST = BIT(1), + + /* Filter using extended filter policies. */ + BT_LE_SCAN_FILTER_EXTENDED = BIT(2), +}; + +enum { + /* Scan without requesting additional information from advertisers. */ + BT_LE_SCAN_TYPE_PASSIVE = 0x00, + + /* Scan and request additional information from advertisers. */ + BT_LE_SCAN_TYPE_ACTIVE = 0x01, +}; + +/** LE scan parameters */ +struct bt_le_scan_param { + /** Scan type (BT_LE_SCAN_TYPE_ACTIVE or BT_LE_SCAN_TYPE_PASSIVE) */ + u8_t type; + + /** Bit-field of scanning filter options. */ + u8_t filter_dup; + + /** Scan interval (N * 0.625 ms) */ + u16_t interval; + + /** Scan window (N * 0.625 ms) */ + u16_t window; +}; + +/** Helper to declare scan parameters inline + * + * @param _type Scan Type, BT_LE_SCAN_TYPE_ACTIVE or + * BT_LE_SCAN_TYPE_PASSIVE. + * @param _filter Filter options + * @param _interval Scan Interval (N * 0.625 ms) + * @param _window Scan Window (N * 0.625 ms) + */ +#define BT_LE_SCAN_PARAM(_type, _filter, _interval, _window) \ + (&(struct bt_le_scan_param) { \ + .type = (_type), \ + .filter_dup = (_filter), \ + .interval = (_interval), \ + .window = (_window), \ + }) + +/** Helper macro to enable active scanning to discover new devices. */ +#define BT_LE_SCAN_ACTIVE BT_LE_SCAN_PARAM(BT_LE_SCAN_TYPE_ACTIVE, \ + BT_LE_SCAN_FILTER_DUPLICATE, \ + BT_GAP_SCAN_FAST_INTERVAL, \ + BT_GAP_SCAN_FAST_WINDOW) + +/** Helper macro to enable passive scanning to discover new devices. + * + * This macro should be used if information required for device identification + * (e.g., UUID) are known to be placed in Advertising Data. + */ +#define BT_LE_SCAN_PASSIVE BT_LE_SCAN_PARAM(BT_LE_SCAN_TYPE_PASSIVE, \ + BT_LE_SCAN_FILTER_DUPLICATE, \ + BT_GAP_SCAN_FAST_INTERVAL, \ + BT_GAP_SCAN_FAST_WINDOW) + +/** @brief Start (LE) scanning + * + * Start LE scanning with given parameters and provide results through + * the specified callback. + * + * @param param Scan parameters. + * @param cb Callback to notify scan results. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +#if defined(CONFIG_BT_STACK_PTS) +int bt_le_pts_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb, u8_t addre_type); +#endif +int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb); + + +/** @brief Stop (LE) scanning. + * + * Stops ongoing LE scanning. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_scan_stop(void); + +/** @brief Add device (LE) to whitelist. + * + * Add peer device LE address to the whitelist. + * + * @note The whitelist cannot be modified when an LE role is using + * the whitelist, i.e advertiser or scanner using a whitelist or automatic + * connecting to devices using whitelist. + * + * @param addr Bluetooth LE identity address. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error. + */ +int bt_le_whitelist_add(const bt_addr_le_t *addr); + +/** @brief Remove device (LE) from whitelist. + * + * Remove peer device LE address from the whitelist. + * + * @note The whitelist cannot be modified when an LE role is using + * the whitelist, i.e advertiser or scanner using a whitelist or automatic + * connecting to devices using whitelist. + * + * @param addr Bluetooth LE identity address. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error. + */ +int bt_le_whitelist_rem(const bt_addr_le_t *addr); + +/** @brief Clear whitelist. + * + * Clear all devices from the whitelist. + * + * @note The whitelist cannot be modified when an LE role is using + * the whitelist, i.e advertiser or scanner using a whitelist or automatic + * connecting to devices using whitelist. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error. + */ +int bt_le_whitelist_clear(void); + +/** @brief Set (LE) channel map. + * + * @param chan_map Channel map. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_set_chan_map(u8_t chan_map[5]); + +/** @brief Helper for parsing advertising (or EIR or OOB) data. + * + * A helper for parsing the basic data types used for Extended Inquiry + * Response (EIR), Advertising Data (AD), and OOB data blocks. The most + * common scenario is to call this helper on the advertising data + * received in the callback that was given to bt_le_scan_start(). + * + * @param ad Advertising data as given to the bt_le_scan_cb_t callback. + * @param func Callback function which will be called for each element + * that's found in the data. The callback should return + * true to continue parsing, or false to stop parsing. + * @param user_data User data to be passed to the callback. + */ +void bt_data_parse(struct net_buf_simple *ad, + bool (*func)(struct bt_data *data, void *user_data), + void *user_data); + +/** OOB data that is specific for LE SC pairing method. */ +struct bt_le_oob_sc_data { + /** Random Number. */ + u8_t r[16]; + + /** Confirm Value. */ + u8_t c[16]; +}; + +/** General OOB data. */ +struct bt_le_oob { + /** LE address. If local privacy is enabled this is Resolvable Private + * Address. + */ + bt_addr_le_t addr; + + /** OOB data that are relevant for LESC pairing. */ + struct bt_le_oob_sc_data le_sc_data; +}; + +/** + * @brief Get LE local Out Of Band information + * + * This function allows to get local information that are useful for Out Of Band + * pairing or connection creation process. + * + * If privacy is enabled this will result in generating new Resolvable Private + * Address that is valid for CONFIG_BT_RPA_TIMEOUT seconds. This address + * will be used for advertising, active scanning and connection creation. + * + * @param id Local identity, in most cases BT_ID_DEFAULT. + * @param oob LE related information + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_oob_get_local(u8_t id, struct bt_le_oob *oob); + +/** @brief BR/EDR discovery result structure */ +struct bt_br_discovery_result { + /** private */ + u8_t _priv[4]; + + /** Remote device address */ + bt_addr_t addr; + + /** RSSI from inquiry */ + s8_t rssi; + + /** Class of Device */ + u8_t cod[3]; + + /** Extended Inquiry Response */ + u8_t eir[240]; +}; + +/** @typedef bt_br_discovery_cb_t + * @brief Callback type for reporting BR/EDR discovery (inquiry) + * results. + * + * A callback of this type is given to the bt_br_discovery_start() + * function and will be called at the end of the discovery with + * information about found devices populated in the results array. + * + * @param results Storage used for discovery results + * @param count Number of valid discovery results. + */ +typedef void bt_br_discovery_cb_t(struct bt_br_discovery_result *results, + size_t count); + +/** BR/EDR discovery parameters */ +struct bt_br_discovery_param { + /** Maximum length of the discovery in units of 1.28 seconds. + * Valid range is 0x01 - 0x30. + */ + u8_t length; + + /** True if limited discovery procedure is to be used. */ + bool limited; +}; + +/** @brief Start BR/EDR discovery + * + * Start BR/EDR discovery (inquiry) and provide results through the specified + * callback. When bt_br_discovery_cb_t is called it indicates that discovery + * has completed. If more inquiry results were received during session than + * fits in provided result storage, only ones with highest RSSI will be + * reported. + * + * @param param Discovery parameters. + * @param results Storage for discovery results. + * @param count Number of results in storage. Valid range: 1-255. + * @param cb Callback to notify discovery results. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_br_discovery_start(const struct bt_br_discovery_param *param, + struct bt_br_discovery_result *results, size_t count, + bt_br_discovery_cb_t cb); + +/** @brief Stop BR/EDR discovery. + * + * Stops ongoing BR/EDR discovery. If discovery was stopped by this call + * results won't be reported + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_br_discovery_stop(void); + +int bt_disable(void); + + +struct bt_br_oob { + /** BR/EDR address. */ + bt_addr_t addr; +}; + +/** + * @brief Get BR/EDR local Out Of Band information + * + * This function allows to get local controller information that are useful + * for Out Of Band pairing or connection creation process. + * + * @param oob Out Of Band information + */ +int bt_br_oob_get_local(struct bt_br_oob *oob); + +/** @def BT_ADDR_STR_LEN + * + * @brief Recommended length of user string buffer for Bluetooth address + * + * @details The recommended length guarantee the output of address + * conversion will not lose valuable information about address being + * processed. + */ +#define BT_ADDR_STR_LEN 18 + +/** @def BT_ADDR_LE_STR_LEN + * + * @brief Recommended length of user string buffer for Bluetooth LE address + * + * @details The recommended length guarantee the output of address + * conversion will not lose valuable information about address being + * processed. + */ +#define BT_ADDR_LE_STR_LEN 30 + +/** @brief Converts binary Bluetooth address to string. + * + * @param addr Address of buffer containing binary Bluetooth address. + * @param str Address of user buffer with enough room to store formatted + * string containing binary address. + * @param len Length of data to be copied to user string buffer. Refer to + * BT_ADDR_STR_LEN about recommended value. + * + * @return Number of successfully formatted bytes from binary address. + */ +static inline int bt_addr_to_str(const bt_addr_t *addr, char *str, size_t len) +{ + return snprintk(str, len, "%02X:%02X:%02X:%02X:%02X:%02X", + addr->val[5], addr->val[4], addr->val[3], + addr->val[2], addr->val[1], addr->val[0]); +} + +/** @brief Converts binary LE Bluetooth address to string. + * + * @param addr Address of buffer containing binary LE Bluetooth address. + * @param str Address of user buffer with enough room to store + * formatted string containing binary LE address. + * @param len Length of data to be copied to user string buffer. Refer to + * BT_ADDR_LE_STR_LEN about recommended value. + * + * @return Number of successfully formatted bytes from binary address. + */ +static inline int bt_addr_le_to_str(const bt_addr_le_t *addr, char *str, + size_t len) +{ + char type[10]; + + switch (addr->type) { + case BT_ADDR_LE_PUBLIC: + strcpy(type, "public"); + break; + case BT_ADDR_LE_RANDOM: + strcpy(type, "random"); + break; + case BT_ADDR_LE_PUBLIC_ID: + strcpy(type, "public-id"); + break; + case BT_ADDR_LE_RANDOM_ID: + strcpy(type, "random-id"); + break; + default: + snprintk(type, sizeof(type), "0x%02x", addr->type); + break; + } + + return snprintk(str, len, "%02X:%02X:%02X:%02X:%02X:%02X (%s)", + addr->a.val[5], addr->a.val[4], addr->a.val[3], + addr->a.val[2], addr->a.val[1], addr->a.val[0], type); +} + +/** + * @brief Convert Bluetooth address from string to binary. + * + * @param[in] str The string representation of a Bluetooth address. + * @param[out] addr Address of buffer to store the Bluetooth address + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_addr_from_str(const char *str, bt_addr_t *addr); + +/** + * @brief Convert LE Bluetooth address from string to binary. + * + * @param[in] str The string representation of an LE Bluetooth address. + * @param[in] type The string representation of the LE Bluetooth address type. + * @param[out] addr Address of buffer to store the LE Bluetooth address + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_addr_le_from_str(const char *str, const char *type, bt_addr_le_t *addr); + +/** @brief Enable/disable set controller in discoverable state. + * + * Allows make local controller to listen on INQUIRY SCAN channel and responds + * to devices making general inquiry. To enable this state it's mandatory + * to first be in connectable state. + * + * @param enable Value allowing/disallowing controller to become discoverable. + * + * @return Negative if fail set to requested state or requested state has been + * already set. Zero if done successfully. + */ +int bt_br_set_discoverable(bool enable); + +/** @brief Enable/disable set controller in connectable state. + * + * Allows make local controller to be connectable. It means the controller + * start listen to devices requests on PAGE SCAN channel. If disabled also + * resets discoverability if was set. + * + * @param enable Value allowing/disallowing controller to be connectable. + * + * @return Negative if fail set to requested state or requested state has been + * already set. Zero if done successfully. + */ +int bt_br_set_connectable(bool enable); + +/** Clear pairing information. + * + * @param id Local identity (mostly just BT_ID_DEFAULT). + * @param addr Remote address, NULL or BT_ADDR_LE_ANY to clear all remote + * devices. + * + * @return 0 on success or negative error value on failure. + */ +int bt_unpair(u8_t id, const bt_addr_le_t *addr); + +/** Information about a bond with a remote device. */ +struct bt_bond_info { + /** Address of the remote device. */ + bt_addr_le_t addr; +}; + +/** Iterate through all existing bonds. + * + * @param id Local identity (mostly just BT_ID_DEFAULT). + * @param func Function to call for each bond. + * @param user_data Data to pass to the callback function. + */ +void bt_foreach_bond(u8_t id, void (*func)(const struct bt_bond_info *info, void *user_data), + void *user_data); + +/** + * write extern inquiry response. + */ +int bt_br_write_eir(u8_t rec, u8_t *data); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_BLUETOOTH_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/buf.h b/components/ble/ble_stack/include/bluetooth/buf.h new file mode 100644 index 00000000..f641a2fa --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/buf.h @@ -0,0 +1,139 @@ +/** @file + * @brief Bluetooth data buffer API + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_BUF_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_BUF_H_ + +/** + * @brief Data buffers + * @defgroup bt_buf Data buffers + * @ingroup bluetooth + * @{ + */ + +#include +#include +#include + +/** Possible types of buffers passed around the Bluetooth stack */ +enum bt_buf_type { + /** HCI command */ + BT_BUF_CMD, + /** HCI event */ + BT_BUF_EVT, + /** Outgoing ACL data */ + BT_BUF_ACL_OUT, + /** Incoming ACL data */ + BT_BUF_ACL_IN, + /** Outgoing ISO data */ + BT_BUF_ISO_OUT, + /** Incoming ISO data */ + BT_BUF_ISO_IN, +}; + +/** Minimum amount of user data size for buffers passed to the stack. */ +#define BT_BUF_USER_DATA_MIN 4 + +#if defined(CONFIG_BT_HCI_RAW) +#define BT_BUF_RESERVE MAX(CONFIG_BT_HCI_RESERVE, CONFIG_BT_HCI_RAW_RESERVE) +#else +#define BT_BUF_RESERVE CONFIG_BT_HCI_RESERVE +#endif + +/** Data size neeed for HCI RX buffers */ +#define BT_BUF_RX_SIZE (BT_BUF_RESERVE + CONFIG_BT_RX_BUF_LEN) + +int bt_buf_get_rx_avail_cnt(void); + +/** Allocate a buffer for incoming data + * + * This will set the buffer type so bt_buf_set_type() does not need to + * be explicitly called before bt_recv_prio(). + * + * @param type Type of buffer. Only BT_BUF_EVT and BT_BUF_ACL_IN are + * allowed. + * @param timeout Timeout in milliseconds, or one of the special values + * K_NO_WAIT and K_FOREVER. + * @return A new buffer. + */ +struct net_buf *bt_buf_get_rx(enum bt_buf_type type, s32_t timeout); + +/** Allocate a buffer for an HCI Command Complete/Status Event + * + * This will set the buffer type so bt_buf_set_type() does not need to + * be explicitly called before bt_recv_prio(). + * + * @param timeout Timeout in milliseconds, or one of the special values + * K_NO_WAIT and K_FOREVER. + * @return A new buffer. + */ +struct net_buf *bt_buf_get_cmd_complete(s32_t timeout); + +/** Allocate a buffer for an HCI Event + * + * This will set the buffer type so bt_buf_set_type() does not need to + * be explicitly called before bt_recv_prio() or bt_recv(). + * + * @param evt HCI event code + * @param discardable Whether the driver considers the event discardable. + * @param timeout Timeout in milliseconds, or one of the special values + * K_NO_WAIT and K_FOREVER. + * @return A new buffer. + */ +struct net_buf *bt_buf_get_evt(u8_t evt, bool discardable, s32_t timeout); + +/** Set the buffer type + * + * @param buf Bluetooth buffer + * @param type The BT_* type to set the buffer to + */ +static inline void bt_buf_set_type(struct net_buf *buf, enum bt_buf_type type) +{ + *(u8_t *)net_buf_user_data(buf) = type; +} + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +static inline void bt_buf_set_rx_adv(struct net_buf *buf, bool is_adv) +{ + u8_t *usr_data = (u8_t *)net_buf_user_data(buf); + usr_data++; + *usr_data = is_adv; +} + +static inline u8_t bt_buf_check_rx_adv(struct net_buf *buf) +{ + u8_t *usr_data = (u8_t *)net_buf_user_data(buf); + usr_data++; + return (*usr_data); +} +#endif + +/** Get the buffer type + * + * @param buf Bluetooth buffer + * + * @return The BT_* type to of the buffer + */ +static inline enum bt_buf_type bt_buf_get_type(struct net_buf *buf) +{ + /* De-referencing the pointer from net_buf_user_data(buf) as a + * pointer to an enum causes issues on qemu_x86 because the true + * size is 8-bit, but the enum is 32-bit on qemu_x86. So we put in + * a temporary cast to 8-bit to ensure only 8 bits are read from + * the pointer. + */ + return (enum bt_buf_type)(*(u8_t *)net_buf_user_data(buf)); +} + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_BUF_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/conn.h b/components/ble/ble_stack/include/bluetooth/conn.h new file mode 100644 index 00000000..3b6aaae8 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/conn.h @@ -0,0 +1,944 @@ +/** @file + * @brief Bluetooth connection handling + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_CONN_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_CONN_H_ + +/** + * @brief Connection management + * @defgroup bt_conn Connection management + * @ingroup bluetooth + * @{ + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque type representing a connection to a remote device */ +struct bt_conn; + +/** Connection parameters for LE connections */ +struct bt_le_conn_param { + u16_t interval_min; + u16_t interval_max; + u16_t latency; + u16_t timeout; + + #if defined(CONFIG_BT_STACK_PTS) + u8_t own_address_type; + #endif +}; + +/** Helper to declare connection parameters inline + * + * @param int_min Minimum Connection Interval (N * 1.25 ms) + * @param int_max Maximum Connection Interval (N * 1.25 ms) + * @param lat Connection Latency + * @param to Supervision Timeout (N * 10 ms) + */ +#define BT_LE_CONN_PARAM(int_min, int_max, lat, to) \ + (&(struct bt_le_conn_param) { \ + .interval_min = (int_min), \ + .interval_max = (int_max), \ + .latency = (lat), \ + .timeout = (to), \ + }) + +/** Default LE connection parameters: + * Connection Interval: 30-50 ms + * Latency: 0 + * Timeout: 4 s + */ +#define BT_LE_CONN_PARAM_DEFAULT BT_LE_CONN_PARAM(BT_GAP_INIT_CONN_INT_MIN, \ + BT_GAP_INIT_CONN_INT_MAX, \ + 0, 400) +/** @brief Increment a connection's reference count. + * + * Increment the reference count of a connection object. + * + * @param conn Connection object. + * + * @return Connection object with incremented reference count. + */ +struct bt_conn *bt_conn_ref(struct bt_conn *conn); + +/** @brief Decrement a connection's reference count. + * + * Decrement the reference count of a connection object. + * + * @param conn Connection object. + */ +void bt_conn_unref(struct bt_conn *conn); + +/** @brief Iterate through all existing connections. + * + * @param type Connection Type + * @param func Function to call for each connection. + * @param data Data to pass to the callback function. + */ +void bt_conn_foreach(int type, void (*func)(struct bt_conn *conn, void *data), + void *data); + +/** @brief Look up an existing connection by address. + * + * Look up an existing connection based on the remote address. + * + * @param id Local identity (in most cases BT_ID_DEFAULT). + * @param peer Remote address. + * + * @return Connection object or NULL if not found. The caller gets a + * new reference to the connection object which must be released with + * bt_conn_unref() once done using the object. + */ +struct bt_conn *bt_conn_lookup_addr_le(u8_t id, const bt_addr_le_t *peer); + +#if defined(BFLB_BLE) +bool le_check_valid_conn(void); +#if defined(BFLB_HOST_ASSISTANT) +void bt_notify_disconnected(void); +#endif +#endif + +/** @brief Get destination (peer) address of a connection. + * + * @param conn Connection object. + * + * @return Destination address. + */ +const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn); + +/** @brief Get array index of a connection + * + * This function is used to map bt_conn to index of an array of + * connections. The array has CONFIG_BT_MAX_CONN elements. + * + * @param conn Connection object. + * + * @return Index of the connection object. + * The range of the returned value is 0..CONFIG_BT_MAX_CONN-1 + */ +u8_t bt_conn_index(struct bt_conn *conn); + +/** Connection Type */ +enum { + /** LE Connection Type */ + BT_CONN_TYPE_LE = BIT(0), + /** BR/EDR Connection Type */ + BT_CONN_TYPE_BR = BIT(1), + /** SCO Connection Type */ + BT_CONN_TYPE_SCO = BIT(2), + /** ISO Connection Type */ + BT_CONN_TYPE_ISO = BIT(3), + /** All Connection Type */ + BT_CONN_TYPE_ALL = BT_CONN_TYPE_LE | BT_CONN_TYPE_BR | + BT_CONN_TYPE_SCO | BT_CONN_TYPE_ISO, +}; + +/** LE Connection Info Structure */ +struct bt_conn_le_info { + /** Source (Local) Identity Address */ + const bt_addr_le_t *src; + /** Destination (Remote) Identity Address or remote Resolvable Private + * Address (RPA) before identity has been resolved. + */ + const bt_addr_le_t *dst; + /** Local device address used during connection setup. */ + const bt_addr_le_t *local; + /** Remote device address used during connection setup. */ + const bt_addr_le_t *remote; + u16_t interval; /** Connection interval */ + u16_t latency; /** Connection slave latency */ + u16_t timeout; /** Connection supervision timeout */ +}; + +/** BR/EDR Connection Info Structure */ +struct bt_conn_br_info { + const bt_addr_t *dst; /** Destination (Remote) BR/EDR address */ +}; + +/** Connection role (master or slave) */ +enum { + BT_CONN_ROLE_MASTER, + BT_CONN_ROLE_SLAVE, +}; + +/** @brief Connection Info Structure + * + * + * @param type Connection Type + * @param role Connection Role + * @param id Which local identity the connection was created with + * @param le LE Connection specific Info + * @param br BR/EDR Connection specific Info + */ +struct bt_conn_info { + u8_t type; + + u8_t role; + + u8_t id; + + union { + struct bt_conn_le_info le; + + struct bt_conn_br_info br; + }; +}; + +/** @brief Get connection info + * + * @param conn Connection object. + * @param info Connection info object. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info); + +/** @brief Get connected devices' info + * + * @param info Connection info object. + * + * @return Connected device number. + */ + int bt_conn_get_remote_dev_info(struct bt_conn_info *info); + +/** @brief Update the connection parameters. + * + * @param conn Connection object. + * @param param Updated connection parameters. + * + * @return Zero on success or (negative) error code on failure. + */ +#if defined(CONFIG_BT_STACK_PTS) +int pts_bt_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); +#endif +int bt_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); +/** @brief Disconnect from a remote device or cancel pending connection. + * + * Disconnect an active connection with the specified reason code or cancel + * pending outgoing connection. + * + * @param conn Connection to disconnect. + * @param reason Reason code for the disconnection. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_disconnect(struct bt_conn *conn, u8_t reason); + +/** @brief Initiate an LE connection to a remote device. + * + * Allows initiate new LE link to remote peer using its address. + * Returns a new reference that the the caller is responsible for managing. + * + * This uses the General Connection Establishment procedure. + * + * @param peer Remote address. + * @param param Initial connection parameters. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer, + const struct bt_le_conn_param *param); + +/** @brief Automatically connect to remote devices in whitelist. + * + * This uses the Auto Connection Establishment procedure. + * + * @param param Initial connection parameters. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_create_auto_le(const struct bt_le_conn_param *param); + +/** @brief Stop automatic connect creation. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_create_auto_stop(void); + +/** @brief Automatically connect to remote device if it's in range. + * + * This function enables/disables automatic connection initiation. + * Every time the device loses the connection with peer, this connection + * will be re-established if connectable advertisement from peer is received. + * + * Note: Auto connect is disabled during explicit scanning. + * + * @param addr Remote Bluetooth address. + * @param param If non-NULL, auto connect is enabled with the given + * parameters. If NULL, auto connect is disabled. + * + * @return Zero on success or error code otherwise. + */ +int bt_le_set_auto_conn(const bt_addr_le_t *addr, + const struct bt_le_conn_param *param); + +/** @brief Initiate directed advertising to a remote device + * + * Allows initiating a new LE connection to remote peer with the remote + * acting in central role and the local device in peripheral role. + * + * The advertising type will either be BT_LE_ADV_DIRECT_IND, or + * BT_LE_ADV_DIRECT_IND_LOW_DUTY if the BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY + * option was used as part of the advertising parameters. + * + * In case of high duty cycle this will result in a callback with + * connected() with a new connection or with an error. + * + * The advertising may be canceled with bt_conn_disconnect(). + * + * Returns a new reference that the the caller is responsible for managing. + * + * @param peer Remote address. + * @param param Directed advertising parameters. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer, + const struct bt_le_adv_param *param); + +/** Security level. */ +typedef enum __packed { + /** Level 0: Only for BR/EDR special cases, like SDP */ + BT_SECURITY_L0, + /** Level 1: No encryption and no authentication. */ + BT_SECURITY_L1, + /** Level 2: Encryption and no authentication (no MITM). */ + BT_SECURITY_L2, + /** Level 3: Encryption and authentication (MITM). */ + BT_SECURITY_L3, + /** Level 4: Authenticated Secure Connections and 128-bit key. */ + BT_SECURITY_L4, + + BT_SECURITY_NONE __deprecated = BT_SECURITY_L0, + BT_SECURITY_LOW __deprecated = BT_SECURITY_L1, + BT_SECURITY_MEDIUM __deprecated = BT_SECURITY_L2, + BT_SECURITY_HIGH __deprecated = BT_SECURITY_L3, + BT_SECURITY_FIPS __deprecated = BT_SECURITY_L4, + + /** Bit to force new pairing procedure, bit-wise OR with requested + * security level. + */ + BT_SECURITY_FORCE_PAIR = BIT(7), +} bt_security_t; + +/** @brief Set security level for a connection. + * + * This function enable security (encryption) for a connection. If device is + * already paired with sufficiently strong key encryption will be enabled. If + * link is already encrypted with sufficiently strong key this function does + * nothing. + * + * If device is not paired pairing will be initiated. If device is paired and + * keys are too weak but input output capabilities allow for strong enough keys + * pairing will be initiated. + * + * This function may return error if required level of security is not possible + * to achieve due to local or remote device limitation (e.g., input output + * capabilities), or if the maximum number of paired devices has been reached. + * + * This function may return error if the pairing procedure has already been + * initiated by the local device or the peer device. + * + * @param conn Connection object. + * @param sec Requested security level. + * + * @return 0 on success or negative error + */ +int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec); + +/** @brief Get security level for a connection. + * + * @return Connection security level + */ +bt_security_t bt_conn_get_security(struct bt_conn *conn); + +static inline int __deprecated bt_conn_security(struct bt_conn *conn, + bt_security_t sec) +{ + return bt_conn_set_security(conn, sec); +} + +/** @brief Get encryption key size. + * + * This function gets encryption key size. + * If there is no security (encryption) enabled 0 will be returned. + * + * @param conn Existing connection object. + * + * @return Encryption key size. + */ +u8_t bt_conn_enc_key_size(struct bt_conn *conn); + +enum bt_security_err { + /** Security procedure successful. */ + BT_SECURITY_ERR_SUCCESS, + + /** Authentication failed. */ + BT_SECURITY_ERR_AUTH_FAIL, + + /** PIN or encryption key is missing. */ + BT_SECURITY_ERR_PIN_OR_KEY_MISSING, + + /** OOB data is not available. */ + BT_SECURITY_ERR_OOB_NOT_AVAILABLE, + + /** The requested security level could not be reached. */ + BT_SECURITY_ERR_AUTH_REQUIREMENT, + + /** Pairing is not supported */ + BT_SECURITY_ERR_PAIR_NOT_SUPPORTED, + + /** Pairing is not allowed. */ + BT_SECURITY_ERR_PAIR_NOT_ALLOWED, + + /** Invalid parameters. */ + BT_SECURITY_ERR_INVALID_PARAM, + + /** Pairing failed but the exact reason could not be specified. */ + BT_SECURITY_ERR_UNSPECIFIED, +}; + +/** @brief Connection callback structure. + * + * This structure is used for tracking the state of a connection. + * It is registered with the help of the bt_conn_cb_register() API. + * It's permissible to register multiple instances of this @ref bt_conn_cb + * type, in case different modules of an application are interested in + * tracking the connection state. If a callback is not of interest for + * an instance, it may be set to NULL and will as a consequence not be + * used for that instance. + */ +struct bt_conn_cb { + /** @brief A new connection has been established. + * + * This callback notifies the application of a new connection. + * In case the err parameter is non-zero it means that the + * connection establishment failed. + * + * @param conn New connection object. + * @param err HCI error. Zero for success, non-zero otherwise. + * + * @p err can mean either of the following: + * - @ref BT_HCI_ERR_UNKNOWN_CONN_ID Creating the connection started by + * @ref bt_conn_create_le was canceled either by the user through + * @ref bt_conn_disconnect or by the timeout in the host through + * :option:`CONFIG_BT_CREATE_CONN_TIMEOUT`. + * - @p BT_HCI_ERR_ADV_TIMEOUT Directed advertiser started by @ref + * bt_conn_create_slave_le with high duty cycle timed out after 1.28 + * seconds. + */ + void (*connected)(struct bt_conn *conn, u8_t err); + + /** @brief A connection has been disconnected. + * + * This callback notifies the application that a connection + * has been disconnected. + * + * @param conn Connection object. + * @param reason HCI reason for the disconnection. + */ + void (*disconnected)(struct bt_conn *conn, u8_t reason); + + /** @brief LE connection parameter update request. + * + * This callback notifies the application that a remote device + * is requesting to update the connection parameters. The + * application accepts the parameters by returning true, or + * rejects them by returning false. Before accepting, the + * application may also adjust the parameters to better suit + * its needs. + * + * It is recommended for an application to have just one of these + * callbacks for simplicity. However, if an application registers + * multiple it needs to manage the potentially different + * requirements for each callback. Each callback gets the + * parameters as returned by previous callbacks, i.e. they are not + * necessarily the same ones as the remote originally sent. + * + * @param conn Connection object. + * @param param Proposed connection parameters. + * + * @return true to accept the parameters, or false to reject them. + */ + bool (*le_param_req)(struct bt_conn *conn, + struct bt_le_conn_param *param); + + /** @brief The parameters for an LE connection have been updated. + * + * This callback notifies the application that the connection + * parameters for an LE connection have been updated. + * + * @param conn Connection object. + * @param interval Connection interval. + * @param latency Connection latency. + * @param timeout Connection supervision timeout. + */ + void (*le_param_updated)(struct bt_conn *conn, u16_t interval, + u16_t latency, u16_t timeout); +#if defined(CONFIG_BT_SMP) + /** @brief Remote Identity Address has been resolved. + * + * This callback notifies the application that a remote + * Identity Address has been resolved + * + * @param conn Connection object. + * @param rpa Resolvable Private Address. + * @param identity Identity Address. + */ + void (*identity_resolved)(struct bt_conn *conn, + const bt_addr_le_t *rpa, + const bt_addr_le_t *identity); +#endif /* CONFIG_BT_SMP */ +#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) + /** @brief The security level of a connection has changed. + * + * This callback notifies the application that the security level + * of a connection has changed. + * + * @param conn Connection object. + * @param level New security level of the connection. + * @param err Security error. Zero for success, non-zero otherwise. + */ + void (*security_changed)(struct bt_conn *conn, bt_security_t level, + enum bt_security_err err); +#endif /* defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR) */ + struct bt_conn_cb *_next; +}; + + +#if defined(CONFIG_BT_STACK_PTS) +typedef enum{ + SMP_AUTH_NO_BONDING_MITM = 1, + SMP_IO_CAP_DISPLAY_ONLY = 2, + SMP_AUTH_NO_MITM = 3, + SMP_AUTH_NO_BONDING_MITM_IO_DISPLAY_ONLY = 4, + SMP_IO_KEYBOARD_ONLY = 5, + SMP_IO_NO_INPUT_OUTPUT = 6, + SMP_PARING_INVALID_PUBLIC_KEY = 7, +}smp_test_id; + +void bt_set_mitm(bool enable); +void bt_set_smpflag(smp_test_id id); +void bt_clear_smpflag(smp_test_id id); +#endif + +/** @brief Register connection callbacks. + * + * Register callbacks to monitor the state of connections. + * + * @param cb Callback struct. + */ +void bt_conn_cb_register(struct bt_conn_cb *cb); + +/** Enable/disable bonding. + * + * Set/clear the Bonding flag in the Authentication Requirements of + * SMP Pairing Request/Response data. + * The initial value of this flag depends on BT_BONDABLE Kconfig setting. + * For the vast majority of applications calling this function shouldn't be + * needed. + * + * @param enable Value allowing/disallowing to be bondable. + */ +void bt_set_bondable(bool enable); + +/** Allow/disallow remote OOB data to be used for pairing. + * + * Set/clear the OOB data flag for SMP Pairing Request/Response data. + * The initial value of this flag depends on BT_OOB_DATA_PRESENT Kconfig + * setting. + * + * @param enable Value allowing/disallowing remote OOB data. + */ +void bt_set_oob_data_flag(bool enable); + +/** + * @brief Set OOB data during LE SC pairing procedure + * + * This function allows to set OOB data during the LE SC pairing procedure. The + * function should only be called in response to the oob_data_request() callback + * provided that LE SC method is used for pairing. + * + * The user should submit OOB data according to the information received in the + * callback. This may yield three different configurations: with only local OOB + * data present, with only remote OOB data present or with both local and + * remote OOB data present. + * + * @param conn Connection object + * @param oobd_local Local OOB data or NULL if not present + * @param oobd_remote Remote OOB data or NULL if not present + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_oob_set_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data *oobd_local, + const struct bt_le_oob_sc_data *oobd_remote); + +/** + * @brief Get OOB data used for LE SC pairing procedure + * + * This function allows to get OOB data during the LE SC pairing procedure that + * were set by the bt_le_oob_set_sc_data() API. + * + * Note: The OOB data will only be available as long as the connection object + * associated with it is valid. + * + * @param conn Connection object + * @param oobd_local Local OOB data or NULL if not set + * @param oobd_remote Remote OOB data or NULL if not set + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_oob_get_sc_data(struct bt_conn *conn, + const struct bt_le_oob_sc_data **oobd_local, + const struct bt_le_oob_sc_data **oobd_remote); + +/** @def BT_PASSKEY_INVALID + * + * Special passkey value that can be used to disable a previously + * set fixed passkey. + */ +#define BT_PASSKEY_INVALID 0xffffffff + +/** @brief Set a fixed passkey to be used for pairing. + * + * This API is only available when the CONFIG_BT_FIXED_PASSKEY + * configuration option has been enabled. + * + * Sets a fixed passkey to be used for pairing. If set, the + * pairing_confim() callback will be called for all incoming pairings. + * + * @param passkey A valid passkey (0 - 999999) or BT_PASSKEY_INVALID + * to disable a previously set fixed passkey. + * + * @return 0 on success or a negative error code on failure. + */ +int bt_passkey_set(unsigned int passkey); + +/** Info Structure for OOB pairing */ +struct bt_conn_oob_info { + /** Type of OOB pairing method */ + enum { + /** LE legacy pairing */ + BT_CONN_OOB_LE_LEGACY, + + /** LE SC pairing */ + BT_CONN_OOB_LE_SC, + } type; + + union { + /** LESC OOB pairing parameters */ + struct { + /** OOB data configuration */ + enum { + /** Local OOB data requested */ + BT_CONN_OOB_LOCAL_ONLY, + + /** Remote OOB data requested */ + BT_CONN_OOB_REMOTE_ONLY, + + /** Both local and remote OOB data requested */ + BT_CONN_OOB_BOTH_PEERS, + + /** No OOB data requested */ + BT_CONN_OOB_NO_DATA, + } oob_config; + } lesc; + }; +}; + +/** Authenticated pairing callback structure */ +struct bt_conn_auth_cb { + /** @brief Display a passkey to the user. + * + * When called the application is expected to display the given + * passkey to the user, with the expectation that the passkey will + * then be entered on the peer device. The passkey will be in the + * range of 0 - 999999, and is expected to be padded with zeroes so + * that six digits are always shown. E.g. the value 37 should be + * shown as 000037. + * + * This callback may be set to NULL, which means that the local + * device lacks the ability do display a passkey. If set + * to non-NULL the cancel callback must also be provided, since + * this is the only way the application can find out that it should + * stop displaying the passkey. + * + * @param conn Connection where pairing is currently active. + * @param passkey Passkey to show to the user. + */ + void (*passkey_display)(struct bt_conn *conn, unsigned int passkey); + + /** @brief Request the user to enter a passkey. + * + * When called the user is expected to enter a passkey. The passkey + * must be in the range of 0 - 999999, and should be expected to + * be zero-padded, as that's how the peer device will typically be + * showing it (e.g. 37 would be shown as 000037). + * + * Once the user has entered the passkey its value should be given + * to the stack using the bt_conn_auth_passkey_entry() API. + * + * This callback may be set to NULL, which means that the local + * device lacks the ability to enter a passkey. If set to non-NULL + * the cancel callback must also be provided, since this is the + * only way the application can find out that it should stop + * requesting the user to enter a passkey. + * + * @param conn Connection where pairing is currently active. + */ + void (*passkey_entry)(struct bt_conn *conn); + + /** @brief Request the user to confirm a passkey. + * + * When called the user is expected to confirm that the given + * passkey is also shown on the peer device.. The passkey will + * be in the range of 0 - 999999, and should be zero-padded to + * always be six digits (e.g. 37 would be shown as 000037). + * + * Once the user has confirmed the passkey to match, the + * bt_conn_auth_passkey_confirm() API should be called. If the + * user concluded that the passkey doesn't match the + * bt_conn_auth_cancel() API should be called. + * + * This callback may be set to NULL, which means that the local + * device lacks the ability to confirm a passkey. If set to non-NULL + * the cancel callback must also be provided, since this is the + * only way the application can find out that it should stop + * requesting the user to confirm a passkey. + * + * @param conn Connection where pairing is currently active. + * @param passkey Passkey to be confirmed. + */ + void (*passkey_confirm)(struct bt_conn *conn, unsigned int passkey); + + /** @brief Request the user to provide OOB data. + * + * When called the user is expected to provide OOB data. The required + * data are indicated by the information structure. + * + * For LESC OOB pairing method, the user should provide local OOB data, + * remote OOB data or both depending on their availability. Their value + * should be given to the stack using the bt_le_oob_set_sc_data() API. + * + * This callback must be set to non-NULL in order to support OOB + * pairing. + * + * @param conn Connection where pairing is currently active. + * @param info OOB pairing information. + */ + void (*oob_data_request)(struct bt_conn *conn, + struct bt_conn_oob_info *info); + + /** @brief Cancel the ongoing user request. + * + * This callback will be called to notify the application that it + * should cancel any previous user request (passkey display, entry + * or confirmation). + * + * This may be set to NULL, but must always be provided whenever the + * passkey_display, passkey_entry passkey_confirm or pairing_confirm + * callback has been provided. + * + * @param conn Connection where pairing is currently active. + */ + void (*cancel)(struct bt_conn *conn); + + /** @brief Request confirmation for an incoming pairing. + * + * This callback will be called to confirm an incoming pairing + * request where none of the other user callbacks is applicable. + * + * If the user decides to accept the pairing the + * bt_conn_auth_pairing_confirm() API should be called. If the + * user decides to reject the pairing the bt_conn_auth_cancel() API + * should be called. + * + * This callback may be set to NULL, which means that the local + * device lacks the ability to confirm a pairing request. If set + * to non-NULL the cancel callback must also be provided, since + * this is the only way the application can find out that it should + * stop requesting the user to confirm a pairing request. + * + * @param conn Connection where pairing is currently active. + */ + void (*pairing_confirm)(struct bt_conn *conn); + +#if defined(CONFIG_BT_BREDR) + /** @brief Request the user to enter a passkey. + * + * This callback will be called for a BR/EDR (Bluetooth Classic) + * connection where pairing is being performed. Once called the + * user is expected to enter a PIN code with a length between + * 1 and 16 digits. If the @a highsec parameter is set to true + * the PIN code must be 16 digits long. + * + * Once entered, the PIN code should be given to the stack using + * the bt_conn_auth_pincode_entry() API. + * + * This callback may be set to NULL, however in that case pairing + * over BR/EDR will not be possible. If provided, the cancel + * callback must be provided as well. + * + * @param conn Connection where pairing is currently active. + * @param highsec true if 16 digit PIN is required. + */ + void (*pincode_entry)(struct bt_conn *conn, bool highsec); +#endif + + /** @brief notify that pairing process was complete. + * + * This callback notifies the application that the pairing process + * has been completed. + * + * @param conn Connection object. + * @param bonded pairing is bonded or not. + */ + void (*pairing_complete)(struct bt_conn *conn, bool bonded); + + /** @brief notify that pairing process has failed. + * + * @param conn Connection object. + * @param reason Pairing failed reason + */ + void (*pairing_failed)(struct bt_conn *conn, + enum bt_security_err reason); +}; + +/** @brief Register authentication callbacks. + * + * Register callbacks to handle authenticated pairing. Passing NULL + * unregisters a previous callbacks structure. + * + * @param cb Callback struct. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb); + +/** @brief Reply with entered passkey. + * + * This function should be called only after passkey_entry callback from + * bt_conn_auth_cb structure was called. + * + * @param conn Connection object. + * @param passkey Entered passkey. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey); + +/** @brief Cancel ongoing authenticated pairing. + * + * This function allows to cancel ongoing authenticated pairing. + * + * @param conn Connection object. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_cancel(struct bt_conn *conn); + +/** @brief Reply if passkey was confirmed to match by user. + * + * This function should be called only after passkey_confirm callback from + * bt_conn_auth_cb structure was called. + * + * @param conn Connection object. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_passkey_confirm(struct bt_conn *conn); + +/** @brief Reply if incoming pairing was confirmed by user. + * + * This function should be called only after pairing_confirm callback from + * bt_conn_auth_cb structure was called if user confirmed incoming pairing. + * + * @param conn Connection object. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_pairing_confirm(struct bt_conn *conn); + +/** @brief Reply with entered PIN code. + * + * This function should be called only after PIN code callback from + * bt_conn_auth_cb structure was called. It's for legacy 2.0 devices. + * + * @param conn Connection object. + * @param pin Entered PIN code. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin); + +/** Connection parameters for BR/EDR connections */ +struct bt_br_conn_param { + bool allow_role_switch; +}; + +/** Helper to declare BR/EDR connection parameters inline + * + * @param role_switch True if role switch is allowed + */ +#define BT_BR_CONN_PARAM(role_switch) \ + (&(struct bt_br_conn_param) { \ + .allow_role_switch = (role_switch), \ + }) + +/** Default BR/EDR connection parameters: + * Role switch allowed + */ +#define BT_BR_CONN_PARAM_DEFAULT BT_BR_CONN_PARAM(true) + + +/** @brief Initiate an BR/EDR connection to a remote device. + * + * Allows initiate new BR/EDR link to remote peer using its address. + * Returns a new reference that the the caller is responsible for managing. + * + * @param peer Remote address. + * @param param Initial connection parameters. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_br(const bt_addr_t *peer, + const struct bt_br_conn_param *param); + +/** @brief Initiate an SCO connection to a remote device. + * + * Allows initiate new SCO link to remote peer using its address. + * Returns a new reference that the the caller is responsible for managing. + * + * @param peer Remote address. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_sco(const bt_addr_t *peer); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_CONN_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/crypto.h b/components/ble/ble_stack/include/bluetooth/crypto.h new file mode 100644 index 00000000..4f2034c4 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/crypto.h @@ -0,0 +1,77 @@ +/** @file + * @brief Bluetooth subsystem crypto APIs. + */ + +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * Copyright (c) 2015-2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_CRYPTO_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_CRYPTO_H_ + +/** + * @brief Cryptography + * @defgroup bt_crypto Cryptography + * @ingroup bluetooth + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Generate random data. + * + * A random number generation helper which utilizes the Bluetooth + * controller's own RNG. + * + * @param buf Buffer to insert the random data + * @param len Length of random data to generate + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_rand(void *buf, size_t len); + +/** @brief AES encrypt little-endian data. + * + * An AES encrypt helper is used to request the Bluetooth controller's own + * hardware to encrypt the plaintext using the key and returns the encrypted + * data. + * + * @param key 128 bit LS byte first key for the encryption of the plaintext + * @param plaintext 128 bit LS byte first plaintext data block to be encrypted + * @param enc_data 128 bit LS byte first encrypted data block + * + * @return Zero on success or error code otherwise. + */ +int bt_encrypt_le(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]); + +/** @brief AES encrypt big-endian data. + * + * An AES encrypt helper is used to request the Bluetooth controller's own + * hardware to encrypt the plaintext using the key and returns the encrypted + * data. + * + * @param key 128 bit MS byte first key for the encryption of the plaintext + * @param plaintext 128 bit MS byte first plaintext data block to be encrypted + * @param enc_data 128 bit MS byte first encrypted data block + * + * @return Zero on success or error code otherwise. + */ +int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16], + u8_t enc_data[16]); + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_CRYPTO_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/gap.h b/components/ble/ble_stack/include/bluetooth/gap.h new file mode 100644 index 00000000..cc69eb3f --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/gap.h @@ -0,0 +1,86 @@ +/** @file + * @brief Bluetooth Generic Access Profile defines and Assigned Numbers. + */ + +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_GAP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_GAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Company Identifiers (see Bluetooth Assigned Numbers) */ +#define BT_COMP_ID_LF 0x05f1 /* The Linux Foundation */ + +/* EIR/AD data type definitions */ +#define BT_DATA_FLAGS 0x01 /* AD flags */ +#define BT_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BT_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BT_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BT_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BT_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BT_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BT_DATA_NAME_SHORTENED 0x08 /* Shortened name */ +#define BT_DATA_NAME_COMPLETE 0x09 /* Complete name */ +#define BT_DATA_TX_POWER 0x0a /* Tx Power */ +#define BT_DATA_SM_TK_VALUE 0x10 /* Security Manager TK Value */ +#define BT_DATA_SM_OOB_FLAGS 0x11 /* Security Manager OOB Flags */ +#define BT_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BT_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BT_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BT_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BT_DATA_LE_BT_DEVICE_ADDRESS 0x1b /* LE Bluetooth Device Address */ +#define BT_DATA_LE_ROLE 0x1c /* LE Role */ +#define BT_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BT_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BT_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BT_DATA_LE_SC_CONFIRM_VALUE 0x22 /* LE SC Confirmation Value */ +#define BT_DATA_LE_SC_RANDOM_VALUE 0x23 /* LE SC Random Value */ +#define BT_DATA_URI 0x24 /* URI */ +#define BT_DATA_MESH_PROV 0x29 /* Mesh Provisioning PDU */ +#define BT_DATA_MESH_MESSAGE 0x2a /* Mesh Networking PDU */ +#define BT_DATA_MESH_BEACON 0x2b /* Mesh Beacon */ + +#define BT_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BT_LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */ +#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +/* Defined GAP timers */ +#define BT_GAP_SCAN_FAST_INTERVAL 0x0060 /* 60 ms */ +#define BT_GAP_SCAN_FAST_WINDOW 0x0030 /* 30 ms */ +#define BT_GAP_SCAN_SLOW_INTERVAL_1 0x0800 /* 1.28 s */ +#define BT_GAP_SCAN_SLOW_WINDOW_1 0x0012 /* 11.25 ms */ +#define BT_GAP_SCAN_SLOW_INTERVAL_2 0x1000 /* 2.56 s */ +#define BT_GAP_SCAN_SLOW_WINDOW_2 0x0012 /* 11.25 ms */ + +#if defined(BFLB_BLE) +#define CONFIG_BT_BACKGROUND_SCAN_INTERVAL 0x0800 +#define CONFIG_BT_BACKGROUND_SCAN_WINDOW 0x0012 +#define BT_GAP_ADV_FAST_INT_MIN_3 0x0020 /* 20 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_3 0x0020 /* 20 ms */ +#endif + +#define BT_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */ +#define BT_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */ + + +#define BT_GAP_ADV_SLOW_INT_MIN 0x0640 /* 1 s */ +#define BT_GAP_ADV_SLOW_INT_MAX 0x0780 /* 1.2 s */ +#define BT_GAP_INIT_CONN_INT_MIN 0x0018 /* 30 ms */ +#define BT_GAP_INIT_CONN_INT_MAX 0x0028 /* 50 ms */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_GAP_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/gatt.h b/components/ble/ble_stack/include/bluetooth/gatt.h new file mode 100644 index 00000000..b52a8785 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/gatt.h @@ -0,0 +1,1390 @@ +/** @file + * @brief Generic Attribute Profile handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_GATT_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_GATT_H_ + +/** + * @brief Generic Attribute Profile (GATT) + * @defgroup bt_gatt Generic Attribute Profile (GATT) + * @ingroup bluetooth + * @{ + */ + +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/* GATT attribute permission bit field values */ +enum { + /** No operations supported, e.g. for notify-only */ + BT_GATT_PERM_NONE = 0, + + /** Attribute read permission. */ + BT_GATT_PERM_READ = BIT(0), + + /** Attribute write permission. */ + BT_GATT_PERM_WRITE = BIT(1), + + /** Attribute read permission with encryption. + * + * If set, requires encryption for read access. + */ + BT_GATT_PERM_READ_ENCRYPT = BIT(2), + + /** Attribute write permission with encryption. + * + * If set, requires encryption for write access. + */ + BT_GATT_PERM_WRITE_ENCRYPT = BIT(3), + + /** Attribute read permission with authentication. + * + * If set, requires encryption using authenticated link-key for read + * access. + */ + BT_GATT_PERM_READ_AUTHEN = BIT(4), + + /** Attribute write permission with authentication. + * + * If set, requires encryption using authenticated link-key for write + * access. + */ + BT_GATT_PERM_WRITE_AUTHEN = BIT(5), + + /** Attribute prepare write permission. + * + * If set, allows prepare writes with use of BT_GATT_WRITE_FLAG_PREPARE + * passed to write callback. + */ + BT_GATT_PERM_PREPARE_WRITE = BIT(6), +}; + +/** @def BT_GATT_ERR + * @brief Construct error return value for attribute read and write callbacks. + * + * @param _att_err ATT error code + * + * @return Appropriate error code for the attribute callbacks. + * + */ +#define BT_GATT_ERR(_att_err) (-(_att_err)) + +/* GATT attribute write flags */ +enum { + /** Attribute prepare write flag + * + * If set, write callback should only check if the device is + * authorized but no data shall be written. + */ + BT_GATT_WRITE_FLAG_PREPARE = BIT(0), + + /** Attribute write command flag + * + * If set, indicates that write operation is a command (Write without + * response) which doesn't generate any response. + */ + BT_GATT_WRITE_FLAG_CMD = BIT(1), +}; + +/** @brief GATT Attribute structure. */ +struct bt_gatt_attr { + /** Attribute UUID */ + const struct bt_uuid *uuid; + + /** Attribute read callback + * + * The callback can also be used locally to read the contents of the + * attribute in which case no connection will be set. + * + * @param conn The connection that is requesting to read + * @param attr The attribute that's being read + * @param buf Buffer to place the read result in + * @param len Length of data to read + * @param offset Offset to start reading from + * + * @return Number fo bytes read, or in case of an error + * BT_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*read)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, + u16_t offset); + + /** Attribute write callback + * + * The callback can also be used locally to read the contents of the + * attribute in which case no connection will be set. + * + * @param conn The connection that is requesting to write + * @param attr The attribute that's being written + * @param buf Buffer with the data to write + * @param len Number of bytes in the buffer + * @param offset Offset to start writing from + * @param flags Flags (BT_GATT_WRITE_*) + * + * @return Number of bytes written, or in case of an error + * BT_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*write)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, u16_t len, + u16_t offset, u8_t flags); + + /** Attribute user data */ + void *user_data; + /** Attribute handle */ + u16_t handle; + /** Attribute permissions */ + u8_t perm; +}; + +/** @brief GATT Service structure */ +struct bt_gatt_service_static { + /** Service Attributes */ + const struct bt_gatt_attr *attrs; + /** Service Attribute count */ + size_t attr_count; +}; + +/** @brief GATT Service structure */ +struct bt_gatt_service { + /** Service Attributes */ + struct bt_gatt_attr *attrs; + /** Service Attribute count */ + size_t attr_count; + sys_snode_t node; +}; + +/** @brief Service Attribute Value. */ +struct bt_gatt_service_val { + /** Service UUID. */ + const struct bt_uuid *uuid; + /** Service end handle. */ + u16_t end_handle; +}; + +/** @brief Include Attribute Value. */ +struct bt_gatt_include { + /** Service UUID. */ + const struct bt_uuid *uuid; + /** Service start handle. */ + u16_t start_handle; + /** Service end handle. */ + u16_t end_handle; +}; + +/* Characteristic Properties Bit field values */ + +/** @def BT_GATT_CHRC_BROADCAST + * @brief Characteristic broadcast property. + * + * If set, permits broadcasts of the Characteristic Value using Server + * Characteristic Configuration Descriptor. + */ +#define BT_GATT_CHRC_BROADCAST 0x01 +/** @def BT_GATT_CHRC_READ + * @brief Characteristic read property. + * + * If set, permits reads of the Characteristic Value. + */ +#define BT_GATT_CHRC_READ 0x02 +/** @def BT_GATT_CHRC_WRITE_WITHOUT_RESP + * @brief Characteristic write without response property. + * + * If set, permits write of the Characteristic Value without response. + */ +#define BT_GATT_CHRC_WRITE_WITHOUT_RESP 0x04 +/** @def BT_GATT_CHRC_WRITE + * @brief Characteristic write with response property. + * + * If set, permits write of the Characteristic Value with response. + */ +#define BT_GATT_CHRC_WRITE 0x08 +/** @def BT_GATT_CHRC_NOTIFY + * @brief Characteristic notify property. + * + * If set, permits notifications of a Characteristic Value without + * acknowledgment. + */ +#define BT_GATT_CHRC_NOTIFY 0x10 +/** @def BT_GATT_CHRC_INDICATE + * @brief Characteristic indicate property. + * + * If set, permits indications of a Characteristic Value with acknowledgment. + */ +#define BT_GATT_CHRC_INDICATE 0x20 +/** @def BT_GATT_CHRC_AUTH + * @brief Characteristic Authenticated Signed Writes property. + * + * If set, permits signed writes to the Characteristic Value. + */ +#define BT_GATT_CHRC_AUTH 0x40 +/** @def BT_GATT_CHRC_EXT_PROP + * @brief Characteristic Extended Properties property. + * + * If set, additional characteristic properties are defined in the + * Characteristic Extended Properties Descriptor. + */ +#define BT_GATT_CHRC_EXT_PROP 0x80 + +/** @brief Characteristic Attribute Value. */ +struct bt_gatt_chrc { + /** Characteristic UUID. */ + const struct bt_uuid *uuid; + /** Characteristic Value handle. */ + u16_t value_handle; + /** Characteristic properties. */ + u8_t properties; +}; + +/* Characteristic Extended Properties Bit field values */ +#define BT_GATT_CEP_RELIABLE_WRITE 0x0001 +#define BT_GATT_CEP_WRITABLE_AUX 0x0002 + +/** @brief Characteristic Extended Properties Attribute Value. */ +struct bt_gatt_cep { + /** Characteristic Extended properties */ + u16_t properties; +}; + +/* Client Characteristic Configuration Values */ + +/** @def BT_GATT_CCC_NOTIFY + * @brief Client Characteristic Configuration Notification. + * + * If set, changes to Characteristic Value shall be notified. + */ +#define BT_GATT_CCC_NOTIFY 0x0001 +/** @def BT_GATT_CCC_INDICATE + * @brief Client Characteristic Configuration Indication. + * + * If set, changes to Characteristic Value shall be indicated. + */ +#define BT_GATT_CCC_INDICATE 0x0002 + +/* Client Characteristic Configuration Attribute Value */ +struct bt_gatt_ccc { + /** Client Characteristic Configuration flags */ + u16_t flags; +}; + +/** @brief GATT Characteristic Presentation Format Attribute Value. */ +struct bt_gatt_cpf { + /** Format of the value of the characteristic */ + u8_t format; + /** Exponent field to determine how the value of this characteristic is + * further formatted + */ + s8_t exponent; + /** Unit of the characteristic */ + u16_t unit; + /** Name space of the description */ + u8_t name_space; + /** Description of the characteristic as defined in a higher layer profile */ + u16_t description; +} __packed; + +/** + * @defgroup bt_gatt_server GATT Server APIs + * @ingroup bt_gatt + * @{ + */ + +/** @brief Register GATT service. + * + * Register GATT service. Applications can make use of + * macros such as BT_GATT_PRIMARY_SERVICE, BT_GATT_CHARACTERISTIC, + * BT_GATT_DESCRIPTOR, etc. + * + * @param svc Service containing the available attributes + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_service_register(struct bt_gatt_service *svc); + +/** @brief Unregister GATT service. + * * + * @param svc Service to be unregistered. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_service_unregister(struct bt_gatt_service *svc); + +enum { + BT_GATT_ITER_STOP = 0, + BT_GATT_ITER_CONTINUE, +}; + +/** @typedef bt_gatt_attr_func_t + * @brief Attribute iterator callback. + * + * @param attr Attribute found. + * @param user_data Data given. + * + * @return BT_GATT_ITER_CONTINUE if should continue to the next attribute + * or BT_GATT_ITER_STOP to stop. + */ +typedef u8_t (*bt_gatt_attr_func_t)(const struct bt_gatt_attr *attr, + void *user_data); + +/** @brief Attribute iterator by type. + * + * Iterate attributes in the given range matching given UUID and/or data. + * + * @param start_handle Start handle. + * @param end_handle End handle. + * @param uuid UUID to match, passing NULL skips UUID matching. + * @param attr_data Attribute data to match, passing NULL skips data matching. + * @param num_matches Number matches, passing 0 makes it unlimited. + * @param func Callback function. + * @param user_data Data to pass to the callback. + */ +void bt_gatt_foreach_attr_type(u16_t start_handle, u16_t end_handle, + const struct bt_uuid *uuid, + const void *attr_data, uint16_t num_matches, + bt_gatt_attr_func_t func, + void *user_data); + +/** @brief Attribute iterator. + * + * Iterate attributes in the given range. + * + * @param start_handle Start handle. + * @param end_handle End handle. + * @param func Callback function. + * @param user_data Data to pass to the callback. + */ +static inline void bt_gatt_foreach_attr(u16_t start_handle, u16_t end_handle, + bt_gatt_attr_func_t func, + void *user_data) +{ + bt_gatt_foreach_attr_type(start_handle, end_handle, NULL, NULL, 0, func, + user_data); +} + +/** @brief Iterate to the next attribute + * + * Iterate to the next attribute following a given attribute. + * + * @param attr Current Attribute. + * + * @return The next attribute or NULL if it cannot be found. + */ +struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr); + +/** @brief Get the handle of the characteristic value descriptor. + * + * @param attr A Characteristic Attribute + * + * @return the handle of the corresponding Characteristic Value. The + * value will be zero (the invalid handle) if @p attr was not a + * characteristic attribute. + */ +uint16_t bt_gatt_attr_value_handle(const struct bt_gatt_attr *attr); + +/** @brief Generic Read Attribute value helper. + * + * Read attribute value from local database storing the result into buffer. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value. + * @param buf_len Buffer length. + * @param offset Start offset. + * @param value Attribute value. + * @param value_len Length of the attribute value. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t buf_len, u16_t offset, + const void *value, u16_t value_len); + +/** @brief Read Service Attribute helper. + * + * Read service attribute value from local database storing the result into + * buffer after encoding it. + * NOTE: Only use this with attributes which user_data is a bt_uuid. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset); + +/** @def BT_GATT_SERVICE_DEFINE + * @brief Statically define and register a service. + * + * Helper macro to statically define and register a service. + * + * @param _name Service name. + */ +#define BT_GATT_SERVICE_DEFINE(_name, ...) \ + const struct bt_gatt_attr attr_##_name[] = { __VA_ARGS__ }; \ + const Z_STRUCT_SECTION_ITERABLE(bt_gatt_service_static, _name) =\ + BT_GATT_SERVICE(attr_##_name) + +/** @def BT_GATT_SERVICE + * @brief Service Structure Declaration Macro. + * + * Helper macro to declare a service structure. + * + * @param _attrs Service attributes. + */ +#define BT_GATT_SERVICE(_attrs) \ +{ \ + .attrs = _attrs, \ + .attr_count = ARRAY_SIZE(_attrs), \ +} + +/** @def BT_GATT_PRIMARY_SERVICE + * @brief Primary Service Declaration Macro. + * + * Helper macro to declare a primary service attribute. + * + * @param _service Service attribute value. + */ +#define BT_GATT_PRIMARY_SERVICE(_service) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_PRIMARY, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_service, NULL, _service) + +/** @def BT_GATT_SECONDARY_SERVICE + * @brief Secondary Service Declaration Macro. + * + * Helper macro to declare a secondary service attribute. + * + * @param _service Service attribute value. + */ +#define BT_GATT_SECONDARY_SERVICE(_service) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_SECONDARY, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_service, NULL, _service) + +/** @brief Read Include Attribute helper. + * + * Read include service attribute value from local database storing the result + * into buffer after encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_include. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset); + +/** @def BT_GATT_INCLUDE_SERVICE + * @brief Include Service Declaration Macro. + * + * Helper macro to declare database internal include service attribute. + * + * @param _service_incl the first service attribute of service to include + */ +#define BT_GATT_INCLUDE_SERVICE(_service_incl) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_INCLUDE, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_included, NULL, _service_incl) + +/** @brief Read Characteristic Attribute helper. + * + * Read characteristic attribute value from local database storing the result + * into buffer after encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_chrc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +/** @def BT_GATT_CHARACTERISTIC + * @brief Characteristic and Value Declaration Macro. + * + * Helper macro to declare a characteristic attribute along with its + * attribute value. + * + * @param _uuid Characteristic attribute uuid. + * @param _props Characteristic attribute properties. + * @param _perm Characteristic Attribute access permissions. + * @param _read Characteristic Attribute read callback. + * @param _write Characteristic Attribute write callback. + * @param _value Characteristic Attribute value. + */ +#define BT_GATT_CHARACTERISTIC(_uuid, _props, _perm, _read, _write, _value) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_chrc, NULL, \ + (&(struct bt_gatt_chrc) { .uuid = _uuid, \ + .value_handle = 0U, \ + .properties = _props, })), \ + BT_GATT_ATTRIBUTE(_uuid, _perm, _read, _write, _value) + +#if IS_ENABLED(CONFIG_BT_SETTINGS_CCC_LAZY_LOADING) + #define BT_GATT_CCC_MAX (CONFIG_BT_MAX_CONN) +#else + #define BT_GATT_CCC_MAX (CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN) +#endif + +/** @brief GATT CCC configuration entry. + * @param id Local identity, BT_ID_DEFAULT in most cases. + * @param peer Remote peer address + * @param value Configuration value. + * @param data Configuration pointer data. + */ +struct bt_gatt_ccc_cfg { + u8_t id; + bt_addr_le_t peer; + u16_t value; +}; + +/* Internal representation of CCC value */ +struct _bt_gatt_ccc { + struct bt_gatt_ccc_cfg cfg[BT_GATT_CCC_MAX]; + u16_t value; + void (*cfg_changed)(const struct bt_gatt_attr *attr, + u16_t value); + bool (*cfg_write)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + u16_t value); + bool (*cfg_match)(struct bt_conn *conn, + const struct bt_gatt_attr *attr); +}; + +/** @brief Read Client Characteristic Configuration Attribute helper. + * + * Read CCC attribute value from local database storing the result into buffer + * after encoding it. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +/** @brief Write Client Characteristic Configuration Attribute helper. + * + * Write value in the buffer into CCC attribute. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * @param flags Write flags. + * + * @return number of bytes written in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags); + + +/** @def BT_GATT_CCC_INITIALIZER + * @brief Initialize Client Characteristic Configuration Declaration Macro. + * + * Helper macro to initialize a Managed CCC attribute value. + * + * @param _changed Configuration changed callback. + * @param _write Configuration write callback. + * @param _match Configuration match callback. + */ +#define BT_GATT_CCC_INITIALIZER(_changed, _write, _match) \ + { \ + .cfg = {}, \ + .cfg_changed = _changed, \ + .cfg_write = _write, \ + .cfg_match = _match, \ + } + +/** @def BT_GATT_CCC_MANAGED + * @brief Managed Client Characteristic Configuration Declaration Macro. + * + * Helper macro to declare a Managed CCC attribute. + * + * @param _ccc CCC attribute user data, shall point to a _bt_gatt_ccc. + * @param _perm CCC access permissions. + */ +#define BT_GATT_CCC_MANAGED(_ccc, _perm) \ + BT_GATT_ATTRIBUTE(BT_UUID_GATT_CCC, _perm, \ + bt_gatt_attr_read_ccc, bt_gatt_attr_write_ccc, \ + _ccc) + +/** @def BT_GATT_CCC + * @brief Client Characteristic Configuration Declaration Macro. + * + * Helper macro to declare a CCC attribute. + * + * @param _changed Configuration changed callback. + * @param _perm CCC access permissions. + */ +#define BT_GATT_CCC(_changed, _perm) \ + BT_GATT_CCC_MANAGED((&(struct _bt_gatt_ccc) \ + BT_GATT_CCC_INITIALIZER(_changed, NULL, NULL)), _perm) + +/** @brief Read Characteristic Extended Properties Attribute helper + * + * Read CEP attribute value from local database storing the result into buffer + * after encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_cep. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cep(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +/** @def BT_GATT_CEP + * @brief Characteristic Extended Properties Declaration Macro. + * + * Helper macro to declare a CEP attribute. + * + * @param _value Descriptor attribute value. + */ +#define BT_GATT_CEP(_value) \ + BT_GATT_DESCRIPTOR(BT_UUID_GATT_CEP, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_cep, NULL, (void *)_value) + +/** @brief Read Characteristic User Description Descriptor Attribute helper + * + * Read CUD attribute value from local database storing the result into buffer + * after encoding it. + * NOTE: Only use this with attributes which user_data is a NULL-terminated C + * string. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cud(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +/** @def BT_GATT_CUD + * @brief Characteristic User Format Descriptor Declaration Macro. + * + * Helper macro to declare a CUD attribute. + * + * @param _value User description NULL-terminated C string. + * @param _perm Descriptor attribute access permissions. + */ +#define BT_GATT_CUD(_value, _perm) \ + BT_GATT_DESCRIPTOR(BT_UUID_GATT_CUD, _perm, bt_gatt_attr_read_cud, \ + NULL, (void *)_value) + +/** @brief Read Characteristic Presentation format Descriptor Attribute helper + * + * Read CPF attribute value from local database storing the result into buffer + * after encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_pf. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset); + +/** @def BT_GATT_CPF + * @brief Characteristic Presentation Format Descriptor Declaration Macro. + * + * Helper macro to declare a CPF attribute. + * + * @param _value Descriptor attribute value. + */ +#define BT_GATT_CPF(_value) \ + BT_GATT_DESCRIPTOR(BT_UUID_GATT_CPF, BT_GATT_PERM_READ, \ + bt_gatt_attr_read_cpf, NULL, (void *)_value) + +/** @def BT_GATT_DESCRIPTOR + * @brief Descriptor Declaration Macro. + * + * Helper macro to declare a descriptor attribute. + * + * @param _uuid Descriptor attribute uuid. + * @param _perm Descriptor attribute access permissions. + * @param _read Descriptor attribute read callback. + * @param _write Descriptor attribute write callback. + * @param _value Descriptor attribute value. + */ +#define BT_GATT_DESCRIPTOR(_uuid, _perm, _read, _write, _value) \ + BT_GATT_ATTRIBUTE(_uuid, _perm, _read, _write, _value) + +/** @def BT_GATT_ATTRIBUTE + * @brief Attribute Declaration Macro. + * + * Helper macro to declare an attribute. + * + * @param _uuid Attribute uuid. + * @param _perm Attribute access permissions. + * @param _read Attribute read callback. + * @param _write Attribute write callback. + * @param _value Attribute value. + */ +#define BT_GATT_ATTRIBUTE(_uuid, _perm, _read, _write, _value) \ +{ \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .user_data = _value, \ +} + +/** @brief Notification complete result callback. + * + * @param conn Connection object. + */ +typedef void (*bt_gatt_complete_func_t) (struct bt_conn *conn, void *user_data); + +struct bt_gatt_notify_params { + /** Notification Attribute UUID type */ + const struct bt_uuid *uuid; + /** Notification Attribute object*/ + const struct bt_gatt_attr *attr; + /** Notification Value data */ + const void *data; + /** Notification Value length */ + u16_t len; + /** Notification Value callback */ + bt_gatt_complete_func_t func; + /** Notification Value callback user data */ + void *user_data; +}; +#if defined(CONFIG_BLE_AT_CMD) +int bt_gatt_notify_at_cb(struct bt_conn *conn,struct bt_gatt_notify_params *params, u16_t attr_handle); +#endif + +/** @brief Notify attribute value change. + * + * This function works in the same way as @ref bt_gatt_notify. + * With the addition that after sending the notification the + * callback function will be called. + * + * The callback is run from System Workqueue context. + * + * Alternatively it is possible to notify by UUID by setting it on the + * parameters, when using this method the attribute given is used as the + * start range when looking up for possible matches. + * + * @param conn Connection object. + * @param params Notification parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_notify_cb(struct bt_conn *conn, + struct bt_gatt_notify_params *params); + +/** @brief Notify attribute value change. + * + * Send notification of attribute value change, if connection is NULL notify + * all peer that have notification enabled via CCC otherwise do a direct + * notification only the given connection. + * + * The attribute object on the parameters can be the so called Characteristic + * Declaration, which is usually declared with BT_GATT_CHARACTERISTIC followed + * by BT_GATT_CCC, or the Characteristic Value Declaration which is + * automatically created after the Characteristic Declaration when using + * BT_GATT_CHARACTERISTIC. + * + * @param conn Connection object. + * @param attr Characteristic or Characteristic Value attribute. + * @param data Pointer to Attribute data. + * @param len Attribute value length. + * + * @return 0 in case of success or negative value in case of error. + */ +static inline int bt_gatt_notify(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *data, u16_t len) +{ + struct bt_gatt_notify_params params; + + memset(¶ms, 0, sizeof(params)); + + params.attr = attr; + params.data = data; + params.len = len; + + return bt_gatt_notify_cb(conn, ¶ms); +} + +/** @typedef bt_gatt_indicate_func_t + * @brief Indication complete result callback. + * + * @param conn Connection object. + * @param attr Attribute object. + * @param err ATT error code + * + * @return 0 in case of success or negative value in case of error. + */ +typedef void (*bt_gatt_indicate_func_t)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + u8_t err); + +/** @brief GATT Indicate Value parameters */ +struct bt_gatt_indicate_params { + struct bt_att_req _req; + /** Notification Attribute UUID type */ + const struct bt_uuid *uuid; + /** Indicate Attribute object*/ + const struct bt_gatt_attr *attr; + /** Indicate Value callback */ + bt_gatt_indicate_func_t func; + /** Indicate Value data*/ + const void *data; + /** Indicate Value length*/ + u16_t len; +}; + +/** @brief Indicate attribute value change. + * + * Send an indication of attribute value change. if connection is NULL + * indicate all peer that have notification enabled via CCC otherwise do a + * direct indication only the given connection. + * + * The attribute object on the parameters can be the so called Characteristic + * Declaration, which is usually declared with BT_GATT_CHARACTERISTIC followed + * by BT_GATT_CCC, or the Characteristic Value Declaration which is + * automatically created after the Characteristic Declaration when using + * BT_GATT_CHARACTERISTIC. + * + * The callback is run from System Workqueue context. + * + * Alternatively it is possible to indicate by UUID by setting it on the + * parameters, when using this method the attribute given is used as the + * start range when looking up for possible matches. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Indicate parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params); + +#if defined(CONFIG_BT_STACK_PTS) +int service_change_test(struct bt_gatt_indicate_params *params,const struct bt_conn *con); + +#endif +/** @brief Check if connection have subscribed to attribute + * + * Check if connection has subscribed to attribute value change. + * + * The attribute object can be the so called Characteristic Declaration, + * which is usually declared with BT_GATT_CHARACTERISTIC followed + * by BT_GATT_CCC, or the Characteristic Value Declaration which is + * automatically created after the Characteristic Declaration when using + * BT_GATT_CHARACTERISTIC, or the Client Characteristic Configuration + * Descriptor (CCCD) which is created by BT_GATT_CCC. + * + * @param conn Connection object. + * @param attr Attribute object. + * @param ccc_value The subscription type, either notifications or indications. + * + * @return true if the attribute object has been subscribed. + */ +bool bt_gatt_is_subscribed(struct bt_conn *conn, + const struct bt_gatt_attr *attr, u16_t ccc_value); + +/** @brief Get ATT MTU for a connection + * + * Get negotiated ATT connection MTU, note that this does not equal the largest + * amount of attribute data that can be transferred within a single packet. + * + * @param conn Connection object. + * + * @return MTU in bytes + */ +u16_t bt_gatt_get_mtu(struct bt_conn *conn); + +/** @} */ + +/** + * @defgroup bt_gatt_client GATT Client APIs + * @ingroup bt_gatt + * @{ + */ + +/** @brief GATT Exchange MTU parameters */ +struct bt_gatt_exchange_params { + struct bt_att_req _req; + /** Response callback */ + void (*func)(struct bt_conn *conn, u8_t err, + struct bt_gatt_exchange_params *params); +}; + +/** @brief Exchange MTU + * + * This client procedure can be used to set the MTU to the maximum possible + * size the buffers can hold. + * + * NOTE: Shall only be used once per connection. + * + * @param conn Connection object. + * @param params Exchange MTU parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_exchange_mtu(struct bt_conn *conn, + struct bt_gatt_exchange_params *params); + +#if defined(CONFIG_BLE_AT_CMD) +int bt_at_gatt_exchange_mtu(struct bt_conn *conn, struct bt_gatt_exchange_params *params,u16_t mtu_size); +#endif + +struct bt_gatt_discover_params; + +/** @typedef bt_gatt_discover_func_t + * @brief Discover attribute callback function. + * + * @param conn Connection object. + * @param attr Attribute found. + * @param params Discovery parameters given. + * + * If discovery procedure has completed this callback will be called with + * attr set to NULL. This will not happen if procedure was stopped by returning + * BT_GATT_ITER_STOP. The attribute is read-only and cannot be cached without + * copying its contents. + * + * @return BT_GATT_ITER_CONTINUE if should continue attribute discovery + * or BT_GATT_ITER_STOP to stop discovery procedure. + */ +typedef u8_t (*bt_gatt_discover_func_t)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params); + +/* GATT Discover types */ +enum { + /** Discover Primary Services. */ + BT_GATT_DISCOVER_PRIMARY, + /** Discover Secondary Services. */ + BT_GATT_DISCOVER_SECONDARY, + /** Discover Included Services. */ + BT_GATT_DISCOVER_INCLUDE, + /** Discover Characteristic Values. + * + * Discover Characteristic Value and its properties. + */ + BT_GATT_DISCOVER_CHARACTERISTIC, + /** Discover Descriptors. + * + * Discover Attributes which are not services or characteristics. + * + * Note: The use of this type of discover is not recommended for + * discovering in ranges across multiple services/characteristics + * as it may incur in extra round trips. + */ + BT_GATT_DISCOVER_DESCRIPTOR, + /** Discover Attributes. + * + * Discover Attributes of any type. + * + * Note: The use of this type of discover is not recommended for + * discovering in ranges across multiple services/characteristics as + * it may incur in more round trips. + */ + BT_GATT_DISCOVER_ATTRIBUTE, +}; + +/** @brief GATT Discover Attributes parameters */ +struct bt_gatt_discover_params { + struct bt_att_req _req; + /** Discover UUID type */ + struct bt_uuid *uuid; + /** Discover attribute callback */ + bt_gatt_discover_func_t func; + union { + struct { + /** Include service attribute declaration handle */ + u16_t attr_handle; + /** Included service start handle */ + u16_t start_handle; + /** Included service end handle */ + u16_t end_handle; + } _included; + /** Discover start handle */ + u16_t start_handle; + }; + /** Discover end handle */ + u16_t end_handle; + /** Discover type */ + u8_t type; +}; + +/** @brief GATT Discover function + * + * This procedure is used by a client to discover attributes on a server. + * + * Primary Service Discovery: Procedure allows to discover specific Primary + * Service based on UUID. + * Include Service Discovery: Procedure allows to discover all Include Services + * within specified range. + * Characteristic Discovery: Procedure allows to discover all characteristics + * within specified handle range as well as + * discover characteristics with specified UUID. + * Descriptors Discovery: Procedure allows to discover all characteristic + * descriptors within specified range. + * + * For each attribute found the callback is called which can then decide + * whether to continue discovering or stop. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Discover parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_discover(struct bt_conn *conn, + struct bt_gatt_discover_params *params); + +struct bt_gatt_read_params; + +/** @typedef bt_gatt_read_func_t + * @brief Read callback function + * + * @param conn Connection object. + * @param err ATT error code. + * @param params Read parameters used. + * @param data Attribute value data. NULL means read has completed. + * @param length Attribute value length. + */ +typedef u8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, u8_t err, + struct bt_gatt_read_params *params, + const void *data, u16_t length); + +/** @brief GATT Read parameters + * @param func Read attribute callback + * @param handle_count If equals to 1 single.handle and single.offset + * are used. If >1 Read Multiple Characteristic + * Values is performed and handles are used. + * If equals to 0 by_uuid is used for Read Using + * Characteristic UUID. + * @param handle Attribute handle + * @param offset Attribute data offset + * @param handles Handles to read in Read Multiple Characteristic Values + * @param start_handle First requested handle number + * @param end_handle Last requested handle number + * @param uuid 2 or 16 octet UUID + */ +struct bt_gatt_read_params { + struct bt_att_req _req; + bt_gatt_read_func_t func; + size_t handle_count; + union { + struct { + u16_t handle; + u16_t offset; + } single; + u16_t *handles; + struct { + u16_t start_handle; + u16_t end_handle; + struct bt_uuid *uuid; + } by_uuid; + }; +}; + +/** @brief Read Attribute Value by handle + * + * This procedure read the attribute value and return it to the callback. + * + * When reading attributes by UUID the callback can be called multiple times + * depending on how many instances of given the UUID exists with the + * start_handle being updated for each instance. + * + * If an instance does contain a long value which cannot be read entirely the + * caller will need to read the remaining data separately using the handle and + * offset. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Read parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params); + +struct bt_gatt_write_params; + +/** @typedef bt_gatt_write_func_t + * @brief Write callback function + * + * @param conn Connection object. + * @param err ATT error code. + * @param params Write parameters used. + */ +typedef void (*bt_gatt_write_func_t)(struct bt_conn *conn, u8_t err, + struct bt_gatt_write_params *params); + +/** @brief GATT Write parameters */ +struct bt_gatt_write_params { + struct bt_att_req _req; + /** Response callback */ + bt_gatt_write_func_t func; + /** Attribute handle */ + u16_t handle; + /** Attribute data offset */ + u16_t offset; + /** Data to be written */ + const void *data; + /** Length of the data */ + u16_t length; +}; + +/** @brief Write Attribute Value by handle + * + * This procedure write the attribute value and return the result in the + * callback. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Write parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params); + + +#if defined(CONFIG_BT_STACK_PTS) +int bt_gatt_prepare_write(struct bt_conn *conn, + struct bt_gatt_write_params *params); + +#endif + +/** @brief Write Attribute Value by handle without response with callback. + * + * This function works in the same way as @ref bt_gatt_write_without_response. + * With the addition that after sending the write the callback function will be + * called. + * + * The callback is run from System Workqueue context. + * + * Note: By using a callback it also disable the internal flow control + * which would prevent sending multiple commands without waiting for their + * transmissions to complete, so if that is required the caller shall not + * submit more data until the callback is called. + * + * @param conn Connection object. + * @param handle Attribute handle. + * @param data Data to be written. + * @param length Data length. + * @param sign Whether to sign data + * @param func Transmission complete callback. + * @param user_data User data to be passed back to callback. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_write_without_response_cb(struct bt_conn *conn, u16_t handle, + const void *data, u16_t length, + bool sign, bt_gatt_complete_func_t func, + void *user_data); + +/** @brief Write Attribute Value by handle without response + * + * This procedure write the attribute value without requiring an + * acknowledgment that the write was successfully performed + * + * @param conn Connection object. + * @param handle Attribute handle. + * @param data Data to be written. + * @param length Data length. + * @param sign Whether to sign data + * + * @return 0 in case of success or negative value in case of error. + */ +static inline int bt_gatt_write_without_response(struct bt_conn *conn, + u16_t handle, const void *data, + u16_t length, bool sign) +{ + return bt_gatt_write_without_response_cb(conn, handle, data, length, + sign, NULL, NULL); +} + +struct bt_gatt_subscribe_params; + +/** @typedef bt_gatt_notify_func_t + * @brief Notification callback function + * + * @param conn Connection object. May be NULL, indicating that the peer is + * being unpaired + * @param params Subscription parameters. + * @param data Attribute value data. If NULL then subscription was removed. + * @param length Attribute value length. + */ +typedef u8_t (*bt_gatt_notify_func_t)(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, u16_t length); + +/* Subscription flags */ +enum { + /** Persistence flag + * + * If set, indicates that the subscription is not saved + * on the GATT server side. Therefore, upon disconnection, + * the subscription will be automatically removed + * from the client's subscriptions list and + * when the client reconnects, it will have to + * issue a new subscription. + */ + BT_GATT_SUBSCRIBE_FLAG_VOLATILE, + + /** Write pending flag + * + * If set, indicates write operation is pending waiting remote end to + * respond. + */ + BT_GATT_SUBSCRIBE_FLAG_WRITE_PENDING, + + BT_GATT_SUBSCRIBE_NUM_FLAGS +}; + +/** @brief GATT Subscribe parameters */ +struct bt_gatt_subscribe_params { + struct bt_att_req _req; + bt_addr_le_t _peer; + /** Notification value callback */ + bt_gatt_notify_func_t notify; + /** Subscribe value handle */ + u16_t value_handle; + /** Subscribe CCC handle */ + u16_t ccc_handle; + /** Subscribe value */ + u16_t value; + /** Subscription flags */ + ATOMIC_DEFINE(flags, BT_GATT_SUBSCRIBE_NUM_FLAGS); + + sys_snode_t node; +}; + +/** @brief Subscribe Attribute Value Notification + * + * This procedure subscribe to value notification using the Client + * Characteristic Configuration handle. + * If notification received subscribe value callback is called to return + * notified value. One may then decide whether to unsubscribe directly from + * this callback. Notification callback with NULL data will not be called if + * subscription was removed by this method. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Subscribe parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_subscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params); + +/** @brief Unsubscribe Attribute Value Notification + * + * This procedure unsubscribe to value notification using the Client + * Characteristic Configuration handle. Notification callback with NULL data + * will be called if subscription was removed by this call, until then the + * parameters cannot be reused. + * + * @param conn Connection object. + * @param params Subscribe parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_unsubscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params); + +/** @brief Cancel GATT pending request + * + * @param conn Connection object. + * @param params Requested params address. + */ +void bt_gatt_cancel(struct bt_conn *conn, void *params); + +#if defined(BFLB_BLE_MTU_CHANGE_CB) +typedef void (*bt_gatt_mtu_changed_cb_t)(struct bt_conn *conn, int mtu); +void bt_gatt_register_mtu_callback(bt_gatt_mtu_changed_cb_t cb); +#endif + +#if defined(BFLB_BLE) +/** @brief load gatt ccc from flash + * + * @param void. + * @param void. + */ +void bt_gatt_ccc_load(void); +#endif + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_GATT_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/hci_err.h b/components/ble/ble_stack/include/bluetooth/hci_err.h new file mode 100644 index 00000000..fa4951ea --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/hci_err.h @@ -0,0 +1,62 @@ +/** @file + * @brief Bluetooth Host Control Interface status codes. + */ + +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* HCI Error Codes, BT Core spec [Vol 2, Part D]. */ +#define BT_HCI_ERR_SUCCESS 0x00 +#define BT_HCI_ERR_UNKNOWN_CMD 0x01 +#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02 +#define BT_HCI_ERR_HW_FAILURE 0x03 +#define BT_HCI_ERR_PAGE_TIMEOUT 0x04 +#define BT_HCI_ERR_AUTH_FAIL 0x05 +#define BT_HCI_ERR_PIN_OR_KEY_MISSING 0x06 +#define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED 0x07 +#define BT_HCI_ERR_CONN_TIMEOUT 0x08 +#define BT_HCI_ERR_CONN_LIMIT_EXCEEDED 0x09 +#define BT_HCI_ERR_SYNC_CONN_LIMIT_EXCEEDED 0x0a +#define BT_HCI_ERR_CONN_ALREADY_EXISTS 0x0b +#define BT_HCI_ERR_CMD_DISALLOWED 0x0c +#define BT_HCI_ERR_INSUFFICIENT_RESOURCES 0x0d +#define BT_HCI_ERR_INSUFFICIENT_SECURITY 0x0e +#define BT_HCI_ERR_BD_ADDR_UNACCEPTABLE 0x0f +#define BT_HCI_ERR_CONN_ACCEPT_TIMEOUT 0x10 +#define BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL 0x11 +#define BT_HCI_ERR_INVALID_PARAM 0x12 +#define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13 +#define BT_HCI_ERR_REMOTE_LOW_RESOURCES 0x14 +#define BT_HCI_ERR_REMOTE_POWER_OFF 0x15 +#define BT_HCI_ERR_LOCALHOST_TERM_CONN 0x16 +#define BT_HCI_ERR_PAIRING_NOT_ALLOWED 0x18 +#define BT_HCI_ERR_UNSUPP_REMOTE_FEATURE 0x1a +#define BT_HCI_ERR_INVALID_LL_PARAM 0x1e +#define BT_HCI_ERR_UNSPECIFIED 0x1f +#define BT_HCI_ERR_UNSUPP_LL_PARAM_VAL 0x20 +#define BT_HCI_ERR_LL_RESP_TIMEOUT 0x22 +#define BT_HCI_ERR_LL_PROC_COLLISION 0x23 +#define BT_HCI_ERR_INSTANT_PASSED 0x28 +#define BT_HCI_ERR_PAIRING_NOT_SUPPORTED 0x29 +#define BT_HCI_ERR_DIFF_TRANS_COLLISION 0x2a +#define BT_HCI_ERR_UNACCEPT_CONN_PARAM 0x3b +#define BT_HCI_ERR_ADV_TIMEOUT 0x3c +#define BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL 0x3d +#define BT_HCI_ERR_CONN_FAIL_TO_ESTAB 0x3e + +#define BT_HCI_ERR_AUTHENTICATION_FAIL __DEPRECATED_MACRO BT_HCI_ERR_AUTH_FAIL + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_STATUS_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/hci_host.h b/components/ble/ble_stack/include/bluetooth/hci_host.h new file mode 100644 index 00000000..cbfdb0e5 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/hci_host.h @@ -0,0 +1,2627 @@ +/* hci.h - Bluetooth Host Control Interface definitions */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BT_HCI_HOST_H +#define __BT_HCI_HOST_H + +#include +#include +#include +#include +#include +#include +#include + +#if defined(BFLB_BLE) +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Special own address types for LL privacy (used in adv & scan parameters) */ +#define BT_HCI_OWN_ADDR_RPA_OR_PUBLIC 0x02 +#define BT_HCI_OWN_ADDR_RPA_OR_RANDOM 0x03 +#define BT_HCI_OWN_ADDR_RPA_MASK 0x02 + +#define BT_ENC_KEY_SIZE_MIN 0x07 +#define BT_ENC_KEY_SIZE_MAX 0x10 + +struct bt_hci_evt_hdr { + u8_t evt; + u8_t len; +} __packed; +#define BT_HCI_EVT_HDR_SIZE 2 + +#define BT_ACL_START_NO_FLUSH 0x00 +#define BT_ACL_CONT 0x01 +#define BT_ACL_START 0x02 + +#define bt_acl_handle(h) ((h) & 0x0fff) +#define bt_acl_flags(h) ((h) >> 12) +#define bt_acl_handle_pack(h, f) ((h) | ((f) << 12)) + +struct bt_hci_acl_hdr { + u16_t handle; + u16_t len; +} __packed; +#define BT_HCI_ACL_HDR_SIZE 4 + +#define BT_ISO_START 0x00 +#define BT_ISO_CONT 0x01 +#define BT_ISO_SINGLE 0x02 +#define BT_ISO_END 0x03 + +#define bt_iso_handle(h) ((h) & 0x0fff) +#define bt_iso_flags(h) ((h) >> 12) +#define bt_iso_flags_pb(f) ((f) & 0x0003) +#define bt_iso_flags_ts(f) (((f) >> 2) & 0x0001) +#define bt_iso_pack_flags(pb, ts) \ + (((pb) & 0x0003) | (((ts) & 0x0001) << 2)) +#define bt_iso_handle_pack(h, pb, ts) \ + ((h) | (bt_iso_pack_flags(pb, ts) << 12)) + +#define BT_ISO_DATA_VALID 0x00 +#define BT_ISO_DATA_INVALID 0x01 +#define BT_ISO_DATA_NOP 0x02 + +#define bt_iso_pkt_len(h) ((h) & 0x3fff) +#define bt_iso_pkt_flags(h) ((h) >> 14) +#define bt_iso_pkt_len_pack(h, f) ((h) | ((f) << 14)) + +struct bt_hci_iso_data_hdr { + uint16_t sn; + uint16_t slen; +} __packed; +#define BT_HCI_ISO_DATA_HDR_SIZE 4 + +struct bt_hci_iso_ts_data_hdr { + uint32_t ts; + struct bt_hci_iso_data_hdr data; +} __packed; +#define BT_HCI_ISO_TS_DATA_HDR_SIZE 8 + +struct bt_hci_iso_hdr { + uint16_t handle; + uint16_t len; +} __packed; +#define BT_HCI_ISO_HDR_SIZE 4 + +struct bt_hci_cmd_hdr { + u16_t opcode; + u8_t param_len; +} __packed; +#define BT_HCI_CMD_HDR_SIZE 3 + +/* Supported Commands */ +#define BT_CMD_TEST(cmd, octet, bit) (cmd[octet] & BIT(bit)) +#define BT_CMD_LE_STATES(cmd) BT_CMD_TEST(cmd, 28, 3) + +#define BT_FEAT_TEST(feat, page, octet, bit) (feat[page][octet] & BIT(bit)) + +#define BT_FEAT_BREDR(feat) !BT_FEAT_TEST(feat, 0, 4, 5) +#define BT_FEAT_LE(feat) BT_FEAT_TEST(feat, 0, 4, 6) +#define BT_FEAT_EXT_FEATURES(feat) BT_FEAT_TEST(feat, 0, 7, 7) +#define BT_FEAT_HOST_SSP(feat) BT_FEAT_TEST(feat, 1, 0, 0) +#define BT_FEAT_SC(feat) BT_FEAT_TEST(feat, 2, 1, 0) + +#define BT_FEAT_LMP_ESCO_CAPABLE(feat) BT_FEAT_TEST(feat, 0, 3, 7) +#define BT_FEAT_HV2_PKT(feat) BT_FEAT_TEST(feat, 0, 1, 4) +#define BT_FEAT_HV3_PKT(feat) BT_FEAT_TEST(feat, 0, 1, 5) +#define BT_FEAT_EV4_PKT(feat) BT_FEAT_TEST(feat, 0, 4, 0) +#define BT_FEAT_EV5_PKT(feat) BT_FEAT_TEST(feat, 0, 4, 1) +#define BT_FEAT_2EV3_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 5) +#define BT_FEAT_3EV3_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 6) +#define BT_FEAT_3SLOT_PKT(feat) BT_FEAT_TEST(feat, 0, 5, 7) + +/* LE features */ +#define BT_LE_FEAT_BIT_ENC 0 +#define BT_LE_FEAT_BIT_CONN_PARAM_REQ 1 +#define BT_LE_FEAT_BIT_EXT_REJ_IND 2 +#define BT_LE_FEAT_BIT_SLAVE_FEAT_REQ 3 +#define BT_LE_FEAT_BIT_PING 4 +#define BT_LE_FEAT_BIT_DLE 5 +#define BT_LE_FEAT_BIT_PRIVACY 6 +#define BT_LE_FEAT_BIT_EXT_SCAN 7 +#define BT_LE_FEAT_BIT_PHY_2M 8 +#define BT_LE_FEAT_BIT_SMI_TX 9 +#define BT_LE_FEAT_BIT_SMI_RX 10 +#define BT_LE_FEAT_BIT_PHY_CODED 11 +#define BT_LE_FEAT_BIT_ADV_EXT 12 +#define BT_LE_FEAT_BIT_ADV_PER 13 +#define BT_LE_FEAT_BIT_CHAN_SEL_ALGO_2 14 +#define BT_LE_FEAT_BIT_PWR_CLASS_1 15 +#define BT_LE_FEAT_BIT_MIN_USED_CHAN_PROC 16 +#define BT_LE_FEAT_BIT_CONN_CTE_REQ 17 +#define BT_LE_FEAT_BIT_CONN_CTE_RESP 18 +#define BT_LE_FEAT_BIT_CONNECTIONLESS_CTE_TX 19 +#define BT_LE_FEAT_BIT_CONNECTIONLESS_CTE_RX 20 +#define BT_LE_FEAT_BIT_ANT_SWITCH_TX_AOD 21 +#define BT_LE_FEAT_BIT_ANT_SWITCH_RX_AOA 22 +#define BT_LE_FEAT_BIT_RX_CTE 23 +#define BT_LE_FEAT_BIT_PAST_SEND 24 +#define BT_LE_FEAT_BIT_PAST_RECV 25 +#define BT_LE_FEAT_BIT_SCA_UPDATE 26 +#define BT_LE_FEAT_BIT_REMOTE_PUB_KEY_VALIDATE 27 +#define BT_LE_FEAT_BIT_CIS_MASTER 28 +#define BT_LE_FEAT_BIT_CIS_SLAVE 29 +#define BT_LE_FEAT_BIT_ISO_BROADCASTER 30 +#define BT_LE_FEAT_BIT_SYNC_RECEIVER 31 +#define BT_LE_FEAT_BIT_ISO_CHANNELS 32 +#define BT_LE_FEAT_BIT_PWR_CTRL_REQ 33 +#define BT_LE_FEAT_BIT_PWR_CHG_IND 34 +#define BT_LE_FEAT_BIT_PATH_LOSS_MONITOR 35 + +#define BT_LE_FEAT_TEST(feat, n) (feat[(n) >> 3] & \ + BIT((n) & 7)) + +#define BT_FEAT_LE_ENCR(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_ENC) +#define BT_FEAT_LE_CONN_PARAM_REQ_PROC(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CONN_PARAM_REQ) +#define BT_FEAT_LE_SLAVE_FEATURE_XCHG(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_SLAVE_FEAT_REQ) +#define BT_FEAT_LE_DLE(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_DLE) +#define BT_FEAT_LE_PHY_2M(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PHY_2M) +#define BT_FEAT_LE_PHY_CODED(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PHY_CODED) +#define BT_FEAT_LE_PRIVACY(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PRIVACY) +#define BT_FEAT_LE_EXT_ADV(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_EXT_ADV) +#define BT_FEAT_LE_EXT_PER_ADV(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PER_ADV) +#define BT_FEAT_LE_CONNECTIONLESS_CTE_TX(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CONNECTIONLESS_CTE_TX) +#define BT_FEAT_LE_ANT_SWITCH_TX_AOD(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_ANT_SWITCH_TX_AOD) +#define BT_FEAT_LE_PAST_SEND(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PAST_SEND) +#define BT_FEAT_LE_PAST_RECV(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_PAST_RECV) +#define BT_FEAT_LE_CIS_MASTER(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CIS_MASTER) +#define BT_FEAT_LE_CIS_SLAVE(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_CIS_SLAVE) +#define BT_FEAT_LE_ISO_BROADCASTER(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_ISO_BROADCASTER) +#define BT_FEAT_LE_SYNC_RECEIVER(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_SYNC_RECEIVER) +#define BT_FEAT_LE_ISO_CHANNELS(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_ISO_CHANNELS) + +#define BT_FEAT_LE_CIS(feat) (BT_FEAT_LE_CIS_MASTER(feat) | \ + BT_FEAT_LE_CIS_SLAVE(feat)) +#define BT_FEAT_LE_BIS(feat) (BT_FEAT_LE_ISO_BROADCASTER(feat) | \ + BT_FEAT_LE_SYNC_RECEIVER(feat)) +#define BT_FEAT_LE_ISO(feat) (BT_FEAT_LE_CIS(feat) | \ + BT_FEAT_LE_BIS(feat)) + +/* LE States */ +#define BT_LE_STATES_SLAVE_CONN_ADV(states) (states & 0x0000004000000000) + +/* Bonding/authentication types */ +#define BT_HCI_NO_BONDING 0x00 +#define BT_HCI_NO_BONDING_MITM 0x01 +#define BT_HCI_DEDICATED_BONDING 0x02 +#define BT_HCI_DEDICATED_BONDING_MITM 0x03 +#define BT_HCI_GENERAL_BONDING 0x04 +#define BT_HCI_GENERAL_BONDING_MITM 0x05 + +/* + * MITM protection is enabled in SSP authentication requirements octet when + * LSB bit is set. + */ +#define BT_MITM 0x01 + +/* I/O capabilities */ +#define BT_IO_DISPLAY_ONLY 0x00 +#define BT_IO_DISPLAY_YESNO 0x01 +#define BT_IO_KEYBOARD_ONLY 0x02 +#define BT_IO_NO_INPUT_OUTPUT 0x03 + +/* SCO packet types */ +#define HCI_PKT_TYPE_HV1 0x0020 +#define HCI_PKT_TYPE_HV2 0x0040 +#define HCI_PKT_TYPE_HV3 0x0080 + +/* eSCO packet types */ +#define HCI_PKT_TYPE_ESCO_HV1 0x0001 +#define HCI_PKT_TYPE_ESCO_HV2 0x0002 +#define HCI_PKT_TYPE_ESCO_HV3 0x0004 +#define HCI_PKT_TYPE_ESCO_EV3 0x0008 +#define HCI_PKT_TYPE_ESCO_EV4 0x0010 +#define HCI_PKT_TYPE_ESCO_EV5 0x0020 +#define HCI_PKT_TYPE_ESCO_2EV3 0x0040 +#define HCI_PKT_TYPE_ESCO_3EV3 0x0080 +#define HCI_PKT_TYPE_ESCO_2EV5 0x0100 +#define HCI_PKT_TYPE_ESCO_3EV5 0x0200 + + +#define ESCO_PKT_MASK (HCI_PKT_TYPE_ESCO_HV1 | \ + HCI_PKT_TYPE_ESCO_HV2 | \ + HCI_PKT_TYPE_ESCO_HV3) +#define SCO_PKT_MASK (HCI_PKT_TYPE_HV1 | \ + HCI_PKT_TYPE_HV2 | \ + HCI_PKT_TYPE_HV3) +#define EDR_ESCO_PKT_MASK (HCI_PKT_TYPE_ESCO_2EV3 | \ + HCI_PKT_TYPE_ESCO_3EV3 | \ + HCI_PKT_TYPE_ESCO_2EV5 | \ + HCI_PKT_TYPE_ESCO_3EV5) + +/* HCI BR/EDR link types */ +#define BT_HCI_SCO 0x00 +#define BT_HCI_ACL 0x01 +#define BT_HCI_ESCO 0x02 + +/* OpCode Group Fields */ +#define BT_OGF_LINK_CTRL 0x01 +#define BT_OGF_BASEBAND 0x03 +#define BT_OGF_INFO 0x04 +#define BT_OGF_STATUS 0x05 +#define BT_OGF_LE 0x08 +#define BT_OGF_VS 0x3f + +/* Construct OpCode from OGF and OCF */ +#define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) + +/* Invalid opcode */ +#define BT_OP_NOP 0x0000 + +/* Obtain OGF from OpCode */ +#define BT_OGF(opcode) (((opcode) >> 10) & BIT_MASK(6)) +/* Obtain OCF from OpCode */ +#define BT_OCF(opcode) ((opcode) & BIT_MASK(10)) + +#define BT_HCI_OP_INQUIRY BT_OP(BT_OGF_LINK_CTRL, 0x0001) +struct bt_hci_op_inquiry { + u8_t lap[3]; + u8_t length; + u8_t num_rsp; +} __packed; + +#define BT_HCI_OP_INQUIRY_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x0002) + +#define BT_HCI_OP_CONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0005) +struct bt_hci_cp_connect { + bt_addr_t bdaddr; + u16_t packet_type; + u8_t pscan_rep_mode; + u8_t reserved; + u16_t clock_offset; + u8_t allow_role_switch; +} __packed; + +#define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006) +struct bt_hci_cp_disconnect { + u16_t handle; + u8_t reason; +} __packed; + +#define BT_HCI_OP_CONNECT_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x0008) +struct bt_hci_cp_connect_cancel { + bt_addr_t bdaddr; +} __packed; +struct bt_hci_rp_connect_cancel { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_ACCEPT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x0009) +struct bt_hci_cp_accept_conn_req { + bt_addr_t bdaddr; + u8_t role; +} __packed; + +#define BT_HCI_OP_SETUP_SYNC_CONN BT_OP(BT_OGF_LINK_CTRL, 0x0028) +struct bt_hci_cp_setup_sync_conn { + u16_t handle; + u32_t tx_bandwidth; + u32_t rx_bandwidth; + u16_t max_latency; + u16_t content_format; + u8_t retrans_effort; + u16_t pkt_type; +} __packed; + +#define BT_HCI_OP_ACCEPT_SYNC_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x0029) +struct bt_hci_cp_accept_sync_conn_req { + bt_addr_t bdaddr; + u32_t tx_bandwidth; + u32_t rx_bandwidth; + u16_t max_latency; + u16_t content_format; + u8_t retrans_effort; + u16_t pkt_type; +} __packed; + +#define BT_HCI_OP_REJECT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x000a) +struct bt_hci_cp_reject_conn_req { + bt_addr_t bdaddr; + u8_t reason; +} __packed; + +#define BT_HCI_OP_LINK_KEY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000b) +struct bt_hci_cp_link_key_reply { + bt_addr_t bdaddr; + u8_t link_key[16]; +} __packed; + +#define BT_HCI_OP_LINK_KEY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000c) +struct bt_hci_cp_link_key_neg_reply { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_PIN_CODE_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000d) +struct bt_hci_cp_pin_code_reply { + bt_addr_t bdaddr; + u8_t pin_len; + u8_t pin_code[16]; +} __packed; +struct bt_hci_rp_pin_code_reply { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_PIN_CODE_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000e) +struct bt_hci_cp_pin_code_neg_reply { + bt_addr_t bdaddr; +} __packed; +struct bt_hci_rp_pin_code_neg_reply { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_AUTH_REQUESTED BT_OP(BT_OGF_LINK_CTRL, 0x0011) +struct bt_hci_cp_auth_requested { + u16_t handle; +} __packed; + +#define BT_HCI_OP_SET_CONN_ENCRYPT BT_OP(BT_OGF_LINK_CTRL, 0x0013) +struct bt_hci_cp_set_conn_encrypt { + u16_t handle; + u8_t encrypt; +} __packed; + +#define BT_HCI_OP_REMOTE_NAME_REQUEST BT_OP(BT_OGF_LINK_CTRL, 0x0019) +struct bt_hci_cp_remote_name_request { + bt_addr_t bdaddr; + u8_t pscan_rep_mode; + u8_t reserved; + u16_t clock_offset; +} __packed; + +#define BT_HCI_OP_REMOTE_NAME_CANCEL BT_OP(BT_OGF_LINK_CTRL, 0x001a) +struct bt_hci_cp_remote_name_cancel { + bt_addr_t bdaddr; +} __packed; +struct bt_hci_rp_remote_name_cancel { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_READ_REMOTE_FEATURES BT_OP(BT_OGF_LINK_CTRL, 0x001b) +struct bt_hci_cp_read_remote_features { + u16_t handle; +} __packed; + +#define BT_HCI_OP_READ_REMOTE_EXT_FEATURES BT_OP(BT_OGF_LINK_CTRL, 0x001c) +struct bt_hci_cp_read_remote_ext_features { + u16_t handle; + u8_t page; +} __packed; + +#define BT_HCI_OP_READ_REMOTE_VERSION_INFO BT_OP(BT_OGF_LINK_CTRL, 0x001d) +struct bt_hci_cp_read_remote_version_info { + u16_t handle; +} __packed; + +#define BT_HCI_OP_IO_CAPABILITY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002b) +struct bt_hci_cp_io_capability_reply { + bt_addr_t bdaddr; + u8_t capability; + u8_t oob_data; + u8_t authentication; +} __packed; + +#define BT_HCI_OP_USER_CONFIRM_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002c) +#define BT_HCI_OP_USER_CONFIRM_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002d) +struct bt_hci_cp_user_confirm_reply { + bt_addr_t bdaddr; +} __packed; +struct bt_hci_rp_user_confirm_reply { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_USER_PASSKEY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002e) +struct bt_hci_cp_user_passkey_reply { + bt_addr_t bdaddr; + u32_t passkey; +} __packed; + +#define BT_HCI_OP_USER_PASSKEY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002f) +struct bt_hci_cp_user_passkey_neg_reply { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_IO_CAPABILITY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x0034) +struct bt_hci_cp_io_capability_neg_reply { + bt_addr_t bdaddr; + u8_t reason; +} __packed; + +#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001) +struct bt_hci_cp_set_event_mask { + u8_t events[8]; +} __packed; + +#define BT_HCI_OP_RESET BT_OP(BT_OGF_BASEBAND, 0x0003) + +#define BT_HCI_OP_WRITE_LOCAL_NAME BT_OP(BT_OGF_BASEBAND, 0x0013) +struct bt_hci_write_local_name { + u8_t local_name[248]; +} __packed; + +#define BT_HCI_OP_WRITE_PAGE_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x0018) + +#define BT_HCI_OP_WRITE_SCAN_ENABLE BT_OP(BT_OGF_BASEBAND, 0x001a) +#define BT_BREDR_SCAN_DISABLED 0x00 +#define BT_BREDR_SCAN_INQUIRY 0x01 +#define BT_BREDR_SCAN_PAGE 0x02 + +#define BT_TX_POWER_LEVEL_CURRENT 0x00 +#define BT_TX_POWER_LEVEL_MAX 0x01 +#define BT_HCI_OP_READ_TX_POWER_LEVEL BT_OP(BT_OGF_BASEBAND, 0x002d) +struct bt_hci_cp_read_tx_power_level { + u16_t handle; + u8_t type; +} __packed; + +struct bt_hci_rp_read_tx_power_level { + u8_t status; + u16_t handle; + s8_t tx_power_level; +} __packed; + +#define BT_HCI_CTL_TO_HOST_FLOW_DISABLE 0x00 +#define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01 +#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031) +struct bt_hci_cp_set_ctl_to_host_flow { + u8_t flow_enable; +} __packed; + +#define BT_HCI_OP_HOST_BUFFER_SIZE BT_OP(BT_OGF_BASEBAND, 0x0033) +struct bt_hci_cp_host_buffer_size { + u16_t acl_mtu; + u8_t sco_mtu; + u16_t acl_pkts; + u16_t sco_pkts; +} __packed; + +struct bt_hci_handle_count { + u16_t handle; + u16_t count; +} __packed; + +#define BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS BT_OP(BT_OGF_BASEBAND, 0x0035) +struct bt_hci_cp_host_num_completed_packets { + u8_t num_handles; + struct bt_hci_handle_count h[0]; +} __packed; + +#define BT_HCI_OP_WRITE_INQUIRY_MODE BT_OP(BT_OGF_BASEBAND, 0x0045) +struct bt_hci_cp_write_inquiry_mode { + u8_t mode; +} __packed; + +#define BT_HCI_OP_WRITE_EXT_INQUIRY_RESP BT_OP(BT_OGF_BASEBAND, 0x0052) +struct bt_hci_cp_write_ext_inquiry_resp { + u8_t rec; + u8_t eir[240]; +} __packed; + +#define BT_HCI_OP_WRITE_SSP_MODE BT_OP(BT_OGF_BASEBAND, 0x0056) +struct bt_hci_cp_write_ssp_mode { + u8_t mode; +} __packed; + +#define BT_HCI_OP_SET_EVENT_MASK_PAGE_2 BT_OP(BT_OGF_BASEBAND, 0x0063) +struct bt_hci_cp_set_event_mask_page_2 { + u8_t events_page_2[8]; +} __packed; + +#define BT_HCI_OP_LE_WRITE_LE_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x006d) +struct bt_hci_cp_write_le_host_supp { + u8_t le; + u8_t simul; +} __packed; + +#define BT_HCI_OP_WRITE_SC_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x007a) +struct bt_hci_cp_write_sc_host_supp { + u8_t sc_support; +} __packed; + +#define BT_HCI_OP_READ_AUTH_PAYLOAD_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x007b) +struct bt_hci_cp_read_auth_payload_timeout { + u16_t handle; +} __packed; + +struct bt_hci_rp_read_auth_payload_timeout { + u8_t status; + u16_t handle; + u16_t auth_payload_timeout; +} __packed; + +#define BT_HCI_OP_WRITE_AUTH_PAYLOAD_TIMEOUT BT_OP(BT_OGF_BASEBAND, 0x007c) +struct bt_hci_cp_write_auth_payload_timeout { + u16_t handle; + u16_t auth_payload_timeout; +} __packed; + +struct bt_hci_rp_write_auth_payload_timeout { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_CONFIGURE_DATA_PATH BT_OP(BT_OGF_BASEBAND, 0x0083) +struct bt_hci_cp_configure_data_path { + uint8_t data_path_dir; + uint8_t data_path_id; + uint8_t vs_config_len; + uint8_t vs_config[0]; +} __packed; + +struct bt_hci_rp_configure_data_path { + uint8_t status; +} __packed; + +/* HCI version from Assigned Numbers */ +#define BT_HCI_VERSION_1_0B 0 +#define BT_HCI_VERSION_1_1 1 +#define BT_HCI_VERSION_1_2 2 +#define BT_HCI_VERSION_2_0 3 +#define BT_HCI_VERSION_2_1 4 +#define BT_HCI_VERSION_3_0 5 +#define BT_HCI_VERSION_4_0 6 +#define BT_HCI_VERSION_4_1 7 +#define BT_HCI_VERSION_4_2 8 +#define BT_HCI_VERSION_5_0 9 +#define BT_HCI_VERSION_5_1 10 +#define BT_HCI_VERSION_5_2 11 + +#define BT_HCI_OP_READ_LOCAL_VERSION_INFO BT_OP(BT_OGF_INFO, 0x0001) +struct bt_hci_rp_read_local_version_info { + u8_t status; + u8_t hci_version; + u16_t hci_revision; + u8_t lmp_version; + u16_t manufacturer; + u16_t lmp_subversion; +} __packed; + +#define BT_HCI_OP_READ_SUPPORTED_COMMANDS BT_OP(BT_OGF_INFO, 0x0002) +struct bt_hci_rp_read_supported_commands { + u8_t status; + u8_t commands[64]; +} __packed; + +#define BT_HCI_OP_READ_LOCAL_EXT_FEATURES BT_OP(BT_OGF_INFO, 0x0004) +struct bt_hci_cp_read_local_ext_features { + u8_t page; +}; +struct bt_hci_rp_read_local_ext_features { + u8_t status; + u8_t page; + u8_t max_page; + u8_t ext_features[8]; +} __packed; + +#define BT_HCI_OP_READ_LOCAL_FEATURES BT_OP(BT_OGF_INFO, 0x0003) +struct bt_hci_rp_read_local_features { + u8_t status; + u8_t features[8]; +} __packed; + +#define BT_HCI_OP_READ_BUFFER_SIZE BT_OP(BT_OGF_INFO, 0x0005) +struct bt_hci_rp_read_buffer_size { + u8_t status; + u16_t acl_max_len; + u8_t sco_max_len; + u16_t acl_max_num; + u16_t sco_max_num; +} __packed; + +#define BT_HCI_OP_READ_BD_ADDR BT_OP(BT_OGF_INFO, 0x0009) +struct bt_hci_rp_read_bd_addr { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +/* logic transport type bits as returned when reading supported codecs */ +#define BT_HCI_CODEC_TRANSPORT_MASK_BREDR_ACL BIT(0) +#define BT_HCI_CODEC_TRANSPORT_MASK_BREDR_SCO BIT(1) +#define BT_HCI_CODEC_TRANSPORT_MASK_LE_CIS BIT(2) +#define BT_HCI_CODEC_TRANSPORT_MASK_LE_BIS BIT(3) + +/* logic transport types for reading codec capabilities and controller delays */ +#define BT_HCI_LOGICAL_TRANSPORT_TYPE_BREDR_ACL 0x00 +#define BT_HCI_LOGICAL_TRANSPORT_TYPE_BREDR_SCO 0x01 +#define BT_HCI_LOGICAL_TRANSPORT_TYPE_LE_CIS 0x02 +#define BT_HCI_LOGICAL_TRANSPORT_TYPE_LE_BIS 0x03 + +/* audio datapath directions */ +#define BT_HCI_DATAPATH_DIR_HOST_TO_CTLR 0x00 +#define BT_HCI_DATAPATH_DIR_CTLR_TO_HOST 0x01 + +/* coding format assigned numbers, used for codec IDs */ +#define BT_HCI_CODING_FORMAT_ULAW_LOG 0x00 +#define BT_HCI_CODING_FORMAT_ALAW_LOG 0x01 +#define BT_HCI_CODING_FORMAT_CVSD 0x02 +#define BT_HCI_CODING_FORMAT_TRANSPARENT 0x03 +#define BT_HCI_CODING_FORMAT_LINEAR_PCM 0x04 +#define BT_HCI_CODING_FORMAT_MSBC 0x05 +#define BT_HCI_CODING_FORMAT_VS 0xFF + + +#define BT_HCI_OP_READ_CODECS BT_OP(BT_OGF_INFO, 0x000b) +struct bt_hci_std_codec_info { + uint8_t codec_id; +} __packed; +struct bt_hci_std_codecs { + uint8_t num_codecs; + struct bt_hci_std_codec_info codec_info[0]; +} __packed; +struct bt_hci_vs_codec_info { + uint16_t company_id; + uint16_t codec_id; +} __packed; +struct bt_hci_vs_codecs { + uint8_t num_codecs; + struct bt_hci_vs_codec_info codec_info[0]; +} __packed; +struct bt_hci_rp_read_codecs { + uint8_t status; + /* other fields filled in dynamically */ + uint8_t codecs[0]; +} __packed; + +#define BT_HCI_OP_READ_CODECS_V2 BT_OP(BT_OGF_INFO, 0x000d) +struct bt_hci_std_codec_info_v2 { + uint8_t codec_id; + uint8_t transports; /* bitmap */ +} __packed; +struct bt_hci_std_codecs_v2 { + uint8_t num_codecs; + struct bt_hci_std_codec_info_v2 codec_info[0]; +} __packed; +struct bt_hci_vs_codec_info_v2 { + uint16_t company_id; + uint16_t codec_id; + uint8_t transports; /* bitmap */ +} __packed; +struct bt_hci_vs_codecs_v2 { + uint8_t num_codecs; + struct bt_hci_vs_codec_info_v2 codec_info[0]; +} __packed; +struct bt_hci_rp_read_codecs_v2 { + uint8_t status; + /* other fields filled in dynamically */ + uint8_t codecs[0]; +} __packed; + +struct bt_hci_cp_codec_id { + uint8_t coding_format; + uint16_t company_id; + uint16_t vs_codec_id; +} __packed; + +#define BT_HCI_OP_READ_CODEC_CAPABILITIES BT_OP(BT_OGF_INFO, 0x000e) +struct bt_hci_cp_read_codec_capabilities { + struct bt_hci_cp_codec_id codec_id; + uint8_t transport; + uint8_t direction; +} __packed; +struct bt_hci_codec_capability_info { + uint8_t length; + uint8_t data[0]; +} __packed; +struct bt_hci_rp_read_codec_capabilities { + uint8_t status; + uint8_t num_capabilities; + /* other fields filled in dynamically */ + uint8_t capabilities[0]; +} __packed; + +#define BT_HCI_OP_READ_CTLR_DELAY BT_OP(BT_OGF_INFO, 0x000f) +struct bt_hci_cp_read_ctlr_delay { + struct bt_hci_cp_codec_id codec_id; + uint8_t transport; + uint8_t direction; + uint8_t codec_config_len; + uint8_t codec_config[0]; +} __packed; +struct bt_hci_rp_read_ctlr_delay { + uint8_t status; + uint8_t min_ctlr_delay[3]; + uint8_t max_ctlr_delay[3]; +} __packed; + +#define BT_HCI_OP_READ_RSSI BT_OP(BT_OGF_STATUS, 0x0005) +struct bt_hci_cp_read_rssi { + u16_t handle; +} __packed; +struct bt_hci_rp_read_rssi { + u8_t status; + u16_t handle; + s8_t rssi; +} __packed; + +#define BT_HCI_ENCRYPTION_KEY_SIZE_MIN 7 +#define BT_HCI_ENCRYPTION_KEY_SIZE_MAX 16 + +#define BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE BT_OP(BT_OGF_STATUS, 0x0008) +struct bt_hci_cp_read_encryption_key_size { + u16_t handle; +} __packed; +struct bt_hci_rp_read_encryption_key_size { + u8_t status; + u16_t handle; + u8_t key_size; +} __packed; + +/* BLE */ + +#define BT_HCI_OP_LE_SET_EVENT_MASK BT_OP(BT_OGF_LE, 0x0001) +struct bt_hci_cp_le_set_event_mask { + u8_t events[8]; +} __packed; + +#define BT_HCI_OP_LE_READ_BUFFER_SIZE BT_OP(BT_OGF_LE, 0x0002) +struct bt_hci_rp_le_read_buffer_size { + u8_t status; + u16_t le_max_len; + u8_t le_max_num; +} __packed; + +#define BT_HCI_OP_LE_READ_LOCAL_FEATURES BT_OP(BT_OGF_LE, 0x0003) +struct bt_hci_rp_le_read_local_features { + u8_t status; + u8_t features[8]; +} __packed; + +#define BT_HCI_OP_LE_SET_RANDOM_ADDRESS BT_OP(BT_OGF_LE, 0x0005) +struct bt_hci_cp_le_set_random_address { + bt_addr_t bdaddr; +} __packed; + +/* Advertising types */ +#define BT_LE_ADV_IND 0x00 +#define BT_LE_ADV_DIRECT_IND 0x01 +#define BT_LE_ADV_SCAN_IND 0x02 +#define BT_LE_ADV_NONCONN_IND 0x03 +#define BT_LE_ADV_DIRECT_IND_LOW_DUTY 0x04 +/* Needed in advertising reports when getting info about */ +#define BT_LE_ADV_SCAN_RSP 0x04 + +#define BT_LE_ADV_FP_NO_WHITELIST 0x00 +#define BT_LE_ADV_FP_WHITELIST_SCAN_REQ 0x01 +#define BT_LE_ADV_FP_WHITELIST_CONN_IND 0x02 +#define BT_LE_ADV_FP_WHITELIST_BOTH 0x03 + +#define BT_HCI_OP_LE_SET_ADV_PARAM BT_OP(BT_OGF_LE, 0x0006) +struct bt_hci_cp_le_set_adv_param { + u16_t min_interval; + u16_t max_interval; + u8_t type; + u8_t own_addr_type; + bt_addr_le_t direct_addr; + u8_t channel_map; + u8_t filter_policy; +} __packed; + +#define BT_HCI_OP_LE_READ_ADV_CHAN_TX_POWER BT_OP(BT_OGF_LE, 0x0007) +struct bt_hci_rp_le_read_chan_tx_power { + u8_t status; + s8_t tx_power_level; +} __packed; + +#define BT_HCI_OP_LE_SET_ADV_DATA BT_OP(BT_OGF_LE, 0x0008) +struct bt_hci_cp_le_set_adv_data { + u8_t len; + u8_t data[31]; +} __packed; + +#define BT_HCI_OP_LE_SET_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0009) +struct bt_hci_cp_le_set_scan_rsp_data { + u8_t len; + u8_t data[31]; +} __packed; + +#define BT_HCI_LE_ADV_DISABLE 0x00 +#define BT_HCI_LE_ADV_ENABLE 0x01 + +#define BT_HCI_OP_LE_SET_ADV_ENABLE BT_OP(BT_OGF_LE, 0x000a) +struct bt_hci_cp_le_set_adv_enable { + u8_t enable; +} __packed; + +/* Scan types */ +#define BT_HCI_OP_LE_SET_SCAN_PARAM BT_OP(BT_OGF_LE, 0x000b) +#define BT_HCI_LE_SCAN_PASSIVE 0x00 +#define BT_HCI_LE_SCAN_ACTIVE 0x01 + +#define BT_HCI_LE_SCAN_FP_NO_WHITELIST 0x00 +#define BT_HCI_LE_SCAN_FP_USE_WHITELIST 0x01 + +struct bt_hci_cp_le_set_scan_param { + u8_t scan_type; + u16_t interval; + u16_t window; + u8_t addr_type; + u8_t filter_policy; +} __packed; + +#define BT_HCI_OP_LE_SET_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x000c) + +#define BT_HCI_LE_SCAN_DISABLE 0x00 +#define BT_HCI_LE_SCAN_ENABLE 0x01 + +#define BT_HCI_LE_SCAN_FILTER_DUP_DISABLE 0x00 +#define BT_HCI_LE_SCAN_FILTER_DUP_ENABLE 0x01 + +struct bt_hci_cp_le_set_scan_enable { + u8_t enable; + u8_t filter_dup; +} __packed; + +#define BT_HCI_OP_LE_CREATE_CONN BT_OP(BT_OGF_LE, 0x000d) + +#define BT_HCI_LE_CREATE_CONN_FP_DIRECT 0x00 +#define BT_HCI_LE_CREATE_CONN_FP_WHITELIST 0x01 + +struct bt_hci_cp_le_create_conn { + u16_t scan_interval; + u16_t scan_window; + u8_t filter_policy; + bt_addr_le_t peer_addr; + u8_t own_addr_type; + u16_t conn_interval_min; + u16_t conn_interval_max; + u16_t conn_latency; + u16_t supervision_timeout; + u16_t min_ce_len; + u16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_CREATE_CONN_CANCEL BT_OP(BT_OGF_LE, 0x000e) + +#define BT_HCI_OP_LE_READ_WL_SIZE BT_OP(BT_OGF_LE, 0x000f) +struct bt_hci_rp_le_read_wl_size { + u8_t status; + u8_t wl_size; +} __packed; + +#define BT_HCI_OP_LE_CLEAR_WL BT_OP(BT_OGF_LE, 0x0010) + +#define BT_HCI_OP_LE_ADD_DEV_TO_WL BT_OP(BT_OGF_LE, 0x0011) +struct bt_hci_cp_le_add_dev_to_wl { + bt_addr_le_t addr; +} __packed; + +#define BT_HCI_OP_LE_REM_DEV_FROM_WL BT_OP(BT_OGF_LE, 0x0012) +struct bt_hci_cp_le_rem_dev_from_wl { + bt_addr_le_t addr; +} __packed; + +#define BT_HCI_OP_LE_CONN_UPDATE BT_OP(BT_OGF_LE, 0x0013) +struct hci_cp_le_conn_update { + u16_t handle; + u16_t conn_interval_min; + u16_t conn_interval_max; + u16_t conn_latency; + u16_t supervision_timeout; + u16_t min_ce_len; + u16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_SET_HOST_CHAN_CLASSIF BT_OP(BT_OGF_LE, 0x0014) +struct bt_hci_cp_le_set_host_chan_classif { + u8_t ch_map[5]; +} __packed; + +#define BT_HCI_OP_LE_READ_CHAN_MAP BT_OP(BT_OGF_LE, 0x0015) +struct bt_hci_cp_le_read_chan_map { + u16_t handle; +} __packed; +struct bt_hci_rp_le_read_chan_map { + u8_t status; + u16_t handle; + u8_t ch_map[5]; +} __packed; + +#define BT_HCI_OP_LE_READ_REMOTE_FEATURES BT_OP(BT_OGF_LE, 0x0016) +struct bt_hci_cp_le_read_remote_features { + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ENCRYPT BT_OP(BT_OGF_LE, 0x0017) +struct bt_hci_cp_le_encrypt { + u8_t key[16]; + u8_t plaintext[16]; +} __packed; +struct bt_hci_rp_le_encrypt { + u8_t status; + u8_t enc_data[16]; +} __packed; + +#define BT_HCI_OP_LE_RAND BT_OP(BT_OGF_LE, 0x0018) +struct bt_hci_rp_le_rand { + u8_t status; + u8_t rand[8]; +} __packed; + +#define BT_HCI_OP_LE_START_ENCRYPTION BT_OP(BT_OGF_LE, 0x0019) +struct bt_hci_cp_le_start_encryption { + u16_t handle; + u64_t rand; + u16_t ediv; + u8_t ltk[16]; +} __packed; + +#define BT_HCI_OP_LE_LTK_REQ_REPLY BT_OP(BT_OGF_LE, 0x001a) +struct bt_hci_cp_le_ltk_req_reply { + u16_t handle; + u8_t ltk[16]; +} __packed; +struct bt_hci_rp_le_ltk_req_reply { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_LTK_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x001b) +struct bt_hci_cp_le_ltk_req_neg_reply { + u16_t handle; +} __packed; +struct bt_hci_rp_le_ltk_req_neg_reply { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_READ_SUPP_STATES BT_OP(BT_OGF_LE, 0x001c) +struct bt_hci_rp_le_read_supp_states { + u8_t status; + u8_t le_states[8]; +} __packed; + +#define BT_HCI_OP_LE_RX_TEST BT_OP(BT_OGF_LE, 0x001d) +struct bt_hci_cp_le_rx_test { + u8_t rx_ch; +} __packed; + +#define BT_HCI_OP_LE_TX_TEST BT_OP(BT_OGF_LE, 0x001e) +struct bt_hci_cp_le_tx_test { + u8_t tx_ch; + u8_t test_data_len; + u8_t pkt_payload; +} __packed; + +#define BT_HCI_OP_LE_TEST_END BT_OP(BT_OGF_LE, 0x001f) +struct bt_hci_rp_le_test_end { + u8_t status; + u16_t rx_pkt_count; +} __packed; + +#define BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY BT_OP(BT_OGF_LE, 0x0020) +struct bt_hci_cp_le_conn_param_req_reply { + u16_t handle; + u16_t interval_min; + u16_t interval_max; + u16_t latency; + u16_t timeout; + u16_t min_ce_len; + u16_t max_ce_len; +} __packed; +struct bt_hci_rp_le_conn_param_req_reply { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x0021) +struct bt_hci_cp_le_conn_param_req_neg_reply { + u16_t handle; + u8_t reason; +} __packed; +struct bt_hci_rp_le_conn_param_req_neg_reply { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_SET_DATA_LEN BT_OP(BT_OGF_LE, 0x0022) +struct bt_hci_cp_le_set_data_len { + u16_t handle; + u16_t tx_octets; + u16_t tx_time; +} __packed; +struct bt_hci_rp_le_set_data_len { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_READ_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0023) +struct bt_hci_rp_le_read_default_data_len { + u8_t status; + u16_t max_tx_octets; + u16_t max_tx_time; +} __packed; + +#define BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN BT_OP(BT_OGF_LE, 0x0024) +struct bt_hci_cp_le_write_default_data_len { + u16_t max_tx_octets; + u16_t max_tx_time; +} __packed; + +#define BT_HCI_OP_LE_P256_PUBLIC_KEY BT_OP(BT_OGF_LE, 0x0025) + +#define BT_HCI_OP_LE_GENERATE_DHKEY BT_OP(BT_OGF_LE, 0x0026) +struct bt_hci_cp_le_generate_dhkey { + u8_t key[64]; +} __packed; + +#define BT_HCI_OP_LE_ADD_DEV_TO_RL BT_OP(BT_OGF_LE, 0x0027) +struct bt_hci_cp_le_add_dev_to_rl { + bt_addr_le_t peer_id_addr; + u8_t peer_irk[16]; + u8_t local_irk[16]; +} __packed; + +#define BT_HCI_OP_LE_REM_DEV_FROM_RL BT_OP(BT_OGF_LE, 0x0028) +struct bt_hci_cp_le_rem_dev_from_rl { + bt_addr_le_t peer_id_addr; +} __packed; + +#define BT_HCI_OP_LE_CLEAR_RL BT_OP(BT_OGF_LE, 0x0029) + +#define BT_HCI_OP_LE_READ_RL_SIZE BT_OP(BT_OGF_LE, 0x002a) +struct bt_hci_rp_le_read_rl_size { + u8_t status; + u8_t rl_size; +} __packed; + +#define BT_HCI_OP_LE_READ_PEER_RPA BT_OP(BT_OGF_LE, 0x002b) +struct bt_hci_cp_le_read_peer_rpa { + bt_addr_le_t peer_id_addr; +} __packed; +struct bt_hci_rp_le_read_peer_rpa { + u8_t status; + bt_addr_t peer_rpa; +} __packed; + +#define BT_HCI_OP_LE_READ_LOCAL_RPA BT_OP(BT_OGF_LE, 0x002c) +struct bt_hci_cp_le_read_local_rpa { + bt_addr_le_t peer_id_addr; +} __packed; +struct bt_hci_rp_le_read_local_rpa { + u8_t status; + bt_addr_t local_rpa; +} __packed; + +#define BT_HCI_ADDR_RES_DISABLE 0x00 +#define BT_HCI_ADDR_RES_ENABLE 0x01 + +#define BT_HCI_OP_LE_SET_ADDR_RES_ENABLE BT_OP(BT_OGF_LE, 0x002d) +struct bt_hci_cp_le_set_addr_res_enable { + u8_t enable; +} __packed; + +#define BT_HCI_OP_LE_SET_RPA_TIMEOUT BT_OP(BT_OGF_LE, 0x002e) +struct bt_hci_cp_le_set_rpa_timeout { + u16_t rpa_timeout; +} __packed; + +#define BT_HCI_OP_LE_READ_MAX_DATA_LEN BT_OP(BT_OGF_LE, 0x002f) +struct bt_hci_rp_le_read_max_data_len { + u8_t status; + u16_t max_tx_octets; + u16_t max_tx_time; + u16_t max_rx_octets; + u16_t max_rx_time; +} __packed; + +#define BT_HCI_LE_PHY_1M 0x01 +#define BT_HCI_LE_PHY_2M 0x02 +#define BT_HCI_LE_PHY_CODED 0x03 + +#define BT_HCI_OP_LE_READ_PHY BT_OP(BT_OGF_LE, 0x0030) +struct bt_hci_cp_le_read_phy { + u16_t handle; +} __packed; +struct bt_hci_rp_le_read_phy { + u8_t status; + u16_t handle; + u8_t tx_phy; + u8_t rx_phy; +} __packed; + +#define BT_HCI_LE_PHY_TX_ANY BIT(0) +#define BT_HCI_LE_PHY_RX_ANY BIT(1) + +#define BT_HCI_LE_PHY_PREFER_1M BIT(0) +#define BT_HCI_LE_PHY_PREFER_2M BIT(1) +#define BT_HCI_LE_PHY_PREFER_CODED BIT(2) + +#define BT_HCI_OP_LE_SET_DEFAULT_PHY BT_OP(BT_OGF_LE, 0x0031) +struct bt_hci_cp_le_set_default_phy { + u8_t all_phys; + u8_t tx_phys; + u8_t rx_phys; +} __packed; + +#define BT_HCI_LE_PHY_CODED_ANY 0x00 +#define BT_HCI_LE_PHY_CODED_S2 0x01 +#define BT_HCI_LE_PHY_CODED_S8 0x02 + +#define BT_HCI_OP_LE_SET_PHY BT_OP(BT_OGF_LE, 0x0032) +struct bt_hci_cp_le_set_phy { + u16_t handle; + u8_t all_phys; + u8_t tx_phys; + u8_t rx_phys; + u16_t phy_opts; +} __packed; + +#define BT_HCI_LE_MOD_INDEX_STANDARD 0x00 +#define BT_HCI_LE_MOD_INDEX_STABLE 0x01 + +#define BT_HCI_OP_LE_ENH_RX_TEST BT_OP(BT_OGF_LE, 0x0033) +struct bt_hci_cp_le_enh_rx_test { + u8_t rx_ch; + u8_t phy; + u8_t mod_index; +} __packed; + +/* Extends BT_HCI_LE_PHY */ +#define BT_HCI_LE_TX_PHY_CODED_S8 0x03 +#define BT_HCI_LE_TX_PHY_CODED_S2 0x04 + +#define BT_HCI_OP_LE_ENH_TX_TEST BT_OP(BT_OGF_LE, 0x0034) +struct bt_hci_cp_le_enh_tx_test { + u8_t tx_ch; + u8_t test_data_len; + u8_t pkt_payload; + u8_t phy; +} __packed; + +#define BT_HCI_OP_LE_SET_ADV_SET_RANDOM_ADDR BT_OP(BT_OGF_LE, 0x0035) +struct bt_hci_cp_le_set_adv_set_random_addr { + u8_t handle; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_LE_ADV_PROP_CONN BIT(0) +#define BT_HCI_LE_ADV_PROP_SCAN BIT(1) +#define BT_HCI_LE_ADV_PROP_DIRECT BIT(2) +#define BT_HCI_LE_ADV_PROP_HI_DC_CONN BIT(3) +#define BT_HCI_LE_ADV_PROP_LEGACY BIT(4) +#define BT_HCI_LE_ADV_PROP_ANON BIT(5) +#define BT_HCI_LE_ADV_PROP_TX_POWER BIT(6) + +#define BT_HCI_LE_ADV_SCAN_REQ_ENABLE 1 +#define BT_HCI_LE_ADV_SCAN_REQ_DISABLE 0 + +#define BT_HCI_LE_ADV_TX_POWER_NO_PREF 0x7F + +#define BT_HCI_LE_ADV_HANDLE_MAX 0xEF + +#define BT_HCI_OP_LE_SET_EXT_ADV_PARAM BT_OP(BT_OGF_LE, 0x0036) +struct bt_hci_cp_le_set_ext_adv_param { + u8_t handle; + u16_t props; + u8_t prim_min_interval[3]; + u8_t prim_max_interval[3]; + u8_t prim_channel_map; + u8_t own_addr_type; + bt_addr_le_t peer_addr; + u8_t filter_policy; + s8_t tx_power; + u8_t prim_adv_phy; + u8_t sec_adv_max_skip; + u8_t sec_adv_phy; + u8_t sid; + u8_t scan_req_notify_enable; +} __packed; +struct bt_hci_rp_le_set_ext_adv_param { + u8_t status; + s8_t tx_power; +} __packed; + +#define BT_HCI_LE_EXT_ADV_OP_INTERM_FRAG 0x00 +#define BT_HCI_LE_EXT_ADV_OP_FIRST_FRAG 0x01 +#define BT_HCI_LE_EXT_ADV_OP_LAST_FRAG 0x02 +#define BT_HCI_LE_EXT_ADV_OP_COMPLETE_DATA 0x03 +#define BT_HCI_LE_EXT_ADV_OP_UNCHANGED_DATA 0x04 + +#define BT_HCI_LE_EXT_ADV_FRAG_ENABLED 0x00 +#define BT_HCI_LE_EXT_ADV_FRAG_DISABLED 0x01 + +#define BT_HCI_LE_EXT_ADV_FRAG_MAX_LEN 251 + +#define BT_HCI_OP_LE_SET_EXT_ADV_DATA BT_OP(BT_OGF_LE, 0x0037) +struct bt_hci_cp_le_set_ext_adv_data { + u8_t handle; + u8_t op; + u8_t frag_pref; + u8_t len; + u8_t data[251]; +} __packed; + +#define BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0038) +struct bt_hci_cp_le_set_ext_scan_rsp_data { + u8_t handle; + u8_t op; + u8_t frag_pref; + u8_t len; + u8_t data[251]; +} __packed; + +#define BT_HCI_OP_LE_SET_EXT_ADV_ENABLE BT_OP(BT_OGF_LE, 0x0039) +struct bt_hci_ext_adv_set { + u8_t handle; + u16_t duration; + u8_t max_ext_adv_evts; +} __packed; + +struct bt_hci_cp_le_set_ext_adv_enable { + u8_t enable; + u8_t set_num; + struct bt_hci_ext_adv_set s[0]; +} __packed; + +#define BT_HCI_OP_LE_READ_MAX_ADV_DATA_LEN BT_OP(BT_OGF_LE, 0x003a) +struct bt_hci_rp_le_read_max_adv_data_len { + u8_t status; + u16_t max_adv_data_len; +} __packed; + +#define BT_HCI_OP_LE_READ_NUM_ADV_SETS BT_OP(BT_OGF_LE, 0x003b) +struct bt_hci_rp_le_read_num_adv_sets { + u8_t status; + u8_t num_sets; +} __packed; + +#define BT_HCI_OP_LE_REMOVE_ADV_SET BT_OP(BT_OGF_LE, 0x003c) +struct bt_hci_cp_le_remove_adv_set { + u8_t handle; +} __packed; + +#define BT_HCI_OP_CLEAR_ADV_SETS BT_OP(BT_OGF_LE, 0x003d) + +#define BT_HCI_OP_LE_SET_PER_ADV_PARAM BT_OP(BT_OGF_LE, 0x003e) +struct bt_hci_cp_le_set_per_adv_param { + u8_t handle; + u16_t min_interval; + u16_t max_interval; + u16_t props; +} __packed; + +#define BT_HCI_LE_PER_ADV_OP_INTERM_FRAG 0x00 +#define BT_HCI_LE_PER_ADV_OP_FIRST_FRAG 0x01 +#define BT_HCI_LE_PER_ADV_OP_LAST_FRAG 0x02 +#define BT_HCI_LE_PER_ADV_OP_COMPLETE_DATA 0x03 + +#define BT_HCI_LE_PER_ADV_FRAG_MAX_LEN 252 + +#define BT_HCI_OP_LE_SET_PER_ADV_DATA BT_OP(BT_OGF_LE, 0x003f) +struct bt_hci_cp_le_set_per_adv_data { + u8_t handle; + u8_t op; + u8_t len; + u8_t data[251]; +} __packed; + +#define BT_HCI_OP_LE_SET_PER_ADV_ENABLE BT_OP(BT_OGF_LE, 0x0040) +struct bt_hci_cp_le_set_per_adv_enable { + u8_t enable; + u8_t handle; +} __packed; + +#define BT_HCI_OP_LE_SET_EXT_SCAN_PARAM BT_OP(BT_OGF_LE, 0x0041) +struct bt_hci_ext_scan_phy { + u8_t type; + u16_t interval; + u16_t window; +} __packed; + +#define BT_HCI_LE_EXT_SCAN_PHY_1M BIT(0) +#define BT_HCI_LE_EXT_SCAN_PHY_2M BIT(1) +#define BT_HCI_LE_EXT_SCAN_PHY_CODED BIT(2) + +struct bt_hci_cp_le_set_ext_scan_param { + u8_t own_addr_type; + u8_t filter_policy; + u8_t phys; + struct bt_hci_ext_scan_phy p[0]; +} __packed; + +/* Extends BT_HCI_LE_SCAN_FILTER_DUP */ +#define BT_HCI_LE_EXT_SCAN_FILTER_DUP_ENABLE_RESET 0x02 + +#define BT_HCI_OP_LE_SET_EXT_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x0042) +struct bt_hci_cp_le_set_ext_scan_enable { + u8_t enable; + u8_t filter_dup; + u16_t duration; + u16_t period; +} __packed; + +#define BT_HCI_OP_LE_EXT_CREATE_CONN BT_OP(BT_OGF_LE, 0x0043) +struct bt_hci_ext_conn_phy { + u16_t interval; + u16_t window; + u16_t conn_interval_min; + u16_t conn_interval_max; + u16_t conn_latency; + u16_t supervision_timeout; + u16_t min_ce_len; + u16_t max_ce_len; +} __packed; + +struct bt_hci_cp_le_ext_create_conn { + u8_t filter_policy; + u8_t own_addr_type; + bt_addr_le_t peer_addr; + u8_t phys; + struct bt_hci_ext_conn_phy p[0]; +} __packed; + +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST BIT(0) +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED BIT(1) + +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOA BIT(0) +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_1US BIT(1) +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_2US BIT(2) +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_CTE BIT(3) +#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE BIT(4) + +#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC BT_OP(BT_OGF_LE, 0x0044) +struct bt_hci_cp_le_per_adv_create_sync { + u8_t filter_policy; + u8_t sid; + bt_addr_le_t addr; + u16_t skip; + u16_t sync_timeout; + u8_t unused; +} __packed; + +#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL BT_OP(BT_OGF_LE, 0x0045) + +#define BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC BT_OP(BT_OGF_LE, 0x0046) +struct bt_hci_cp_le_per_adv_terminate_sync { + u16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ADD_DEV_TO_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0047) +struct bt_hci_cp_le_add_dev_to_per_adv_list { + bt_addr_le_t addr; + u8_t sid; +} __packed; + +#define BT_HCI_OP_LE_REM_DEV_FROM_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0048) +struct bt_hci_cp_le_rem_dev_from_per_adv_list { + bt_addr_le_t addr; + u8_t sid; +} __packed; + +#define BT_HCI_OP_LE_CLEAR_PER_ADV_LIST BT_OP(BT_OGF_LE, 0x0049) + +#define BT_HCI_OP_LE_READ_PER_ADV_LIST_SIZE BT_OP(BT_OGF_LE, 0x004a) +struct bt_hci_rp_le_read_per_adv_list_size { + u8_t status; + u8_t list_size; +} __packed; + +#define BT_HCI_OP_LE_READ_TX_POWER BT_OP(BT_OGF_LE, 0x004b) +struct bt_hci_rp_le_read_tx_power { + u8_t status; + s8_t min_tx_power; + s8_t max_tx_power; +} __packed; + +#define BT_HCI_OP_LE_READ_RF_PATH_COMP BT_OP(BT_OGF_LE, 0x004c) +struct bt_hci_rp_le_read_rf_path_comp { + u8_t status; + s16_t tx_path_comp; + s16_t rx_path_comp; +} __packed; + +#define BT_HCI_OP_LE_WRITE_RF_PATH_COMP BT_OP(BT_OGF_LE, 0x004d) +struct bt_hci_cp_le_write_rf_path_comp { + s16_t tx_path_comp; + s16_t rx_path_comp; +} __packed; + +#define BT_HCI_LE_PRIVACY_MODE_NETWORK 0x00 +#define BT_HCI_LE_PRIVACY_MODE_DEVICE 0x01 + +#define BT_HCI_OP_LE_SET_PRIVACY_MODE BT_OP(BT_OGF_LE, 0x004e) +struct bt_hci_cp_le_set_privacy_mode { + bt_addr_le_t id_addr; + u8_t mode; +} __packed; + +#define BT_HCI_OP_LE_SET_CL_CTE_TX_ENABLE BT_OP(BT_OGF_LE, 0x0052) +struct bt_hci_cp_le_set_cl_cte_tx_enable { + uint8_t handle; + uint8_t cte_enable; +} __packed; + +/* Min and max Constant Tone Extension length in 8us units */ +#define BT_HCI_LE_CTE_LEN_MIN 0x2 +#define BT_HCI_LE_CTE_LEN_MAX 0x14 + +#define BT_HCI_LE_AOA_CTE 0x1 +#define BT_HCI_LE_AOD_CTE_1US 0x2 +#define BT_HCI_LE_AOD_CTE_2US 0x3 + +#define BT_HCI_LE_CTE_COUNT_MIN 0x1 +#define BT_HCI_LE_CTE_COUNT_MAX 0x10 + +#define BT_HCI_OP_LE_SET_CL_CTE_TX_PARAMS BT_OP(BT_OGF_LE, 0x0051) +struct bt_hci_cp_le_set_cl_cte_tx_params { + uint8_t handle; + uint8_t cte_len; + uint8_t cte_type; + uint8_t cte_count; + uint8_t switch_pattern_len; + uint8_t ant_ids[0]; +} __packed; + +#define BT_HCI_LE_AOA_CTE_RSP BIT(0) +#define BT_HCI_LE_AOD_CTE_RSP_1US BIT(1) +#define BT_HCI_LE_AOD_CTE_RSP_2US BIT(2) + +#define BT_HCI_LE_SWITCH_PATTERN_LEN_MIN 0x2 +#define BT_HCI_LE_SWITCH_PATTERN_LEN_MAX 0x4B + +#define BT_HCI_OP_LE_SET_CONN_CTE_TX_PARAMS BT_OP(BT_OGF_LE, 0x0055) +struct bt_hci_cp_le_set_conn_cte_tx_params { + uint16_t handle; + uint8_t cte_types; + uint8_t switch_pattern_len; + uint8_t ant_id[0]; +} __packed; + +struct bt_hci_rp_le_set_conn_cte_tx_params { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_LE_1US_AOD_TX BIT(0) +#define BT_HCI_LE_1US_AOD_RX BIT(1) +#define BT_HCI_LE_1US_AOA_RX BIT(2) + +#define BT_HCI_LE_NUM_ANT_MIN 0x1 +#define BT_HCI_LE_NUM_ANT_MAX 0x4B + +#define BT_HCI_LE_MAX_SWITCH_PATTERN_LEN_MIN 0x2 +#define BT_HCI_LE_MAX_SWITCH_PATTERN_LEN_MAX 0x4B + +#define BT_HCI_LE_MAX_CTE_LEN_MIN 0x2 +#define BT_HCI_LE_MAX_CTE_LEN_MAX 0x14 + +#define BT_HCI_OP_LE_READ_ANT_INFO BT_OP(BT_OGF_LE, 0x0058) +struct bt_hci_rp_le_read_ant_info { + uint8_t status; + uint8_t switch_sample_rates; + uint8_t num_ant; + uint8_t max_switch_pattern_len; + uint8_t max_cte_len; +}; + +#define BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE BT_OP(BT_OGF_LE, 0x0059) +struct bt_hci_cp_le_set_per_adv_recv_enable { + uint16_t handle; + uint8_t enable; +} __packed; + +#define BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER BT_OP(BT_OGF_LE, 0x005a) +struct bt_hci_cp_le_per_adv_sync_transfer { + uint16_t conn_handle; + uint16_t service_data; + uint16_t sync_handle; +} __packed; + +struct bt_hci_rp_le_per_adv_sync_transfer { + uint8_t status; + uint16_t conn_handle; +} __packed; + +#define BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER BT_OP(BT_OGF_LE, 0x005b) +struct bt_hci_cp_le_per_adv_set_info_transfer { + uint16_t conn_handle; + uint16_t service_data; + uint8_t adv_handle; +} __packed; + +struct bt_hci_rp_le_per_adv_set_info_transfer { + uint8_t status; + uint16_t conn_handle; +} __packed; + +#define BT_HCI_LE_PAST_MODE_NO_SYNC 0x00 +#define BT_HCI_LE_PAST_MODE_NO_REPORTS 0x01 +#define BT_HCI_LE_PAST_MODE_SYNC 0x02 + +#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOA BIT(0) +#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_1US BIT(1) +#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_2US BIT(2) +#define BT_HCI_LE_PAST_CTE_TYPE_NO_CTE BIT(3) +#define BT_HCI_LE_PAST_CTE_TYPE_ONLY_CTE BIT(4) + +#define BT_HCI_OP_LE_PAST_PARAM BT_OP(BT_OGF_LE, 0x005c) +struct bt_hci_cp_le_past_param { + uint16_t conn_handle; + uint8_t mode; + uint16_t skip; + uint16_t timeout; + uint8_t cte_type; +} __packed; + +struct bt_hci_rp_le_past_param { + uint8_t status; + uint16_t conn_handle; +} __packed; + +#define BT_HCI_OP_LE_DEFAULT_PAST_PARAM BT_OP(BT_OGF_LE, 0x005d) +struct bt_hci_cp_le_default_past_param { + uint8_t mode; + uint16_t skip; + uint16_t timeout; + uint8_t cte_type; +} __packed; + +struct bt_hci_rp_le_default_past_param { + uint8_t status; +} __packed; + +#define BT_HCI_OP_LE_READ_BUFFER_SIZE_V2 BT_OP(BT_OGF_LE, 0x0060) +struct bt_hci_rp_le_read_buffer_size_v2 { + uint8_t status; + uint16_t acl_mtu; + uint8_t acl_max_pkt; + uint16_t iso_mtu; + uint8_t iso_max_pkt; +} __packed; + +#define BT_HCI_OP_LE_READ_ISO_TX_SYNC BT_OP(BT_OGF_LE, 0x0061) +struct bt_hci_cp_le_read_iso_tx_sync { + uint16_t handle; +} __packed; + +struct bt_hci_rp_le_read_iso_tx_sync { + uint8_t status; + uint16_t handle; + uint16_t seq; + uint32_t timestamp; + uint8_t offset[3]; +} __packed; + +#define BT_HCI_OP_LE_SET_CIG_PARAMS BT_OP(BT_OGF_LE, 0x0062) +struct bt_hci_cis_params { + uint8_t cis_id; + uint16_t m_sdu; + uint16_t s_sdu; + uint8_t m_phy; + uint8_t s_phy; + uint8_t m_rtn; + uint8_t s_rtn; +} __packed; + +struct bt_hci_cp_le_set_cig_params { + uint8_t cig_id; + uint8_t m_interval[3]; + uint8_t s_interval[3]; + uint8_t sca; + uint8_t packing; + uint8_t framing; + uint16_t m_latency; + uint16_t s_latency; + uint8_t num_cis; + struct bt_hci_cis_params cis[0]; +} __packed; + +struct bt_hci_rp_le_set_cig_params { + uint8_t status; + uint8_t cig_id; + uint8_t num_handles; + uint16_t handle[0]; +} __packed; + +#define BT_HCI_OP_LE_SET_CIG_PARAMS_TEST BT_OP(BT_OGF_LE, 0x0063) +struct bt_hci_cis_params_test { + uint8_t cis_id; + uint8_t nse; + uint16_t m_sdu; + uint16_t s_sdu; + uint16_t m_pdu; + uint16_t s_pdu; + uint8_t m_phy; + uint8_t s_phy; + uint8_t m_bn; + uint8_t s_bn; +} __packed; + +struct bt_hci_cp_le_set_cig_params_test { + uint8_t cig_id; + uint8_t m_interval[3]; + uint8_t s_interval[3]; + uint8_t m_ft; + uint8_t s_ft; + uint16_t iso_interval; + uint8_t sca; + uint8_t packing; + uint8_t framing; + uint8_t num_cis; + struct bt_hci_cis_params_test cis[0]; +} __packed; + +struct bt_hci_rp_le_set_cig_params_test { + uint8_t status; + uint8_t cig_id; + uint8_t num_handles; + uint16_t handle[0]; +} __packed; + +#define BT_HCI_OP_LE_CREATE_CIS BT_OP(BT_OGF_LE, 0x0064) +struct bt_hci_cis { + uint16_t cis_handle; + uint16_t acl_handle; +} __packed; + +struct bt_hci_cp_le_create_cis { + uint8_t num_cis; + struct bt_hci_cis cis[0]; +} __packed; + +#define BT_HCI_OP_LE_REMOVE_CIG BT_OP(BT_OGF_LE, 0x0065) +struct bt_hci_cp_le_remove_cig { + uint8_t cig_id; +} __packed; + +struct bt_hci_rp_le_remove_cig { + uint8_t status; + uint8_t cig_id; +} __packed; + +#define BT_HCI_OP_LE_ACCEPT_CIS BT_OP(BT_OGF_LE, 0x0066) +struct bt_hci_cp_le_accept_cis { + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_REJECT_CIS BT_OP(BT_OGF_LE, 0x0067) +struct bt_hci_cp_le_reject_cis { + uint16_t handle; + uint8_t reason; +} __packed; + +struct bt_hci_rp_le_reject_cis { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_CREATE_BIG BT_OP(BT_OGF_LE, 0x0068) +struct bt_hci_cp_le_create_big { + uint8_t big_handle; + uint8_t adv_handle; + uint8_t num_bis; + uint8_t sdu_interval[3]; + uint16_t max_sdu; + uint16_t max_latency; + uint8_t rtn; + uint8_t phy; + uint8_t packing; + uint8_t framing; + uint8_t encryption; + uint8_t bcode[16]; +} __packed; + +#define BT_HCI_OP_LE_CREATE_BIG_TEST BT_OP(BT_OGF_LE, 0x0069) +struct bt_hci_cp_le_create_big_test { + uint8_t big_handle; + uint8_t adv_handle; + uint8_t num_bis; + uint8_t sdu_interval[3]; + uint16_t iso_interval; + uint8_t nse; + uint16_t max_sdu; + uint16_t max_pdu; + uint8_t phy; + uint8_t packing; + uint8_t framing; + uint8_t bn; + uint8_t irc; + uint8_t pto; + uint8_t encryption; + uint8_t bcode[16]; +} __packed; + +#define BT_HCI_OP_LE_TERMINATE_BIG BT_OP(BT_OGF_LE, 0x006a) +struct bt_hci_cp_le_terminate_big { + uint8_t big_handle; + uint8_t reason; +} __packed; + +#define BT_HCI_OP_LE_BIG_CREATE_SYNC BT_OP(BT_OGF_LE, 0x006b) +struct bt_hci_cp_le_big_create_sync { + uint8_t big_handle; + uint16_t sync_handle; + uint8_t encryption; + uint8_t bcode[16]; + uint8_t mse; + uint16_t sync_timeout; + uint8_t num_bis; + uint8_t bis[0]; +} __packed; + +#define BT_HCI_OP_LE_BIG_TERMINATE_SYNC BT_OP(BT_OGF_LE, 0x006c) +struct bt_hci_cp_le_big_terminate_sync { + uint8_t big_handle; +} __packed; + +struct bt_hci_rp_le_big_terminate_sync { + uint8_t status; + uint8_t big_handle; +} __packed; + +#define BT_HCI_OP_LE_REQ_PEER_SC BT_OP(BT_OGF_LE, 0x006d) +struct bt_hci_cp_le_req_peer_sca { + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_SETUP_ISO_PATH BT_OP(BT_OGF_LE, 0x006e) +struct bt_hci_cp_le_setup_iso_path { + uint16_t handle; + uint8_t path_dir; + uint8_t path_id; + struct bt_hci_cp_codec_id codec_id; + uint8_t controller_delay[3]; + uint8_t codec_config_len; + uint8_t codec_config[0]; +} __packed; + +struct bt_hci_rp_le_setup_iso_path { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_REMOVE_ISO_PATH BT_OP(BT_OGF_LE, 0x006f) +struct bt_hci_cp_le_remove_iso_path { + uint16_t handle; + uint8_t path_dir; +} __packed; + +struct bt_hci_rp_le_remove_iso_path { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ISO_TRANSMIT_TEST BT_OP(BT_OGF_LE, 0x0070) +struct bt_hci_cp_le_iso_transmit_test { + uint16_t handle; + uint8_t payload_type; +} __packed; + +struct bt_hci_rp_le_iso_transmit_test { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ISO_RECEIVE_TEST BT_OP(BT_OGF_LE, 0x0071) +struct bt_hci_cp_le_iso_receive_test { + uint16_t handle; + uint8_t payload_type; +} __packed; + +struct bt_hci_rp_le_iso_receive_test { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ISO_READ_TEST_COUNTERS BT_OP(BT_OGF_LE, 0x0072) +struct bt_hci_cp_le_read_test_counters { + uint16_t handle; +} __packed; + +struct bt_hci_rp_le_read_test_counters { + uint8_t status; + uint16_t handle; + uint32_t received_cnt; + uint32_t missed_cnt; + uint32_t failed_cnt; +} __packed; + +#define BT_HCI_OP_LE_ISO_TEST_END BT_OP(BT_OGF_LE, 0x0073) +struct bt_hci_cp_le_iso_test_end { + uint16_t handle; +} __packed; + +struct bt_hci_rp_le_iso_test_end { + uint8_t status; + uint16_t handle; + uint32_t received_cnt; + uint32_t missed_cnt; + uint32_t failed_cnt; +} __packed; + +#define BT_HCI_OP_LE_SET_HOST_FEATURE BT_OP(BT_OGF_LE, 0x0074) +struct bt_hci_cp_le_set_host_feature { + uint8_t bit_number; + uint8_t bit_value; +} __packed; + +struct bt_hci_rp_le_set_host_feature { + uint8_t status; +} __packed; + +#define BT_HCI_OP_LE_READ_ISO_LINK_QUALITY BT_OP(BT_OGF_LE, 0x0075) +struct bt_hci_cp_le_read_iso_link_quality { + uint16_t handle; +} __packed; + +struct bt_hci_rp_le_read_iso_link_quality { + uint8_t status; + uint16_t handle; + uint32_t tx_unacked_packets; + uint32_t tx_flushed_packets; + uint32_t tx_last_subevent_packets; + uint32_t retransmitted_packets; + uint32_t crc_error_packets; + uint32_t rx_unreceived_packets; + uint32_t duplicate_packets; +} __packed; + +/* Event definitions */ +#if defined(BFLB_BLE) +#define BT_HCI_EVT_CC_PARAM_OFFSET 0x05 +#define BT_HCI_CCEVT_HDR_PARLEN 0x03 +#define BT_HCI_CSEVT_LEN 0x06 +#define BT_HCI_CSVT_PARLEN 0x04 +#define BT_HCI_EVT_LE_PARAM_OFFSET 0x02 +#endif + +#define BT_HCI_EVT_UNKNOWN 0x00 +#define BT_HCI_EVT_VENDOR 0xff + +#define BT_HCI_EVT_INQUIRY_COMPLETE 0x01 +struct bt_hci_evt_inquiry_complete { + u8_t status; +} __packed; + +#define BT_HCI_EVT_CONN_COMPLETE 0x03 +struct bt_hci_evt_conn_complete { + u8_t status; + u16_t handle; + bt_addr_t bdaddr; + u8_t link_type; + u8_t encr_enabled; +} __packed; + +#define BT_HCI_EVT_CONN_REQUEST 0x04 +struct bt_hci_evt_conn_request { + bt_addr_t bdaddr; + u8_t dev_class[3]; + u8_t link_type; +} __packed; + +#define BT_HCI_EVT_DISCONN_COMPLETE 0x05 +struct bt_hci_evt_disconn_complete { + u8_t status; + u16_t handle; + u8_t reason; +} __packed; + +#define BT_HCI_EVT_AUTH_COMPLETE 0x06 +struct bt_hci_evt_auth_complete { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE 0x07 +struct bt_hci_evt_remote_name_req_complete { + u8_t status; + bt_addr_t bdaddr; + u8_t name[248]; +} __packed; + +#define BT_HCI_EVT_ENCRYPT_CHANGE 0x08 +struct bt_hci_evt_encrypt_change { + u8_t status; + u16_t handle; + u8_t encrypt; +} __packed; + +#define BT_HCI_EVT_REMOTE_FEATURES 0x0b +struct bt_hci_evt_remote_features { + u8_t status; + u16_t handle; + u8_t features[8]; +} __packed; + +#define BT_HCI_EVT_REMOTE_VERSION_INFO 0x0c +struct bt_hci_evt_remote_version_info { + u8_t status; + u16_t handle; + u8_t version; + u16_t manufacturer; + u16_t subversion; +} __packed; + +#define BT_HCI_EVT_CMD_COMPLETE 0x0e +struct bt_hci_evt_cmd_complete { + u8_t ncmd; + u16_t opcode; +} __packed; + +struct bt_hci_evt_cc_status { + u8_t status; +} __packed; + +#define BT_HCI_EVT_CMD_STATUS 0x0f +struct bt_hci_evt_cmd_status { + u8_t status; + u8_t ncmd; + u16_t opcode; +} __packed; + +#define BT_HCI_EVT_HARDWARE_ERROR 0x10 +struct bt_hci_evt_hardware_error { + uint8_t hardware_code; +} __packed; + +#define BT_HCI_EVT_ROLE_CHANGE 0x12 +struct bt_hci_evt_role_change { + u8_t status; + bt_addr_t bdaddr; + u8_t role; +} __packed; + +#define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13 +struct bt_hci_evt_num_completed_packets { + u8_t num_handles; + struct bt_hci_handle_count h[0]; +} __packed; + +#define BT_HCI_EVT_PIN_CODE_REQ 0x16 +struct bt_hci_evt_pin_code_req { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_LINK_KEY_REQ 0x17 +struct bt_hci_evt_link_key_req { + bt_addr_t bdaddr; +} __packed; + +/* Link Key types */ +#define BT_LK_COMBINATION 0x00 +#define BT_LK_LOCAL_UNIT 0x01 +#define BT_LK_REMOTE_UNIT 0x02 +#define BT_LK_DEBUG_COMBINATION 0x03 +#define BT_LK_UNAUTH_COMBINATION_P192 0x04 +#define BT_LK_AUTH_COMBINATION_P192 0x05 +#define BT_LK_CHANGED_COMBINATION 0x06 +#define BT_LK_UNAUTH_COMBINATION_P256 0x07 +#define BT_LK_AUTH_COMBINATION_P256 0x08 + +#define BT_HCI_EVT_LINK_KEY_NOTIFY 0x18 +struct bt_hci_evt_link_key_notify { + bt_addr_t bdaddr; + u8_t link_key[16]; + u8_t key_type; +} __packed; + +/* Overflow link types */ +#define BT_OVERFLOW_LINK_SYNCH 0x00 +#define BT_OVERFLOW_LINK_ACL 0x01 + +#define BT_HCI_EVT_DATA_BUF_OVERFLOW 0x1a +struct bt_hci_evt_data_buf_overflow { + u8_t link_type; +} __packed; + +#define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI 0x22 +struct bt_hci_evt_inquiry_result_with_rssi { + bt_addr_t addr; + u8_t pscan_rep_mode; + u8_t reserved; + u8_t cod[3]; + u16_t clock_offset; + s8_t rssi; +} __packed; + +#define BT_HCI_EVT_REMOTE_EXT_FEATURES 0x23 +struct bt_hci_evt_remote_ext_features { + u8_t status; + u16_t handle; + u8_t page; + u8_t max_page; + u8_t features[8]; +} __packed; + +#define BT_HCI_EVT_SYNC_CONN_COMPLETE 0x2c +struct bt_hci_evt_sync_conn_complete { + u8_t status; + u16_t handle; + bt_addr_t bdaddr; + u8_t link_type; + u8_t tx_interval; + u8_t retansmission_window; + u16_t rx_pkt_length; + u16_t tx_pkt_length; + u8_t air_mode; +} __packed; + +#define BT_HCI_EVT_EXTENDED_INQUIRY_RESULT 0x2f +struct bt_hci_evt_extended_inquiry_result { + u8_t num_reports; + bt_addr_t addr; + u8_t pscan_rep_mode; + u8_t reserved; + u8_t cod[3]; + u16_t clock_offset; + s8_t rssi; + u8_t eir[240]; +} __packed; + +#define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE 0x30 +struct bt_hci_evt_encrypt_key_refresh_complete { + u8_t status; + u16_t handle; +} __packed; + +#define BT_HCI_EVT_IO_CAPA_REQ 0x31 +struct bt_hci_evt_io_capa_req { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_IO_CAPA_RESP 0x32 +struct bt_hci_evt_io_capa_resp { + bt_addr_t bdaddr; + u8_t capability; + u8_t oob_data; + u8_t authentication; +} __packed; + +#define BT_HCI_EVT_USER_CONFIRM_REQ 0x33 +struct bt_hci_evt_user_confirm_req { + bt_addr_t bdaddr; + u32_t passkey; +} __packed; + +#define BT_HCI_EVT_USER_PASSKEY_REQ 0x34 +struct bt_hci_evt_user_passkey_req { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_SSP_COMPLETE 0x36 +struct bt_hci_evt_ssp_complete { + u8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_USER_PASSKEY_NOTIFY 0x3b +struct bt_hci_evt_user_passkey_notify { + bt_addr_t bdaddr; + u32_t passkey; +} __packed; + +#define BT_HCI_EVT_LE_META_EVENT 0x3e +struct bt_hci_evt_le_meta_event { + u8_t subevent; +} __packed; + +#define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXP 0x57 +struct bt_hci_evt_auth_payload_timeout_exp { + u16_t handle; +} __packed; + +#define BT_HCI_ROLE_MASTER 0x00 +#define BT_HCI_ROLE_SLAVE 0x01 + +#define BT_HCI_EVT_LE_CONN_COMPLETE 0x01 +struct bt_hci_evt_le_conn_complete { + u8_t status; + u16_t handle; + u8_t role; + bt_addr_le_t peer_addr; + u16_t interval; + u16_t latency; + u16_t supv_timeout; + u8_t clock_accuracy; +} __packed; + +#define BT_HCI_EVT_LE_ADVERTISING_REPORT 0x02 +struct bt_hci_evt_le_advertising_info { + u8_t evt_type; + bt_addr_le_t addr; + u8_t length; + u8_t data[0]; +} __packed; +struct bt_hci_evt_le_advertising_report { + u8_t num_reports; + struct bt_hci_evt_le_advertising_info adv_info[0]; +} __packed; + +#define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE 0x03 +struct bt_hci_evt_le_conn_update_complete { + u8_t status; + u16_t handle; + u16_t interval; + u16_t latency; + u16_t supv_timeout; +} __packed; + +#define BT_HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04 +struct bt_hci_evt_le_remote_feat_complete { + u8_t status; + u16_t handle; + u8_t features[8]; +} __packed; + +#define BT_HCI_EVT_LE_LTK_REQUEST 0x05 +struct bt_hci_evt_le_ltk_request { + u16_t handle; + u64_t rand; + u16_t ediv; +} __packed; + +#define BT_HCI_EVT_LE_CONN_PARAM_REQ 0x06 +struct bt_hci_evt_le_conn_param_req { + u16_t handle; + u16_t interval_min; + u16_t interval_max; + u16_t latency; + u16_t timeout; +} __packed; + +#define BT_HCI_EVT_LE_DATA_LEN_CHANGE 0x07 +struct bt_hci_evt_le_data_len_change { + u16_t handle; + u16_t max_tx_octets; + u16_t max_tx_time; + u16_t max_rx_octets; + u16_t max_rx_time; +} __packed; + +#define BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE 0x08 +struct bt_hci_evt_le_p256_public_key_complete { + u8_t status; + u8_t key[64]; +} __packed; + +#define BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE 0x09 +struct bt_hci_evt_le_generate_dhkey_complete { + u8_t status; + u8_t dhkey[32]; +} __packed; + +#define BT_HCI_EVT_LE_ENH_CONN_COMPLETE 0x0a +struct bt_hci_evt_le_enh_conn_complete { + u8_t status; + u16_t handle; + u8_t role; + bt_addr_le_t peer_addr; + bt_addr_t local_rpa; + bt_addr_t peer_rpa; + u16_t interval; + u16_t latency; + u16_t supv_timeout; + u8_t clock_accuracy; +} __packed; + +#define BT_HCI_EVT_LE_DIRECT_ADV_REPORT 0x0b +struct bt_hci_evt_le_direct_adv_info { + u8_t evt_type; + bt_addr_le_t addr; + bt_addr_le_t dir_addr; + s8_t rssi; +} __packed; +struct bt_hci_evt_le_direct_adv_report { + u8_t num_reports; + struct bt_hci_evt_le_direct_adv_info direct_adv_info[0]; +} __packed; + +#define BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE 0x0c +struct bt_hci_evt_le_phy_update_complete { + u8_t status; + u16_t handle; + u8_t tx_phy; + u8_t rx_phy; +} __packed; + +#define BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT 0x0d + +#define BT_HCI_LE_ADV_EVT_TYPE_CONN BIT(0) +#define BT_HCI_LE_ADV_EVT_TYPE_SCAN BIT(1) +#define BT_HCI_LE_ADV_EVT_TYPE_DIRECT BIT(2) +#define BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP BIT(3) +#define BT_HCI_LE_ADV_EVT_TYPE_LEGACY BIT(4) + +#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(ev_type) (((ev_type) >> 5) & 0x03) +#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_COMPLETE 0 +#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_PARTIAL 1 +#define BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS_INCOMPLETE 2 + +struct bt_hci_evt_le_ext_advertising_info { + u8_t evt_type; + bt_addr_le_t addr; + u8_t prim_phy; + u8_t sec_phy; + u8_t sid; + s8_t tx_power; + s8_t rssi; + u16_t interval; + bt_addr_le_t direct_addr; + u8_t length; + u8_t data[0]; +} __packed; +struct bt_hci_evt_le_ext_advertising_report { + u8_t num_reports; + struct bt_hci_evt_le_ext_advertising_info adv_info[0]; +} __packed; + +#define BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED 0x0e +struct bt_hci_evt_le_per_adv_sync_established { + u8_t status; + u16_t handle; + u8_t sid; + bt_addr_le_t adv_addr; + u8_t phy; + u16_t interval; + u8_t clock_accuracy; +} __packed; + +#define BT_HCI_EVT_LE_PER_ADVERTISING_REPORT 0x0f +struct bt_hci_evt_le_per_advertising_report { + u16_t handle; + s8_t tx_power; + s8_t rssi; + u8_t unused; + u8_t data_status; + u8_t length; + u8_t data[0]; +} __packed; + +#define BT_HCI_EVT_LE_PER_ADV_SYNC_LOST 0x10 +struct bt_hci_evt_le_per_adv_sync_lost { + u16_t handle; +} __packed; + +#define BT_HCI_EVT_LE_SCAN_TIMEOUT 0x11 + +#define BT_HCI_EVT_LE_ADV_SET_TERMINATED 0x12 +struct bt_hci_evt_le_per_adv_set_terminated { + u8_t status; + u8_t adv_handle; + u16_t conn_handle; + u8_t num_completed_ext_adv_evts; +} __packed; + +#define BT_HCI_EVT_LE_SCAN_REQ_RECEIVED 0x13 +struct bt_hci_evt_le_scan_req_received { + u8_t handle; + bt_addr_le_t addr; +} __packed; + +#define BT_HCI_LE_CHAN_SEL_ALGO_1 0x00 +#define BT_HCI_LE_CHAN_SEL_ALGO_2 0x01 + +#define BT_HCI_EVT_LE_CHAN_SEL_ALGO 0x14 +struct bt_hci_evt_le_chan_sel_algo { + u16_t handle; + u8_t chan_sel_algo; +} __packed; + +#define BT_HCI_EVT_LE_PAST_RECEIVED 0x18 +struct bt_hci_evt_le_past_received { + uint8_t status; + uint16_t conn_handle; + uint16_t service_data; + uint16_t sync_handle; + uint8_t adv_sid; + bt_addr_le_t addr; + uint8_t phy; + uint16_t interval; + uint8_t clock_accuracy; +} __packed; + +#define BT_HCI_EVT_LE_CIS_ESTABLISHED 0x19 +struct bt_hci_evt_le_cis_established { + uint8_t status; + uint16_t conn_handle; + uint8_t cig_sync_delay[3]; + uint8_t cis_sync_delay[3]; + uint8_t m_latency[3]; + uint8_t s_latency[3]; + uint8_t m_phy; + uint8_t s_phy; + uint8_t nse; + uint8_t m_bn; + uint8_t s_bn; + uint8_t m_ft; + uint8_t s_ft; + uint16_t m_max_pdu; + uint16_t s_max_pdu; + uint16_t interval; +} __packed; + +#define BT_HCI_EVT_LE_CIS_REQ 0x1a +struct bt_hci_evt_le_cis_req { + uint16_t acl_handle; + uint16_t cis_handle; + uint8_t cig_id; + uint8_t cis_id; +} __packed; + +#define BT_HCI_EVT_LE_BIG_COMPLETE 0x1b +struct bt_hci_evt_le_big_complete { + uint8_t status; + uint8_t big_handle; + uint8_t sync_delay[3]; + uint8_t latency[3]; + uint8_t phy; + uint8_t nse; + uint8_t bn; + uint8_t pto; + uint8_t irc; + uint16_t max_pdu; + uint8_t num_bis; + uint16_t handle[0]; +} __packed; + +#define BT_HCI_EVT_LE_BIG_TERMINATE 0x1c +struct bt_hci_evt_le_big_terminate { + uint8_t big_handle; + uint8_t reason; +} __packed; + +#define BT_HCI_EVT_LE_BIG_SYNC_ESTABLISHED 0x1d +struct bt_hci_evt_le_big_sync_established { + uint8_t status; + uint8_t big_handle; + uint8_t latency[3]; + uint8_t nse; + uint8_t bn; + uint8_t pto; + uint8_t irc; + uint16_t max_pdu; + uint8_t num_bis; + uint16_t handle[0]; +} __packed; + +#define BT_HCI_EVT_LE_BIG_SYNC_LOST 0x1e +struct bt_hci_evt_le_big_sync_lost { + uint8_t big_handle; + uint8_t reason; +} __packed; + +#define BT_HCI_EVT_LE_REQ_PEER_SCA_COMPLETE 0x1f +struct bt_hci_evt_le_req_peer_sca_complete { + uint8_t status; + uint16_t handle; + uint8_t sca; +} __packed; + +#define BT_HCI_EVT_LE_BIGINFO_ADV_REPORT 0x22 +struct bt_hci_evt_le_biginfo_adv_report { + uint16_t sync_handle; + uint8_t num_bis; + uint8_t nse; + uint16_t iso_interval; + uint8_t bn; + uint8_t pto; + uint8_t irc; + uint16_t max_pdu; + uint8_t sdu_interval[3]; + uint16_t max_sdu; + uint8_t phy; + uint8_t framing; + uint8_t encryption; +} __packed; + +/* Event mask bits */ + +#define BT_EVT_BIT(n) (1ULL << (n)) + +#define BT_EVT_MASK_INQUIRY_COMPLETE BT_EVT_BIT(0) +#define BT_EVT_MASK_CONN_COMPLETE BT_EVT_BIT(2) +#define BT_EVT_MASK_CONN_REQUEST BT_EVT_BIT(3) +#define BT_EVT_MASK_DISCONN_COMPLETE BT_EVT_BIT(4) +#define BT_EVT_MASK_AUTH_COMPLETE BT_EVT_BIT(5) +#define BT_EVT_MASK_REMOTE_NAME_REQ_COMPLETE BT_EVT_BIT(6) +#define BT_EVT_MASK_ENCRYPT_CHANGE BT_EVT_BIT(7) +#define BT_EVT_MASK_REMOTE_FEATURES BT_EVT_BIT(10) +#define BT_EVT_MASK_REMOTE_VERSION_INFO BT_EVT_BIT(11) +#define BT_EVT_MASK_HARDWARE_ERROR BT_EVT_BIT(15) +#define BT_EVT_MASK_ROLE_CHANGE BT_EVT_BIT(17) +#define BT_EVT_MASK_PIN_CODE_REQ BT_EVT_BIT(21) +#define BT_EVT_MASK_LINK_KEY_REQ BT_EVT_BIT(22) +#define BT_EVT_MASK_LINK_KEY_NOTIFY BT_EVT_BIT(23) +#define BT_EVT_MASK_DATA_BUFFER_OVERFLOW BT_EVT_BIT(25) +#define BT_EVT_MASK_INQUIRY_RESULT_WITH_RSSI BT_EVT_BIT(33) +#define BT_EVT_MASK_REMOTE_EXT_FEATURES BT_EVT_BIT(34) +#define BT_EVT_MASK_SYNC_CONN_COMPLETE BT_EVT_BIT(43) +#define BT_EVT_MASK_EXTENDED_INQUIRY_RESULT BT_EVT_BIT(46) +#define BT_EVT_MASK_ENCRYPT_KEY_REFRESH_COMPLETE BT_EVT_BIT(47) +#define BT_EVT_MASK_IO_CAPA_REQ BT_EVT_BIT(48) +#define BT_EVT_MASK_IO_CAPA_RESP BT_EVT_BIT(49) +#define BT_EVT_MASK_USER_CONFIRM_REQ BT_EVT_BIT(50) +#define BT_EVT_MASK_USER_PASSKEY_REQ BT_EVT_BIT(51) +#define BT_EVT_MASK_SSP_COMPLETE BT_EVT_BIT(53) +#define BT_EVT_MASK_USER_PASSKEY_NOTIFY BT_EVT_BIT(58) +#define BT_EVT_MASK_LE_META_EVENT BT_EVT_BIT(61) + +/* Page 2 */ +#define BT_EVT_MASK_PHY_LINK_COMPLETE BT_EVT_BIT(0) +#define BT_EVT_MASK_CH_SELECTED_COMPLETE BT_EVT_BIT(1) +#define BT_EVT_MASK_DISCONN_PHY_LINK_COMPLETE BT_EVT_BIT(2) +#define BT_EVT_MASK_PHY_LINK_LOSS_EARLY_WARN BT_EVT_BIT(3) +#define BT_EVT_MASK_PHY_LINK_RECOVERY BT_EVT_BIT(4) +#define BT_EVT_MASK_LOG_LINK_COMPLETE BT_EVT_BIT(5) +#define BT_EVT_MASK_DISCONN_LOG_LINK_COMPLETE BT_EVT_BIT(6) +#define BT_EVT_MASK_FLOW_SPEC_MODIFY_COMPLETE BT_EVT_BIT(7) +#define BT_EVT_MASK_NUM_COMPLETE_DATA_BLOCKS BT_EVT_BIT(8) +#define BT_EVT_MASK_AMP_START_TEST BT_EVT_BIT(9) +#define BT_EVT_MASK_AMP_TEST_END BT_EVT_BIT(10) +#define BT_EVT_MASK_AMP_RX_REPORT BT_EVT_BIT(11) +#define BT_EVT_MASK_AMP_SR_MODE_CHANGE_COMPLETE BT_EVT_BIT(12) +#define BT_EVT_MASK_AMP_STATUS_CHANGE BT_EVT_BIT(13) +#define BT_EVT_MASK_TRIGG_CLOCK_CAPTURE BT_EVT_BIT(14) +#define BT_EVT_MASK_SYNCH_TRAIN_COMPLETE BT_EVT_BIT(15) +#define BT_EVT_MASK_SYNCH_TRAIN_RX BT_EVT_BIT(16) +#define BT_EVT_MASK_CL_SLAVE_BC_RX BT_EVT_BIT(17) +#define BT_EVT_MASK_CL_SLAVE_BC_TIMEOUT BT_EVT_BIT(18) +#define BT_EVT_MASK_TRUNC_PAGE_COMPLETE BT_EVT_BIT(19) +#define BT_EVT_MASK_SLAVE_PAGE_RSP_TIMEOUT BT_EVT_BIT(20) +#define BT_EVT_MASK_CL_SLAVE_BC_CH_MAP_CHANGE BT_EVT_BIT(21) +#define BT_EVT_MASK_INQUIRY_RSP_NOT BT_EVT_BIT(22) +#define BT_EVT_MASK_AUTH_PAYLOAD_TIMEOUT_EXP BT_EVT_BIT(23) +#define BT_EVT_MASK_SAM_STATUS_CHANGE BT_EVT_BIT(24) + +#define BT_EVT_MASK_LE_CONN_COMPLETE BT_EVT_BIT(0) +#define BT_EVT_MASK_LE_ADVERTISING_REPORT BT_EVT_BIT(1) +#define BT_EVT_MASK_LE_CONN_UPDATE_COMPLETE BT_EVT_BIT(2) +#define BT_EVT_MASK_LE_REMOTE_FEAT_COMPLETE BT_EVT_BIT(3) +#define BT_EVT_MASK_LE_LTK_REQUEST BT_EVT_BIT(4) +#define BT_EVT_MASK_LE_CONN_PARAM_REQ BT_EVT_BIT(5) +#define BT_EVT_MASK_LE_DATA_LEN_CHANGE BT_EVT_BIT(6) +#define BT_EVT_MASK_LE_P256_PUBLIC_KEY_COMPLETE BT_EVT_BIT(7) +#define BT_EVT_MASK_LE_GENERATE_DHKEY_COMPLETE BT_EVT_BIT(8) +#define BT_EVT_MASK_LE_ENH_CONN_COMPLETE BT_EVT_BIT(9) +#define BT_EVT_MASK_LE_DIRECT_ADV_REPORT BT_EVT_BIT(10) +#define BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE BT_EVT_BIT(11) +#define BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT BT_EVT_BIT(12) +#define BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED BT_EVT_BIT(13) +#define BT_EVT_MASK_LE_PER_ADVERTISING_REPORT BT_EVT_BIT(14) +#define BT_EVT_MASK_LE_PER_ADV_SYNC_LOST BT_EVT_BIT(15) +#define BT_EVT_MASK_LE_SCAN_TIMEOUT BT_EVT_BIT(16) +#define BT_EVT_MASK_LE_ADV_SET_TERMINATED BT_EVT_BIT(17) +#define BT_EVT_MASK_LE_SCAN_REQ_RECEIVED BT_EVT_BIT(18) +#define BT_EVT_MASK_LE_CHAN_SEL_ALGO BT_EVT_BIT(19) +#define BT_EVT_MASK_LE_PAST_RECEIVED BT_EVT_BIT(23) +#define BT_EVT_MASK_LE_CIS_ESTABLISHED BT_EVT_BIT(24) +#define BT_EVT_MASK_LE_CIS_REQ BT_EVT_BIT(25) +#define BT_EVT_MASK_LE_BIG_COMPLETE BT_EVT_BIT(26) +#define BT_EVT_MASK_LE_BIG_TERMINATED BT_EVT_BIT(27) +#define BT_EVT_MASK_LE_BIG_SYNC_ESTABLISHED BT_EVT_BIT(28) +#define BT_EVT_MASK_LE_BIG_SYNC_LOST BT_EVT_BIT(29) +#define BT_EVT_MASK_LE_REQ_PEER_SCA_COMPLETE BT_EVT_BIT(30) +#define BT_EVT_MASK_LE_PATH_LOSS_THRESHOLD BT_EVT_BIT(31) +#define BT_EVT_MASK_LE_TRANSMIT_POWER_REPORTING BT_EVT_BIT(32) +#define BT_EVT_MASK_LE_BIGINFO_ADV_REPORT BT_EVT_BIT(33) + +/** Allocate a HCI command buffer. + * + * This function allocates a new buffer for a HCI command. It is given + * the OpCode (encoded e.g. using the BT_OP macro) and the total length + * of the parameters. Upon successful return the buffer is ready to have + * the parameters encoded into it. + * + * @param opcode Command OpCode. + * @param param_len Length of command parameters. + * + * @return Newly allocated buffer. + */ +struct net_buf *bt_hci_cmd_create(u16_t opcode, u8_t param_len); + +/** Send a HCI command asynchronously. + * + * This function is used for sending a HCI command asynchronously. It can + * either be called for a buffer created using bt_hci_cmd_create(), or + * if the command has no parameters a NULL can be passed instead. The + * sending of the command will happen asynchronously, i.e. upon successful + * return from this function the caller only knows that it was queued + * successfully. + * + * If synchronous behavior, and retrieval of the Command Complete parameters + * is desired, the bt_hci_cmd_send_sync() API should be used instead. + * + * @param opcode Command OpCode. + * @param buf Command buffer or NULL (if no parameters). + * + * @return 0 on success or negative error value on failure. + */ +int bt_hci_cmd_send(u16_t opcode, struct net_buf *buf); + +/** Send a HCI command synchronously. + * + * This function is used for sending a HCI command synchronously. It can + * either be called for a buffer created using bt_hci_cmd_create(), or + * if the command has no parameters a NULL can be passed instead. + * + * The function will block until a Command Status or a Command Complete + * event is returned. If either of these have a non-zero status the function + * will return a negative error code and the response reference will not + * be set. If the command completed successfully and a non-NULL rsp parameter + * was given, this parameter will be set to point to a buffer containing + * the response parameters. + * + * @param opcode Command OpCode. + * @param buf Command buffer or NULL (if no parameters). + * @param rsp Place to store a reference to the command response. May + * be NULL if the caller is not interested in the response + * parameters. If non-NULL is passed the caller is responsible + * for calling net_buf_unref() on the buffer when done parsing + * it. + * + * @return 0 on success or negative error value on failure. + */ +int bt_hci_cmd_send_sync(u16_t opcode, struct net_buf *buf, + struct net_buf **rsp); + +//declare bt_hci_get_conn_handle in conn_internal.h to pass compile +#if !defined(BFLB_BLE) +/** @brief Get connection handle for a connection. + * + * @param conn Connection object. + * @param conn_handle Place to store the Connection handle. + * + * @return 0 on success or negative error value on failure. + */ +int bt_hci_get_conn_handle(const struct bt_conn *conn, u16_t *conn_handle); +#endif + +/** @typedef bt_hci_vnd_evt_cb_t + * @brief Callback type for vendor handling of HCI Vendor-Specific Events. + * + * A function of this type is registered with bt_hci_register_vnd_evt_cb() + * and will be called for any HCI Vendor-Specific Event. + * + * @param buf Buffer containing event parameters. + * + * @return true if the function handles the event or false to defer the + * handling of this event back to the stack. + */ +typedef bool bt_hci_vnd_evt_cb_t(struct net_buf_simple *buf); + +/** Register user callback for HCI Vendor-Specific Events + * + * @param cb Callback to be called when the stack receives a + * HCI Vendor-Specific Event. + * + * @return 0 on success or negative error value on failure. + */ +int bt_hci_register_vnd_evt_cb(bt_hci_vnd_evt_cb_t cb); + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_HCI_H */ diff --git a/components/ble/ble_stack/include/bluetooth/hci_raw.h b/components/ble/ble_stack/include/bluetooth/hci_raw.h new file mode 100644 index 00000000..dba2d4af --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/hci_raw.h @@ -0,0 +1,54 @@ +/** @file + * @brief Bluetooth HCI RAW channel handling + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_ + +/** + * @brief HCI RAW channel + * @defgroup hci_raw HCI RAW channel + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Send packet to the Bluetooth controller + * + * Send packet to the Bluetooth controller. Caller needs to + * implement netbuf pool. + * + * @param buf netbuf packet to be send + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_send(struct net_buf *buf); + +/** @brief Enable Bluetooth RAW channel + * + * Enable Bluetooth RAW HCI channel. + * + * @param rx_queue netbuf queue where HCI packets received from the Bluetooth + * controller are to be queued. The queue is defined in the caller while + * the available buffers pools are handled in the stack. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_enable_raw(struct k_fifo *rx_queue); + +#ifdef __cplusplus +} +#endif +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HCI_RAW_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/hci_vs.h b/components/ble/ble_stack/include/bluetooth/hci_vs.h new file mode 100644 index 00000000..fd717f50 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/hci_vs.h @@ -0,0 +1,318 @@ +/* hci_vs.h - Bluetooth Host Control Interface Vendor Specific definitions */ + +/* + * Copyright (c) 2017-2018 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HCI_VS_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_HCI_VS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BT_HCI_VS_HW_PLAT_INTEL 0x0001 +#define BT_HCI_VS_HW_PLAT_NORDIC 0x0002 +#define BT_HCI_VS_HW_PLAT_NXP 0x0003 + +#define BT_HCI_VS_HW_VAR_NORDIC_NRF51X 0x0001 +#define BT_HCI_VS_HW_VAR_NORDIC_NRF52X 0x0002 +#define BT_HCI_VS_HW_VAR_NORDIC_NRF53X 0x0003 + +#define BT_HCI_VS_FW_VAR_STANDARD_CTLR 0x0001 +#define BT_HCI_VS_FW_VAR_VS_CTLR 0x0002 +#define BT_HCI_VS_FW_VAR_FW_LOADER 0x0003 +#define BT_HCI_VS_FW_VAR_RESCUE_IMG 0x0004 + +#if !defined(BFLB_BLE) +#define BT_HCI_OP_VS_READ_VERSION_INFO BT_OP(BT_OGF_VS, 0x0001) +struct bt_hci_rp_vs_read_version_info { + u8_t status; + u16_t hw_platform; + u16_t hw_variant; + u8_t fw_variant; + u8_t fw_version; + u16_t fw_revision; + u32_t fw_build; +} __packed; + +#define BT_HCI_OP_VS_READ_SUPPORTED_COMMANDS BT_OP(BT_OGF_VS, 0x0002) +struct bt_hci_rp_vs_read_supported_commands { + u8_t status; + u8_t commands[64]; +} __packed; + +#define BT_HCI_OP_VS_READ_SUPPORTED_FEATURES BT_OP(BT_OGF_VS, 0x0003) +struct bt_hci_rp_vs_read_supported_features { + u8_t status; + u8_t features[8]; +} __packed; + +#define BT_HCI_OP_VS_SET_EVENT_MASK BT_OP(BT_OGF_VS, 0x0004) +struct bt_hci_cp_vs_set_event_mask { + u8_t event_mask[8]; +} __packed; + +#define BT_HCI_VS_RESET_SOFT 0x00 +#define BT_HCI_VS_RESET_HARD 0x01 +#define BT_HCI_OP_VS_RESET BT_OP(BT_OGF_VS, 0x0005) +struct bt_hci_cp_vs_reset { + u8_t type; +} __packed; + +#define BT_HCI_OP_VS_WRITE_BD_ADDR BT_OP(BT_OGF_VS, 0x0006) +struct bt_hci_cp_vs_write_bd_addr { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_VS_TRACE_DISABLED 0x00 +#define BT_HCI_VS_TRACE_ENABLED 0x01 + +#define BT_HCI_VS_TRACE_HCI_EVTS 0x00 +#define BT_HCI_VS_TRACE_VDC 0x01 +#define BT_HCI_OP_VS_SET_TRACE_ENABLE BT_OP(BT_OGF_VS, 0x0007) +struct bt_hci_cp_vs_set_trace_enable { + u8_t enable; + u8_t type; +} __packed; + +#define BT_HCI_OP_VS_READ_BUILD_INFO BT_OP(BT_OGF_VS, 0x0008) +struct bt_hci_rp_vs_read_build_info { + u8_t status; + u8_t info[0]; +} __packed; + +struct bt_hci_vs_static_addr { + bt_addr_t bdaddr; + u8_t ir[16]; +} __packed; + +#define BT_HCI_OP_VS_READ_STATIC_ADDRS BT_OP(BT_OGF_VS, 0x0009) +struct bt_hci_rp_vs_read_static_addrs { + u8_t status; + u8_t num_addrs; + struct bt_hci_vs_static_addr a[0]; +} __packed; + +#define BT_HCI_OP_VS_READ_KEY_HIERARCHY_ROOTS BT_OP(BT_OGF_VS, 0x000a) +struct bt_hci_rp_vs_read_key_hierarchy_roots { + u8_t status; + u8_t ir[16]; + u8_t er[16]; +} __packed; + +#define BT_HCI_OP_VS_READ_CHIP_TEMP BT_OP(BT_OGF_VS, 0x000b) +struct bt_hci_rp_vs_read_chip_temp { + u8_t status; + s8_t temps; +} __packed; + +struct bt_hci_vs_cmd { + u16_t vendor_id; + u16_t opcode_base; +} __packed; + +#define BT_HCI_VS_VID_ANDROID 0x0001 +#define BT_HCI_VS_VID_MICROSOFT 0x0002 +#define BT_HCI_OP_VS_READ_HOST_STACK_CMDS BT_OP(BT_OGF_VS, 0x000c) +struct bt_hci_rp_vs_read_host_stack_cmds { + u8_t status; + u8_t num_cmds; + struct bt_hci_vs_cmd c[0]; +} __packed; + +#define BT_HCI_VS_SCAN_REQ_REPORTS_DISABLED 0x00 +#define BT_HCI_VS_SCAN_REQ_REPORTS_ENABLED 0x01 +#define BT_HCI_OP_VS_SET_SCAN_REQ_REPORTS BT_OP(BT_OGF_VS, 0x000d) +struct bt_hci_cp_vs_set_scan_req_reports { + u8_t enable; +} __packed; +#endif //BFLB_BLE + +#if defined(CONFIG_SET_TX_PWR) +#define BT_HCI_OP_VS_SET_TX_PWR BT_OP(BT_OGF_VS, 0x0061) +struct bt_hci_cp_vs_set_tx_pwr { + int8_t power; +}__packed; +#endif + +/* Events */ + +struct bt_hci_evt_vs { + u8_t subevent; +} __packed; + +#define BT_HCI_EVT_VS_FATAL_ERROR 0x02 +struct bt_hci_evt_vs_fatal_error { + u64_t pc; + u8_t err_info[0]; +} __packed; + +#define BT_HCI_VS_TRACE_LMP_TX 0x01 +#define BT_HCI_VS_TRACE_LMP_RX 0x02 +#define BT_HCI_VS_TRACE_LLCP_TX 0x03 +#define BT_HCI_VS_TRACE_LLCP_RX 0x04 +#define BT_HCI_VS_TRACE_LE_CONN_IND 0x05 +#define BT_HCI_EVT_VS_TRACE_INFO 0x03 +struct bt_hci_evt_vs_trace_info { + u8_t type; + u8_t data[0]; +} __packed; + +#define BT_HCI_EVT_VS_SCAN_REQ_RX 0x04 +struct bt_hci_evt_vs_scan_req_rx { + bt_addr_le_t addr; + s8_t rssi; +} __packed; + +/* Event mask bits */ + +#define BT_EVT_MASK_VS_FATAL_ERROR BT_EVT_BIT(1) +#define BT_EVT_MASK_VS_TRACE_INFO BT_EVT_BIT(2) +#define BT_EVT_MASK_VS_SCAN_REQ_RX BT_EVT_BIT(3) + +/* Mesh HCI commands */ +#define BT_HCI_MESH_REVISION 0x01 + +#define BT_HCI_OP_VS_MESH BT_OP(BT_OGF_VS, 0x0042) +#define BT_HCI_MESH_EVT_PREFIX 0xF0 + +struct bt_hci_cp_mesh { + u8_t opcode; +} __packed; + +#define BT_HCI_OC_MESH_GET_OPTS 0x00 +struct bt_hci_rp_mesh_get_opts { + u8_t status; + u8_t opcode; + u8_t revision; + u8_t ch_map; + s8_t min_tx_power; + s8_t max_tx_power; + u8_t max_scan_filter; + u8_t max_filter_pattern; + u8_t max_adv_slot; + u8_t max_tx_window; + u8_t evt_prefix_len; + u8_t evt_prefix; +} __packed; + +#define BT_HCI_MESH_PATTERN_LEN_MAX 0x0f + +#define BT_HCI_OC_MESH_SET_SCAN_FILTER 0x01 +struct bt_hci_mesh_pattern { + u8_t pattern_len; + u8_t pattern[0]; +} __packed; + +struct bt_hci_cp_mesh_set_scan_filter { + u8_t scan_filter; + u8_t filter_dup; + u8_t num_patterns; + struct bt_hci_mesh_pattern patterns[0]; +} __packed; +struct bt_hci_rp_mesh_set_scan_filter { + u8_t status; + u8_t opcode; + u8_t scan_filter; +} __packed; + +#define BT_HCI_OC_MESH_ADVERTISE 0x02 +struct bt_hci_cp_mesh_advertise { + u8_t adv_slot; + u8_t own_addr_type; + bt_addr_t random_addr; + u8_t ch_map; + s8_t tx_power; + u8_t min_tx_delay; + u8_t max_tx_delay; + u8_t retx_count; + u8_t retx_interval; + u8_t scan_delay; + u16_t scan_duration; + u8_t scan_filter; + u8_t data_len; + u8_t data[31]; +} __packed; +struct bt_hci_rp_mesh_advertise { + u8_t status; + u8_t opcode; + u8_t adv_slot; +} __packed; + +#define BT_HCI_OC_MESH_ADVERTISE_TIMED 0x03 +struct bt_hci_cp_mesh_advertise_timed { + u8_t adv_slot; + u8_t own_addr_type; + bt_addr_t random_addr; + u8_t ch_map; + s8_t tx_power; + u8_t retx_count; + u8_t retx_interval; + u32_t instant; + u16_t tx_delay; + u16_t tx_window; + u8_t data_len; + u8_t data[31]; +} __packed; +struct bt_hci_rp_mesh_advertise_timed { + u8_t status; + u8_t opcode; + u8_t adv_slot; +} __packed; + +#define BT_HCI_OC_MESH_ADVERTISE_CANCEL 0x04 +struct bt_hci_cp_mesh_advertise_cancel { + u8_t adv_slot; +} __packed; +struct bt_hci_rp_mesh_advertise_cancel { + u8_t status; + u8_t opcode; + u8_t adv_slot; +} __packed; + +#define BT_HCI_OC_MESH_SET_SCANNING 0x05 +struct bt_hci_cp_mesh_set_scanning { + u8_t enable; + u8_t ch_map; + u8_t scan_filter; +} __packed; +struct bt_hci_rp_mesh_set_scanning { + u8_t status; + u8_t opcode; +} __packed; + +/* Events */ +struct bt_hci_evt_mesh { + u8_t prefix; + u8_t subevent; +} __packed; + +#define BT_HCI_EVT_MESH_ADV_COMPLETE 0x00 +struct bt_hci_evt_mesh_adv_complete { + u8_t adv_slot; +} __packed; + +#define BT_HCI_EVT_MESH_SCANNING_REPORT 0x01 +struct bt_hci_evt_mesh_scan_report { + bt_addr_le_t addr; + u8_t chan; + s8_t rssi; + u32_t instant; + u8_t data_len; + u8_t data[0]; +} __packed; +struct bt_hci_evt_mesh_scanning_report { + u8_t num_reports; + struct bt_hci_evt_mesh_scan_report reports[0]; +} __packed; + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_HCI_VS_H */ diff --git a/components/ble/ble_stack/include/bluetooth/hfp_hf.h b/components/ble/ble_stack/include/bluetooth/hfp_hf.h new file mode 100644 index 00000000..c0bd0e52 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/hfp_hf.h @@ -0,0 +1,166 @@ +/** @file + * @brief Handsfree Profile handling. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_HFP_HF_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_HFP_HF_H_ + +/** + * @brief Hands Free Profile (HFP) + * @defgroup bt_hfp Hands Free Profile (HFP) + * @ingroup bluetooth + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* AT Commands */ +enum bt_hfp_hf_at_cmd { + BT_HFP_HF_ATA, + BT_HFP_HF_AT_CHUP, +}; + +/* + * Command complete types for the application + */ +#define HFP_HF_CMD_OK 0 +#define HFP_HF_CMD_ERROR 1 +#define HFP_HF_CMD_CME_ERROR 2 +#define HFP_HF_CMD_UNKNOWN_ERROR 4 + +/** @brief HFP HF Command completion field */ +struct bt_hfp_hf_cmd_complete { + /* Command complete status */ + uint8_t type; + /* CME error number to be added */ + uint8_t cme; +}; + +/** @brief HFP profile application callback */ +struct bt_hfp_hf_cb { + /** HF connected callback to application + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param conn Connection object. + */ + void (*connected)(struct bt_conn *conn); + /** HF disconnected callback to application + * + * If this callback is provided it will be called whenever the + * connection gets disconnected, including when a connection gets + * rejected or cancelled or any error in SLC establisment. + * + * @param conn Connection object. + */ + void (*disconnected)(struct bt_conn *conn); + /** HF indicator Callback + * + * This callback provides service indicator value to the application + * + * @param conn Connection object. + * @param value service indicator value received from the AG. + */ + void (*service)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call indicator value to the application + * + * @param conn Connection object. + * @param value call indicator value received from the AG. + */ + void (*call)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call setup indicator value to the application + * + * @param conn Connection object. + * @param value call setup indicator value received from the AG. + */ + void (*call_setup)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides call held indicator value to the application + * + * @param conn Connection object. + * @param value call held indicator value received from the AG. + */ + void (*call_held)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides signal indicator value to the application + * + * @param conn Connection object. + * @param value signal indicator value received from the AG. + */ + void (*signal)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback provides roaming indicator value to the application + * + * @param conn Connection object. + * @param value roaming indicator value received from the AG. + */ + void (*roam)(struct bt_conn *conn, uint32_t value); + /** HF indicator Callback + * + * This callback battery service indicator value to the application + * + * @param conn Connection object. + * @param value battery indicator value received from the AG. + */ + void (*battery)(struct bt_conn *conn, uint32_t value); + /** HF incoming call Ring indication callback to application + * + * If this callback is provided it will be called whenever there + * is an incoming call. + * + * @param conn Connection object. + */ + void (*ring_indication)(struct bt_conn *conn); + /** HF notify command completed callback to application + * + * The command sent from the application is notified about its status + * + * @param conn Connection object. + * @param cmd structure contains status of the command including cme. + */ + void (*cmd_complete_cb)(struct bt_conn *conn, + struct bt_hfp_hf_cmd_complete *cmd); +}; + +/** +* @brief Initialize HFP_HF layer +*/ +int bt_hfp_hf_init(void); + +/** @brief Handsfree client Send AT + * + * Send specific AT commands to handsfree client profile. + * + * @param conn Connection object. + * @param cmd AT command to be sent. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_hfp_hf_send_cmd(struct bt_conn *conn, enum bt_hfp_hf_at_cmd cmd); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_HFP_HF_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/iso.h b/components/ble/ble_stack/include/bluetooth/iso.h new file mode 100644 index 00000000..4914475d --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/iso.h @@ -0,0 +1,256 @@ +/** @file + * @brief Bluetooth ISO handling + */ + +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ + +/** + * @brief ISO + * @defgroup bt_iso ISO + * @ingroup bluetooth + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/** @def BT_ISO_CHAN_SEND_RESERVE + * @brief Headroom needed for outgoing buffers + */ +#define BT_ISO_CHAN_SEND_RESERVE (CONFIG_BT_HCI_RESERVE + \ + BT_HCI_ISO_HDR_SIZE + \ + BT_HCI_ISO_DATA_HDR_SIZE) + +struct bt_iso_chan; + +/** @brief Life-span states of ISO channel. Used only by internal APIs + * dealing with setting channel to proper state depending on operational + * context. + */ +enum { + /** Channel disconnected */ + BT_ISO_DISCONNECTED, + /** Channel bound to a connection */ + BT_ISO_BOUND, + /** Channel in connecting state */ + BT_ISO_CONNECT, + /** Channel ready for upper layer traffic on it */ + BT_ISO_CONNECTED, + /** Channel in disconnecting state */ + BT_ISO_DISCONNECT, +}; + +/** @brief ISO Channel structure. */ +struct bt_iso_chan { + /** Channel connection reference */ + struct bt_conn *conn; + /** Channel operations reference */ + struct bt_iso_chan_ops *ops; + /** Channel QoS reference */ + struct bt_iso_chan_qos *qos; + /** Channel data path reference*/ + struct bt_iso_chan_path *path; + sys_snode_t node; + uint8_t state; + bt_security_t required_sec_level; +}; + +/** @brief Audio QoS direction */ +enum { + BT_ISO_CHAN_QOS_IN, + BT_ISO_CHAN_QOS_OUT, + BT_ISO_CHAN_QOS_INOUT +}; + +/** @brief ISO Channel QoS structure. */ +struct bt_iso_chan_qos { + /** @brief Channel direction + * + * Possible values: BT_ISO_CHAN_QOS_IN, BT_ISO_CHAN_QOS_OUT or + * BT_ISO_CHAN_QOS_INOUT. + */ + uint8_t dir; + /** Channel interval */ + uint32_t interval; + /** Channel SCA */ + uint8_t sca; + /** Channel packing mode */ + uint8_t packing; + /** Channel framing mode */ + uint8_t framing; + /** Channel Latency */ + uint16_t latency; + /** Channel SDU */ + uint8_t sdu; + /** Channel PHY */ + uint8_t phy; + /** Channel Retransmission Number */ + uint8_t rtn; +}; + +/** @brief ISO Channel Data Path structure. */ +struct bt_iso_chan_path { + /** Default path ID */ + uint8_t pid; + /** Coding Format */ + uint8_t format; + /** Company ID */ + uint16_t cid; + /** Vendor-defined Codec ID */ + uint16_t vid; + /** Controller Delay */ + uint32_t delay; + /** Codec Configuration length*/ + uint8_t cc_len; + /** Codec Configuration */ + uint8_t cc[0]; +}; + +/** @brief ISO Channel operations structure. */ +struct bt_iso_chan_ops { + /** @brief Channel connected callback + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param chan The channel that has been connected + */ + void (*connected)(struct bt_iso_chan *chan); + + /** @brief Channel disconnected callback + * + * If this callback is provided it will be called whenever the + * channel is disconnected, including when a connection gets + * rejected. + * + * @param chan The channel that has been Disconnected + */ + void (*disconnected)(struct bt_iso_chan *chan); + + /** @brief Channel alloc_buf callback + * + * If this callback is provided the channel will use it to allocate + * buffers to store incoming data. + * + * @param chan The channel requesting a buffer. + * + * @return Allocated buffer. + */ + struct net_buf *(*alloc_buf)(struct bt_iso_chan *chan); + + /** @brief Channel recv callback + * + * @param chan The channel receiving data. + * @param buf Buffer containing incoming data. + */ + void (*recv)(struct bt_iso_chan *chan, struct net_buf *buf); +}; + +/** @brief ISO Server structure. */ +struct bt_iso_server { + /** Required minimim security level */ + bt_security_t sec_level; + + /** @brief Server accept callback + * + * This callback is called whenever a new incoming connection requires + * authorization. + * + * @param conn The connection that is requesting authorization + * @param chan Pointer to receive the allocated channel + * + * @return 0 in case of success or negative value in case of error. + */ + int (*accept)(struct bt_conn *conn, struct bt_iso_chan **chan); +}; + +/** @brief Register ISO server. + * + * Register ISO server, each new connection is authorized using the accept() + * callback which in case of success shall allocate the channel structure + * to be used by the new connection. + * + * @param server Server structure. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_server_register(struct bt_iso_server *server); + +/** @brief Bind ISO channels + * + * Bind ISO channels with existing ACL connections, Channel objects passed + * (over an address of it) shouldn't be instantiated in application as + * standalone. + * + * @param conns Array of ACL connection objects + * @param num_conns Number of connection objects + * @param chans Array of ISO Channel objects to be created + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_bind(struct bt_conn **conns, uint8_t num_conns, + struct bt_iso_chan **chans); + +/** @brief Connect ISO channels + * + * Connect ISO channels, once the connection is completed each channel + * connected() callback will be called. If the connection is rejected + * disconnected() callback is called instead. + * Channel object passed (over an address of it) as second parameter shouldn't + * be instantiated in application as standalone. + * + * @param chans Array of ISO channel objects + * @param num_chans Number of channel objects + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_connect(struct bt_iso_chan **chans, uint8_t num_chans); + +/** @brief Disconnect ISO channel + * + * Disconnect ISO channel, if the connection is pending it will be + * canceled and as a result the channel disconnected() callback is called. + * Regarding to input parameter, to get details see reference description + * to bt_iso_chan_connect() API above. + * + * @param chan Channel object. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_iso_chan_disconnect(struct bt_iso_chan *chan); + +/** @brief Send data to ISO channel + * + * Send data from buffer to the channel. If credits are not available, buf will + * be queued and sent as and when credits are received from peer. + * Regarding to first input parameter, to get details see reference description + * to bt_iso_chan_connect() API above. + * + * @param chan Channel object. + * @param buf Buffer containing data to be sent. + * + * @return Bytes sent in case of success or negative value in case of error. + */ +int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_ISO_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/l2cap.h b/components/ble/ble_stack/include/bluetooth/l2cap.h new file mode 100644 index 00000000..7495f610 --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/l2cap.h @@ -0,0 +1,397 @@ +/** @file + * @brief Bluetooth L2CAP handling + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_H_ + +/** + * @brief L2CAP + * @defgroup bt_l2cap L2CAP + * @ingroup bluetooth + * @{ + */ + +#include +#include <../bluetooth/buf.h> +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/* L2CAP header size, used for buffer size calculations */ +#define BT_L2CAP_HDR_SIZE 4 + +/** @def BT_L2CAP_BUF_SIZE + * + * Helper to calculate needed outgoing buffer size, useful e.g. for + * creating buffer pools. + * + * @param mtu Needed L2CAP MTU. + * + * @return Needed buffer size to match the requested L2CAP MTU. + */ +#define BT_L2CAP_BUF_SIZE(mtu) (BT_BUF_RESERVE + \ + BT_HCI_ACL_HDR_SIZE + BT_L2CAP_HDR_SIZE + \ + (mtu)) + +struct bt_l2cap_chan; + +/** @typedef bt_l2cap_chan_destroy_t + * @brief Channel destroy callback + * + * @param chan Channel object. + */ +typedef void (*bt_l2cap_chan_destroy_t)(struct bt_l2cap_chan *chan); + +/** @brief Life-span states of L2CAP CoC channel. Used only by internal APIs + * dealing with setting channel to proper state depending on operational + * context. + */ +typedef enum bt_l2cap_chan_state { + /** Channel disconnected */ + BT_L2CAP_DISCONNECTED, + /** Channel in connecting state */ + BT_L2CAP_CONNECT, + /** Channel in config state, BR/EDR specific */ + BT_L2CAP_CONFIG, + /** Channel ready for upper layer traffic on it */ + BT_L2CAP_CONNECTED, + /** Channel in disconnecting state */ + BT_L2CAP_DISCONNECT, + +} __packed bt_l2cap_chan_state_t; + +/** @brief Status of L2CAP channel. */ +typedef enum bt_l2cap_chan_status { + /** Channel output status */ + BT_L2CAP_STATUS_OUT, + + /* Total number of status - must be at the end of the enum */ + BT_L2CAP_NUM_STATUS, +} __packed bt_l2cap_chan_status_t; + +/** @brief L2CAP Channel structure. */ +struct bt_l2cap_chan { + /** Channel connection reference */ + struct bt_conn *conn; + /** Channel operations reference */ + struct bt_l2cap_chan_ops *ops; + sys_snode_t node; + bt_l2cap_chan_destroy_t destroy; + /* Response Timeout eXpired (RTX) timer */ + struct k_delayed_work rtx_work; + ATOMIC_DEFINE(status, BT_L2CAP_NUM_STATUS); + +#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) + bt_l2cap_chan_state_t state; + /** Remote PSM to be connected */ + u16_t psm; + /** Helps match request context during CoC */ + u8_t ident; + bt_security_t required_sec_level; +#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */ +}; + +/** @brief LE L2CAP Endpoint structure. */ +struct bt_l2cap_le_endpoint { + /** Endpoint CID */ + u16_t cid; + /** Endpoint Maximum Transmission Unit */ + u16_t mtu; + /** Endpoint Maximum PDU payload Size */ + u16_t mps; + /** Endpoint initial credits */ + u16_t init_credits; + /** Endpoint credits */ + struct k_sem credits; +}; + +/** @brief LE L2CAP Channel structure. */ +struct bt_l2cap_le_chan { + /** Common L2CAP channel reference object */ + struct bt_l2cap_chan chan; + /** Channel Receiving Endpoint */ + struct bt_l2cap_le_endpoint rx; + /** Channel Transmission Endpoint */ + struct bt_l2cap_le_endpoint tx; + /** Channel Transmission queue */ + struct k_fifo tx_queue; + /** Channel Pending Transmission buffer */ + struct net_buf *tx_buf; + /** Segment SDU packet from upper layer */ + struct net_buf *_sdu; + u16_t _sdu_len; + + struct k_work rx_work; + struct k_fifo rx_queue; +}; + +/** @def BT_L2CAP_LE_CHAN(_ch) + * @brief Helper macro getting container object of type bt_l2cap_le_chan + * address having the same container chan member address as object in question. + * + * @param _ch Address of object of bt_l2cap_chan type + * + * @return Address of in memory bt_l2cap_le_chan object type containing + * the address of in question object. + */ +#define BT_L2CAP_LE_CHAN(_ch) CONTAINER_OF(_ch, struct bt_l2cap_le_chan, chan) + +/** @brief BREDR L2CAP Endpoint structure. */ +struct bt_l2cap_br_endpoint { + /** Endpoint CID */ + u16_t cid; + /** Endpoint Maximum Transmission Unit */ + u16_t mtu; +}; + +/** @brief BREDR L2CAP Channel structure. */ +struct bt_l2cap_br_chan { + /** Common L2CAP channel reference object */ + struct bt_l2cap_chan chan; + /** Channel Receiving Endpoint */ + struct bt_l2cap_br_endpoint rx; + /** Channel Transmission Endpoint */ + struct bt_l2cap_br_endpoint tx; + /* For internal use only */ + atomic_t flags[1]; +}; + +/** @brief L2CAP Channel operations structure. */ +struct bt_l2cap_chan_ops { + /** Channel connected callback + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param chan The channel that has been connected + */ + void (*connected)(struct bt_l2cap_chan *chan); + + /** Channel disconnected callback + * + * If this callback is provided it will be called whenever the + * channel is disconnected, including when a connection gets + * rejected. + * + * @param chan The channel that has been Disconnected + */ + void (*disconnected)(struct bt_l2cap_chan *chan); + + /** Channel encrypt_change callback + * + * If this callback is provided it will be called whenever the + * security level changed (indirectly link encryption done) or + * authentication procedure fails. In both cases security initiator + * and responder got the final status (HCI status) passed by + * related to encryption and authentication events from local host's + * controller. + * + * @param chan The channel which has made encryption status changed. + * @param status HCI status of performed security procedure caused + * by channel security requirements. The value is populated + * by HCI layer and set to 0 when success and to non-zero (reference to + * HCI Error Codes) when security/authentication failed. + */ + void (*encrypt_change)(struct bt_l2cap_chan *chan, u8_t hci_status); + + /** Channel alloc_buf callback + * + * If this callback is provided the channel will use it to allocate + * buffers to store incoming data. + * + * @param chan The channel requesting a buffer. + * + * @return Allocated buffer. + */ + struct net_buf *(*alloc_buf)(struct bt_l2cap_chan *chan); + + /** Channel recv callback + * + * @param chan The channel receiving data. + * @param buf Buffer containing incoming data. + * + * @return 0 in case of success or negative value in case of error. + * If -EINPROGRESS is returned user has to confirm once the data has + * been processed by calling bt_l2cap_chan_recv_complete passing back + * the buffer received with its original user_data which contains the + * number of segments/credits used by the packet. + */ + int (*recv)(struct bt_l2cap_chan *chan, struct net_buf *buf); + + /* Channel sent callback + * + * If this callback is provided it will be called whenever a SDU has + * been completely sent. + * + * @param chan The channel which has sent data. + */ + void (*sent)(struct bt_l2cap_chan *chan); + + /* Channel status callback + * + * If this callback is provided it will be called whenever the + * channel status changes. + * + * @param chan The channel which status changed + * @param status The channel status + */ + void (*status)(struct bt_l2cap_chan *chan, atomic_t *status); + + #if defined(BFLB_BLE_MTU_CHANGE_CB) + void (*mtu_changed)(struct bt_l2cap_chan *chan, u16_t mtu); + #endif +}; + +/** @def BT_L2CAP_CHAN_SEND_RESERVE + * @brief Headroom needed for outgoing buffers + */ +#define BT_L2CAP_CHAN_SEND_RESERVE (BT_BUF_RESERVE + 4 + 4) + +/** @brief L2CAP Server structure. */ +struct bt_l2cap_server { + /** Server PSM. Possible values: + * + * 0 A dynamic value will be auto-allocated when + * bt_l2cap_server_register() is called. + * + * 0x0001-0x007f Standard, Bluetooth SIG-assigned fixed values. + * + * 0x0080-0x00ff Dynamically allocated. May be pre-set by the + * application before server registration (not + * recommended however), or auto-allocated by the + * stack if the app gave 0 as the value. + */ + u16_t psm; + + /** Required minimim security level */ + bt_security_t sec_level; + + /** Server accept callback + * + * This callback is called whenever a new incoming connection requires + * authorization. + * + * @param conn The connection that is requesting authorization + * @param chan Pointer to received the allocated channel + * + * @return 0 in case of success or negative value in case of error. + * Possible return values: + * -ENOMEM if no available space for new channel. + * -EACCES if application did not authorize the connection. + * -EPERM if encryption key size is too short. + */ + int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan); + + sys_snode_t node; +}; + +/** @brief Register L2CAP server. + * + * Register L2CAP server for a PSM, each new connection is authorized using + * the accept() callback which in case of success shall allocate the channel + * structure to be used by the new connection. + * + * For fixed, SIG-assigned PSMs (in the range 0x0001-0x007f) the PSM should + * be assigned to server->psm before calling this API. For dynamic PSMs + * (in the range 0x0080-0x00ff) server->psm may be pre-set to a given value + * (this is however not recommended) or be left as 0, in which case upon + * return a newly allocated value will have been assigned to it. For + * dynamically allocated values the expectation is that it's exposed through + * a GATT service, and that's how L2CAP clients discover how to connect to + * the server. + * + * @param server Server structure. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_l2cap_server_register(struct bt_l2cap_server *server); + +/** @brief Register L2CAP server on BR/EDR oriented connection. + * + * Register L2CAP server for a PSM, each new connection is authorized using + * the accept() callback which in case of success shall allocate the channel + * structure to be used by the new connection. + * + * @param server Server structure. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_l2cap_br_server_register(struct bt_l2cap_server *server); + +/** @brief Connect L2CAP channel + * + * Connect L2CAP channel by PSM, once the connection is completed channel + * connected() callback will be called. If the connection is rejected + * disconnected() callback is called instead. + * Channel object passed (over an address of it) as second parameter shouldn't + * be instantiated in application as standalone. Instead of, application should + * create transport dedicated L2CAP objects, i.e. type of bt_l2cap_le_chan for + * LE and/or type of bt_l2cap_br_chan for BR/EDR. Then pass to this API + * the location (address) of bt_l2cap_chan type object which is a member + * of both transport dedicated objects. + * + * @param conn Connection object. + * @param chan Channel object. + * @param psm Channel PSM to connect to. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, + u16_t psm); + +/** @brief Disconnect L2CAP channel + * + * Disconnect L2CAP channel, if the connection is pending it will be + * canceled and as a result the channel disconnected() callback is called. + * Regarding to input parameter, to get details see reference description + * to bt_l2cap_chan_connect() API above. + * + * @param chan Channel object. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan); + +/** @brief Send data to L2CAP channel + * + * Send data from buffer to the channel. If credits are not available, buf will + * be queued and sent as and when credits are received from peer. + * Regarding to first input parameter, to get details see reference description + * to bt_l2cap_chan_connect() API above. + * + * @return Bytes sent in case of success or negative value in case of error. + */ +int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf); + +/** @brief Complete receiving L2CAP channel data + * + * Complete the reception of incoming data. This shall only be called if the + * channel recv callback has returned -EINPROGRESS to process some incoming + * data. The buffer shall contain the original user_data as that is used for + * storing the credits/segments used by the packet. + * + * @param chan Channel object. + * @param buf Buffer containing the data. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_l2cap_chan_recv_complete(struct bt_l2cap_chan *chan, + struct net_buf *buf); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_L2CAP_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/rfcomm.h b/components/ble/ble_stack/include/bluetooth/rfcomm.h new file mode 100644 index 00000000..bce90deb --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/rfcomm.h @@ -0,0 +1,188 @@ +/** @file + * @brief Bluetooth RFCOMM handling + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_RFCOMM_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_RFCOMM_H_ + +/** + * @brief RFCOMM + * @defgroup bt_rfcomm RFCOMM + * @ingroup bluetooth + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* RFCOMM channels (1-30): pre-allocated for profiles to avoid conflicts */ +enum { + BT_RFCOMM_CHAN_HFP_HF = 1, + BT_RFCOMM_CHAN_HFP_AG, + BT_RFCOMM_CHAN_HSP_AG, + BT_RFCOMM_CHAN_HSP_HS, + BT_RFCOMM_CHAN_SPP, +}; + +struct bt_rfcomm_dlc; + +/** @brief RFCOMM DLC operations structure. */ +struct bt_rfcomm_dlc_ops { + /** DLC connected callback + * + * If this callback is provided it will be called whenever the + * connection completes. + * + * @param dlc The dlc that has been connected + */ + void (*connected)(struct bt_rfcomm_dlc *dlc); + + /** DLC disconnected callback + * + * If this callback is provided it will be called whenever the + * dlc is disconnected, including when a connection gets + * rejected or cancelled (both incoming and outgoing) + * + * @param dlc The dlc that has been Disconnected + */ + void (*disconnected)(struct bt_rfcomm_dlc *dlc); + + /** DLC recv callback + * + * @param dlc The dlc receiving data. + * @param buf Buffer containing incoming data. + */ + void (*recv)(struct bt_rfcomm_dlc *dlc, struct net_buf *buf); +}; + +/** @brief Role of RFCOMM session and dlc. Used only by internal APIs + */ +typedef enum bt_rfcomm_role { + BT_RFCOMM_ROLE_ACCEPTOR, + BT_RFCOMM_ROLE_INITIATOR +} __packed bt_rfcomm_role_t; + +/** @brief RFCOMM DLC structure. */ +struct bt_rfcomm_dlc { + /* Response Timeout eXpired (RTX) timer */ + struct k_delayed_work rtx_work; + + /* Queue for outgoing data */ + struct k_fifo tx_queue; + + /* TX credits, Reuse as a binary sem for MSC FC if CFC is not enabled */ + struct k_sem tx_credits; + + struct bt_rfcomm_session *session; + struct bt_rfcomm_dlc_ops *ops; + struct bt_rfcomm_dlc *_next; + + bt_security_t required_sec_level; + bt_rfcomm_role_t role; + + uint16_t mtu; + uint8_t dlci; + uint8_t state; + uint8_t rx_credit; + + /* Stack & kernel data for TX thread */ + struct k_thread tx_thread; + //K_KERNEL_STACK_MEMBER(stack, 256); //MBHJ +}; + +struct bt_rfcomm_server { + /** Server Channel */ + uint8_t channel; + + /** Server accept callback + * + * This callback is called whenever a new incoming connection requires + * authorization. + * + * @param conn The connection that is requesting authorization + * @param dlc Pointer to received the allocated dlc + * + * @return 0 in case of success or negative value in case of error. + */ + int (*accept)(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc); + + struct bt_rfcomm_server *_next; +}; + +/** @brief Register RFCOMM server + * + * Register RFCOMM server for a channel, each new connection is authorized + * using the accept() callback which in case of success shall allocate the dlc + * structure to be used by the new connection. + * + * @param server Server structure. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_rfcomm_server_register(struct bt_rfcomm_server *server); + +/** @brief Connect RFCOMM channel + * + * Connect RFCOMM dlc by channel, once the connection is completed dlc + * connected() callback will be called. If the connection is rejected + * disconnected() callback is called instead. + * + * @param conn Connection object. + * @param dlc Dlc object. + * @param channel Server channel to connect to. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_rfcomm_dlc_connect(struct bt_conn *conn, struct bt_rfcomm_dlc *dlc, + uint8_t channel); + +/** @brief Send data to RFCOMM + * + * Send data from buffer to the dlc. Length should be less than or equal to + * mtu. + * + * @param dlc Dlc object. + * @param buf Data buffer. + * + * @return Bytes sent in case of success or negative value in case of error. + */ +int bt_rfcomm_dlc_send(struct bt_rfcomm_dlc *dlc, struct net_buf *buf); + +/** @brief Disconnect RFCOMM dlc + * + * Disconnect RFCOMM dlc, if the connection is pending it will be + * canceled and as a result the dlc disconnected() callback is called. + * + * @param dlc Dlc object. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_rfcomm_dlc_disconnect(struct bt_rfcomm_dlc *dlc); + +/** @brief Allocate the buffer from pool after reserving head room for RFCOMM, + * L2CAP and ACL headers. + * + * @param pool Which pool to take the buffer from. + * + * @return New buffer. + */ +struct net_buf *bt_rfcomm_create_pdu(struct net_buf_pool *pool); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_RFCOMM_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/sdp.h b/components/ble/ble_stack/include/bluetooth/sdp.h new file mode 100644 index 00000000..37e42a8c --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/sdp.h @@ -0,0 +1,613 @@ +/** @file + * @brief Service Discovery Protocol handling. + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_SDP_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_SDP_H_ + +/** + * @brief Service Discovery Protocol (SDP) + * @defgroup bt_sdp Service Discovery Protocol (SDP) + * @ingroup bluetooth + * @{ + */ + +#include <../bluetooth/uuid.h> +#include <../bluetooth/conn.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All definitions are based on Bluetooth Assigned Numbers + * of the Bluetooth Specification + */ + +/* + * Service class identifiers of standard services and service groups + */ +#define BT_SDP_SDP_SERVER_SVCLASS 0x1000 +#define BT_SDP_BROWSE_GRP_DESC_SVCLASS 0x1001 +#define BT_SDP_PUBLIC_BROWSE_GROUP 0x1002 +#define BT_SDP_SERIAL_PORT_SVCLASS 0x1101 +#define BT_SDP_LAN_ACCESS_SVCLASS 0x1102 +#define BT_SDP_DIALUP_NET_SVCLASS 0x1103 +#define BT_SDP_IRMC_SYNC_SVCLASS 0x1104 +#define BT_SDP_OBEX_OBJPUSH_SVCLASS 0x1105 +#define BT_SDP_OBEX_FILETRANS_SVCLASS 0x1106 +#define BT_SDP_IRMC_SYNC_CMD_SVCLASS 0x1107 +#define BT_SDP_HEADSET_SVCLASS 0x1108 +#define BT_SDP_CORDLESS_TELEPHONY_SVCLASS 0x1109 +#define BT_SDP_AUDIO_SOURCE_SVCLASS 0x110a +#define BT_SDP_AUDIO_SINK_SVCLASS 0x110b +#define BT_SDP_AV_REMOTE_TARGET_SVCLASS 0x110c +#define BT_SDP_ADVANCED_AUDIO_SVCLASS 0x110d +#define BT_SDP_AV_REMOTE_SVCLASS 0x110e +#define BT_SDP_AV_REMOTE_CONTROLLER_SVCLASS 0x110f +#define BT_SDP_INTERCOM_SVCLASS 0x1110 +#define BT_SDP_FAX_SVCLASS 0x1111 +#define BT_SDP_HEADSET_AGW_SVCLASS 0x1112 +#define BT_SDP_WAP_SVCLASS 0x1113 +#define BT_SDP_WAP_CLIENT_SVCLASS 0x1114 +#define BT_SDP_PANU_SVCLASS 0x1115 +#define BT_SDP_NAP_SVCLASS 0x1116 +#define BT_SDP_GN_SVCLASS 0x1117 +#define BT_SDP_DIRECT_PRINTING_SVCLASS 0x1118 +#define BT_SDP_REFERENCE_PRINTING_SVCLASS 0x1119 +#define BT_SDP_IMAGING_SVCLASS 0x111a +#define BT_SDP_IMAGING_RESPONDER_SVCLASS 0x111b +#define BT_SDP_IMAGING_ARCHIVE_SVCLASS 0x111c +#define BT_SDP_IMAGING_REFOBJS_SVCLASS 0x111d +#define BT_SDP_HANDSFREE_SVCLASS 0x111e +#define BT_SDP_HANDSFREE_AGW_SVCLASS 0x111f +#define BT_SDP_DIRECT_PRT_REFOBJS_SVCLASS 0x1120 +#define BT_SDP_REFLECTED_UI_SVCLASS 0x1121 +#define BT_SDP_BASIC_PRINTING_SVCLASS 0x1122 +#define BT_SDP_PRINTING_STATUS_SVCLASS 0x1123 +#define BT_SDP_HID_SVCLASS 0x1124 +#define BT_SDP_HCR_SVCLASS 0x1125 +#define BT_SDP_HCR_PRINT_SVCLASS 0x1126 +#define BT_SDP_HCR_SCAN_SVCLASS 0x1127 +#define BT_SDP_CIP_SVCLASS 0x1128 +#define BT_SDP_VIDEO_CONF_GW_SVCLASS 0x1129 +#define BT_SDP_UDI_MT_SVCLASS 0x112a +#define BT_SDP_UDI_TA_SVCLASS 0x112b +#define BT_SDP_AV_SVCLASS 0x112c +#define BT_SDP_SAP_SVCLASS 0x112d +#define BT_SDP_PBAP_PCE_SVCLASS 0x112e +#define BT_SDP_PBAP_PSE_SVCLASS 0x112f +#define BT_SDP_PBAP_SVCLASS 0x1130 +#define BT_SDP_MAP_MSE_SVCLASS 0x1132 +#define BT_SDP_MAP_MCE_SVCLASS 0x1133 +#define BT_SDP_MAP_SVCLASS 0x1134 +#define BT_SDP_GNSS_SVCLASS 0x1135 +#define BT_SDP_GNSS_SERVER_SVCLASS 0x1136 +#define BT_SDP_MPS_SC_SVCLASS 0x113a +#define BT_SDP_MPS_SVCLASS 0x113b +#define BT_SDP_PNP_INFO_SVCLASS 0x1200 +#define BT_SDP_GENERIC_NETWORKING_SVCLASS 0x1201 +#define BT_SDP_GENERIC_FILETRANS_SVCLASS 0x1202 +#define BT_SDP_GENERIC_AUDIO_SVCLASS 0x1203 +#define BT_SDP_GENERIC_TELEPHONY_SVCLASS 0x1204 +#define BT_SDP_UPNP_SVCLASS 0x1205 +#define BT_SDP_UPNP_IP_SVCLASS 0x1206 +#define BT_SDP_UPNP_PAN_SVCLASS 0x1300 +#define BT_SDP_UPNP_LAP_SVCLASS 0x1301 +#define BT_SDP_UPNP_L2CAP_SVCLASS 0x1302 +#define BT_SDP_VIDEO_SOURCE_SVCLASS 0x1303 +#define BT_SDP_VIDEO_SINK_SVCLASS 0x1304 +#define BT_SDP_VIDEO_DISTRIBUTION_SVCLASS 0x1305 +#define BT_SDP_HDP_SVCLASS 0x1400 +#define BT_SDP_HDP_SOURCE_SVCLASS 0x1401 +#define BT_SDP_HDP_SINK_SVCLASS 0x1402 +#define BT_SDP_GENERIC_ACCESS_SVCLASS 0x1800 +#define BT_SDP_GENERIC_ATTRIB_SVCLASS 0x1801 +#define BT_SDP_APPLE_AGENT_SVCLASS 0x2112 + +/* + * Attribute identifier codes + */ +#define BT_SDP_SERVER_RECORD_HANDLE 0x0000 + +/* + * Possible values for attribute-id are listed below. + * See SDP Spec, section "Service Attribute Definitions" for more details. + */ +#define BT_SDP_ATTR_RECORD_HANDLE 0x0000 +#define BT_SDP_ATTR_SVCLASS_ID_LIST 0x0001 +#define BT_SDP_ATTR_RECORD_STATE 0x0002 +#define BT_SDP_ATTR_SERVICE_ID 0x0003 +#define BT_SDP_ATTR_PROTO_DESC_LIST 0x0004 +#define BT_SDP_ATTR_BROWSE_GRP_LIST 0x0005 +#define BT_SDP_ATTR_LANG_BASE_ATTR_ID_LIST 0x0006 +#define BT_SDP_ATTR_SVCINFO_TTL 0x0007 +#define BT_SDP_ATTR_SERVICE_AVAILABILITY 0x0008 +#define BT_SDP_ATTR_PROFILE_DESC_LIST 0x0009 +#define BT_SDP_ATTR_DOC_URL 0x000a +#define BT_SDP_ATTR_CLNT_EXEC_URL 0x000b +#define BT_SDP_ATTR_ICON_URL 0x000c +#define BT_SDP_ATTR_ADD_PROTO_DESC_LIST 0x000d + +#define BT_SDP_ATTR_GROUP_ID 0x0200 +#define BT_SDP_ATTR_IP_SUBNET 0x0200 +#define BT_SDP_ATTR_VERSION_NUM_LIST 0x0200 +#define BT_SDP_ATTR_SUPPORTED_FEATURES_LIST 0x0200 +#define BT_SDP_ATTR_GOEP_L2CAP_PSM 0x0200 +#define BT_SDP_ATTR_SVCDB_STATE 0x0201 + +#define BT_SDP_ATTR_MPSD_SCENARIOS 0x0200 +#define BT_SDP_ATTR_MPMD_SCENARIOS 0x0201 +#define BT_SDP_ATTR_MPS_DEPENDENCIES 0x0202 + +#define BT_SDP_ATTR_SERVICE_VERSION 0x0300 +#define BT_SDP_ATTR_EXTERNAL_NETWORK 0x0301 +#define BT_SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301 +#define BT_SDP_ATTR_DATA_EXCHANGE_SPEC 0x0301 +#define BT_SDP_ATTR_NETWORK 0x0301 +#define BT_SDP_ATTR_FAX_CLASS1_SUPPORT 0x0302 +#define BT_SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 +#define BT_SDP_ATTR_MCAP_SUPPORTED_PROCEDURES 0x0302 +#define BT_SDP_ATTR_FAX_CLASS20_SUPPORT 0x0303 +#define BT_SDP_ATTR_SUPPORTED_FORMATS_LIST 0x0303 +#define BT_SDP_ATTR_FAX_CLASS2_SUPPORT 0x0304 +#define BT_SDP_ATTR_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define BT_SDP_ATTR_NETWORK_ADDRESS 0x0306 +#define BT_SDP_ATTR_WAP_GATEWAY 0x0307 +#define BT_SDP_ATTR_HOMEPAGE_URL 0x0308 +#define BT_SDP_ATTR_WAP_STACK_TYPE 0x0309 +#define BT_SDP_ATTR_SECURITY_DESC 0x030a +#define BT_SDP_ATTR_NET_ACCESS_TYPE 0x030b +#define BT_SDP_ATTR_MAX_NET_ACCESSRATE 0x030c +#define BT_SDP_ATTR_IP4_SUBNET 0x030d +#define BT_SDP_ATTR_IP6_SUBNET 0x030e +#define BT_SDP_ATTR_SUPPORTED_CAPABILITIES 0x0310 +#define BT_SDP_ATTR_SUPPORTED_FEATURES 0x0311 +#define BT_SDP_ATTR_SUPPORTED_FUNCTIONS 0x0312 +#define BT_SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313 +#define BT_SDP_ATTR_SUPPORTED_REPOSITORIES 0x0314 +#define BT_SDP_ATTR_MAS_INSTANCE_ID 0x0315 +#define BT_SDP_ATTR_SUPPORTED_MESSAGE_TYPES 0x0316 +#define BT_SDP_ATTR_PBAP_SUPPORTED_FEATURES 0x0317 +#define BT_SDP_ATTR_MAP_SUPPORTED_FEATURES 0x0317 + +#define BT_SDP_ATTR_SPECIFICATION_ID 0x0200 +#define BT_SDP_ATTR_VENDOR_ID 0x0201 +#define BT_SDP_ATTR_PRODUCT_ID 0x0202 +#define BT_SDP_ATTR_VERSION 0x0203 +#define BT_SDP_ATTR_PRIMARY_RECORD 0x0204 +#define BT_SDP_ATTR_VENDOR_ID_SOURCE 0x0205 + +#define BT_SDP_ATTR_HID_DEVICE_RELEASE_NUMBER 0x0200 +#define BT_SDP_ATTR_HID_PARSER_VERSION 0x0201 +#define BT_SDP_ATTR_HID_DEVICE_SUBCLASS 0x0202 +#define BT_SDP_ATTR_HID_COUNTRY_CODE 0x0203 +#define BT_SDP_ATTR_HID_VIRTUAL_CABLE 0x0204 +#define BT_SDP_ATTR_HID_RECONNECT_INITIATE 0x0205 +#define BT_SDP_ATTR_HID_DESCRIPTOR_LIST 0x0206 +#define BT_SDP_ATTR_HID_LANG_ID_BASE_LIST 0x0207 +#define BT_SDP_ATTR_HID_SDP_DISABLE 0x0208 +#define BT_SDP_ATTR_HID_BATTERY_POWER 0x0209 +#define BT_SDP_ATTR_HID_REMOTE_WAKEUP 0x020a +#define BT_SDP_ATTR_HID_PROFILE_VERSION 0x020b +#define BT_SDP_ATTR_HID_SUPERVISION_TIMEOUT 0x020c +#define BT_SDP_ATTR_HID_NORMALLY_CONNECTABLE 0x020d +#define BT_SDP_ATTR_HID_BOOT_DEVICE 0x020e + +/* + * These identifiers are based on the SDP spec stating that + * "base attribute id of the primary (universal) language must be 0x0100" + * + * Other languages should have their own offset; e.g.: + * #define XXXLangBase yyyy + * #define AttrServiceName_XXX 0x0000+XXXLangBase + */ +#define BT_SDP_PRIMARY_LANG_BASE 0x0100 + +#define BT_SDP_ATTR_SVCNAME_PRIMARY (0x0000 + BT_SDP_PRIMARY_LANG_BASE) +#define BT_SDP_ATTR_SVCDESC_PRIMARY (0x0001 + BT_SDP_PRIMARY_LANG_BASE) +#define BT_SDP_ATTR_PROVNAME_PRIMARY (0x0002 + BT_SDP_PRIMARY_LANG_BASE) + +/* + * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec) + * These are the exact data type+size descriptor values + * that go into the PDU buffer. + * + * The datatype (leading 5bits) + size descriptor (last 3 bits) + * is 8 bits. The size descriptor is critical to extract the + * right number of bytes for the data value from the PDU. + * + * For most basic types, the datatype+size descriptor is + * straightforward. However for constructed types and strings, + * the size of the data is in the next "n" bytes following the + * 8 bits (datatype+size) descriptor. Exactly what the "n" is + * specified in the 3 bits of the data size descriptor. + * + * TextString and URLString can be of size 2^{8, 16, 32} bytes + * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32} + * The size are computed post-facto in the API and are not known apriori + */ +#define BT_SDP_DATA_NIL 0x00 +#define BT_SDP_UINT8 0x08 +#define BT_SDP_UINT16 0x09 +#define BT_SDP_UINT32 0x0a +#define BT_SDP_UINT64 0x0b +#define BT_SDP_UINT128 0x0c +#define BT_SDP_INT8 0x10 +#define BT_SDP_INT16 0x11 +#define BT_SDP_INT32 0x12 +#define BT_SDP_INT64 0x13 +#define BT_SDP_INT128 0x14 +#define BT_SDP_UUID_UNSPEC 0x18 +#define BT_SDP_UUID16 0x19 +#define BT_SDP_UUID32 0x1a +#define BT_SDP_UUID128 0x1c +#define BT_SDP_TEXT_STR_UNSPEC 0x20 +#define BT_SDP_TEXT_STR8 0x25 +#define BT_SDP_TEXT_STR16 0x26 +#define BT_SDP_TEXT_STR32 0x27 +#define BT_SDP_BOOL 0x28 +#define BT_SDP_SEQ_UNSPEC 0x30 +#define BT_SDP_SEQ8 0x35 +#define BT_SDP_SEQ16 0x36 +#define BT_SDP_SEQ32 0x37 +#define BT_SDP_ALT_UNSPEC 0x38 +#define BT_SDP_ALT8 0x3d +#define BT_SDP_ALT16 0x3e +#define BT_SDP_ALT32 0x3f +#define BT_SDP_URL_STR_UNSPEC 0x40 +#define BT_SDP_URL_STR8 0x45 +#define BT_SDP_URL_STR16 0x46 +#define BT_SDP_URL_STR32 0x47 + +#define BT_SDP_TYPE_DESC_MASK 0xf8 +#define BT_SDP_SIZE_DESC_MASK 0x07 +#define BT_SDP_SIZE_INDEX_OFFSET 5 + +/** @brief SDP Generic Data Element Value. */ +struct bt_sdp_data_elem { + uint8_t type; + uint32_t data_size; + uint32_t total_size; + const void *data; +}; + +/** @brief SDP Attribute Value. */ +struct bt_sdp_attribute { + uint16_t id; /* Attribute ID */ + struct bt_sdp_data_elem val; /* Attribute data */ +}; + +/** @brief SDP Service Record Value. */ +struct bt_sdp_record { + uint32_t handle; /* Redundant, for quick ref */ + struct bt_sdp_attribute *attrs; /* Base addr of attr array */ + size_t attr_count; /* Number of attributes */ + uint8_t index; /* Index of the record in LL */ + struct bt_sdp_record *next; +}; + +/* + * --------------------------------------------------- ------------------ + * | Service Hdl | Attr list ptr | Attr count | Next | -> | Service Hdl | ... + * --------------------------------------------------- ------------------ + */ + +/** @def BT_SDP_ARRAY_8 + * @brief Declare an array of 8-bit elements in an attribute. + */ +#define BT_SDP_ARRAY_8(...) ((uint8_t[]) {__VA_ARGS__}) + +/** @def BT_SDP_ARRAY_16 + * @brief Declare an array of 16-bit elements in an attribute. + */ +#define BT_SDP_ARRAY_16(...) ((uint16_t[]) {__VA_ARGS__}) + +/** @def BT_SDP_ARRAY_32 + * @brief Declare an array of 32-bit elements in an attribute. + */ +#define BT_SDP_ARRAY_32(...) ((uint32_t[]) {__VA_ARGS__}) + +/** @def BT_SDP_TYPE_SIZE + * @brief Declare a fixed-size data element header. + * + * @param _type Data element header containing type and size descriptors. + */ +#define BT_SDP_TYPE_SIZE(_type) .type = _type, \ + .data_size = BIT(_type & BT_SDP_SIZE_DESC_MASK), \ + .total_size = BIT(_type & BT_SDP_SIZE_DESC_MASK) + 1 + +/** @def BT_SDP_TYPE_SIZE_VAR + * @brief Declare a variable-size data element header. + * + * @param _type Data element header containing type and size descriptors. + * @param _size The actual size of the data. + */ +#define BT_SDP_TYPE_SIZE_VAR(_type, _size) .type = _type, \ + .data_size = _size, \ + .total_size = BIT((_type & BT_SDP_SIZE_DESC_MASK) - \ + BT_SDP_SIZE_INDEX_OFFSET) + _size + 1 + +/** @def BT_SDP_DATA_ELEM_LIST + * @brief Declare a list of data elements. + */ +#define BT_SDP_DATA_ELEM_LIST(...) ((struct bt_sdp_data_elem[]) {__VA_ARGS__}) + + +/** @def BT_SDP_NEW_SERVICE + * @brief SDP New Service Record Declaration Macro. + * + * Helper macro to declare a new service record. + * Default attributes: Record Handle, Record State, + * Language Base, Root Browse Group + * + */ +#define BT_SDP_NEW_SERVICE \ +{ \ + BT_SDP_ATTR_RECORD_HANDLE, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT32), BT_SDP_ARRAY_32(0) } \ +}, \ +{ \ + BT_SDP_ATTR_RECORD_STATE, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT32), BT_SDP_ARRAY_32(0) } \ +}, \ +{ \ + BT_SDP_ATTR_LANG_BASE_ATTR_ID_LIST, \ + { BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 9), \ + BT_SDP_DATA_ELEM_LIST( \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), BT_SDP_ARRAY_8('n', 'e') }, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), BT_SDP_ARRAY_16(106) }, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), \ + BT_SDP_ARRAY_16(BT_SDP_PRIMARY_LANG_BASE) } \ + ), \ + } \ +}, \ +{ \ + BT_SDP_ATTR_BROWSE_GRP_LIST, \ + { BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3), \ + BT_SDP_DATA_ELEM_LIST( \ + { BT_SDP_TYPE_SIZE(BT_SDP_UUID16), \ + BT_SDP_ARRAY_16(BT_SDP_PUBLIC_BROWSE_GROUP) }, \ + ), \ + } \ +} + + +/** @def BT_SDP_LIST + * @brief Generic SDP List Attribute Declaration Macro. + * + * Helper macro to declare a list attribute. + * + * @param _att_id List Attribute ID. + * @param _data_elem_seq Data element sequence for the list. + * @param _type_size SDP type and size descriptor. + */ +#define BT_SDP_LIST(_att_id, _type_size, _data_elem_seq) \ +{ \ + _att_id, { _type_size, _data_elem_seq } \ +} + +/** @def BT_SDP_SERVICE_ID + * @brief SDP Service ID Attribute Declaration Macro. + * + * Helper macro to declare a service ID attribute. + * + * @param _uuid Service ID 16bit UUID. + */ +#define BT_SDP_SERVICE_ID(_uuid) \ +{ \ + BT_SDP_ATTR_SERVICE_ID, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UUID16), &((struct bt_uuid_16) _uuid) } \ +} + +/** @def BT_SDP_SERVICE_NAME + * @brief SDP Name Attribute Declaration Macro. + * + * Helper macro to declare a service name attribute. + * + * @param _name Service name as a string (up to 256 chars). + */ +#define BT_SDP_SERVICE_NAME(_name) \ +{ \ + BT_SDP_ATTR_SVCNAME_PRIMARY, \ + { BT_SDP_TYPE_SIZE_VAR(BT_SDP_TEXT_STR8, (sizeof(_name)-1)), _name } \ +} + +/** @def BT_SDP_SUPPORTED_FEATURES + * @brief SDP Supported Features Attribute Declaration Macro. + * + * Helper macro to declare supported features of a profile/protocol. + * + * @param _features Feature mask as 16bit unsigned integer. + */ +#define BT_SDP_SUPPORTED_FEATURES(_features) \ +{ \ + BT_SDP_ATTR_SUPPORTED_FEATURES, \ + { BT_SDP_TYPE_SIZE(BT_SDP_UINT16), BT_SDP_ARRAY_16(_features) } \ +} + +/** @def BT_SDP_RECORD + * @brief SDP Service Declaration Macro. + * + * Helper macro to declare a service. + * + * @param _attrs List of attributes for the service record. + */ +#define BT_SDP_RECORD(_attrs) \ +{ \ + .attrs = _attrs, \ + .attr_count = ARRAY_SIZE((_attrs)), \ +} + +/* Server API */ + +/** @brief Register a Service Record. + * + * Register a Service Record. Applications can make use of + * macros such as BT_SDP_DECLARE_SERVICE, BT_SDP_LIST, + * BT_SDP_SERVICE_ID, BT_SDP_SERVICE_NAME, etc. + * A service declaration must start with BT_SDP_NEW_SERVICE. + * + * @param service Service record declared using BT_SDP_DECLARE_SERVICE. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_sdp_register_service(struct bt_sdp_record *service); + +/* Client API */ + +/** @brief Generic SDP Client Query Result data holder */ +struct bt_sdp_client_result { + /* buffer containing unparsed SDP record result for given UUID */ + struct net_buf *resp_buf; + /* flag pointing that there are more result chunks for given UUID */ + bool next_record_hint; + /* Reference to UUID object on behalf one discovery was started */ + const struct bt_uuid *uuid; +}; + +/** @brief Helper enum to be used as return value of bt_sdp_discover_func_t. + * The value informs the caller to perform further pending actions or stop them. + */ +enum { + BT_SDP_DISCOVER_UUID_STOP = 0, + BT_SDP_DISCOVER_UUID_CONTINUE, +}; + +/** @typedef bt_sdp_discover_func_t + * + * @brief Callback type reporting to user that there is a resolved result + * on remote for given UUID and the result record buffer can be used by user + * for further inspection. + * + * A function of this type is given by the user to the bt_sdp_discover_params + * object. It'll be called on each valid record discovery completion for given + * UUID. When UUID resolution gives back no records then NULL is passed + * to the user. Otherwise user can get valid record(s) and then the internal + * hint 'next record' is set to false saying the UUID resolution is complete or + * the hint can be set by caller to true meaning that next record is available + * for given UUID. + * The returned function value allows the user to control retrieving follow-up + * resolved records if any. If the user doesn't want to read more resolved + * records for given UUID since current record data fulfills its requirements + * then should return BT_SDP_DISCOVER_UUID_STOP. Otherwise returned value means + * more subcall iterations are allowable. + * + * @param conn Connection object identifying connection to queried remote. + * @param result Object pointing to logical unparsed SDP record collected on + * base of response driven by given UUID. + * + * @return BT_SDP_DISCOVER_UUID_STOP in case of no more need to read next + * record data and continue discovery for given UUID. By returning + * BT_SDP_DISCOVER_UUID_CONTINUE user allows this discovery continuation. + */ +typedef uint8_t (*bt_sdp_discover_func_t) + (struct bt_conn *conn, struct bt_sdp_client_result *result); + +/** @brief Main user structure used in SDP discovery of remote. */ +struct bt_sdp_discover_params { + sys_snode_t _node; + /** UUID (service) to be discovered on remote SDP entity */ + const struct bt_uuid *uuid; + /** Discover callback to be called on resolved SDP record */ + bt_sdp_discover_func_t func; + /** Memory buffer enabled by user for SDP query results */ + struct net_buf_pool *pool; +}; + +/** @brief Allows user to start SDP discovery session. + * + * The function performs SDP service discovery on remote server driven by user + * delivered discovery parameters. Discovery session is made as soon as + * no SDP transaction is ongoing between peers and if any then this one + * is queued to be processed at discovery completion of previous one. + * On the service discovery completion the callback function will be + * called to get feedback to user about findings. + * + * @param conn Object identifying connection to remote. + * @param params SDP discovery parameters. + * + * @return 0 in case of success or negative value in case of error. + */ + +int bt_sdp_discover(struct bt_conn *conn, + const struct bt_sdp_discover_params *params); + +/** @brief Release waiting SDP discovery request. + * + * It can cancel valid waiting SDP client request identified by SDP discovery + * parameters object. + * + * @param conn Object identifying connection to remote. + * @param params SDP discovery parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_sdp_discover_cancel(struct bt_conn *conn, + const struct bt_sdp_discover_params *params); + + +/* Helper types & functions for SDP client to get essential data from server */ + +/** @brief Protocols to be asked about specific parameters */ +enum bt_sdp_proto { + BT_SDP_PROTO_RFCOMM = 0x0003, + BT_SDP_PROTO_L2CAP = 0x0100, +}; + +/** @brief Give to user parameter value related to given stacked protocol UUID. + * + * API extracts specific parameter associated with given protocol UUID + * available in Protocol Descriptor List attribute. + * + * @param buf Original buffered raw record data. + * @param proto Known protocol to be checked like RFCOMM or L2CAP. + * @param param On success populated by found parameter value. + * + * @return 0 on success when specific parameter associated with given protocol + * value is found, or negative if error occurred during processing. + */ +int bt_sdp_get_proto_param(const struct net_buf *buf, enum bt_sdp_proto proto, + uint16_t *param); + +/** @brief Get profile version. + * + * Helper API extracting remote profile version number. To get it proper + * generic profile parameter needs to be selected usually listed in SDP + * Interoperability Requirements section for given profile specification. + * + * @param buf Original buffered raw record data. + * @param profile Profile family identifier the profile belongs. + * @param version On success populated by found version number. + * + * @return 0 on success, negative value if error occurred during processing. + */ +int bt_sdp_get_profile_version(const struct net_buf *buf, uint16_t profile, + uint16_t *version); + +/** @brief Get SupportedFeatures attribute value + * + * Allows if exposed by remote retrieve SupportedFeature attribute. + * + * @param buf Buffer holding original raw record data from remote. + * @param features On success object to be populated with SupportedFeature + * mask. + * + * @return 0 on success if feature found and valid, negative in case any error + */ +int bt_sdp_get_features(const struct net_buf *buf, uint16_t *features); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_SDP_H_ */ diff --git a/components/ble/ble_stack/include/bluetooth/uuid.h b/components/ble/ble_stack/include/bluetooth/uuid.h new file mode 100644 index 00000000..b782cadd --- /dev/null +++ b/components/ble/ble_stack/include/bluetooth/uuid.h @@ -0,0 +1,624 @@ +/** @file + * @brief Bluetooth UUID handling + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_UUID_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_UUID_H_ + +/** + * @brief UUIDs + * @defgroup bt_uuid UUIDs + * @ingroup bluetooth + * @{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth UUID types */ +enum { + BT_UUID_TYPE_16, + BT_UUID_TYPE_32, + BT_UUID_TYPE_128, +}; + +/** @brief This is a 'tentative' type and should be used as a pointer only */ +struct bt_uuid { + u8_t type; +}; + +struct bt_uuid_16 { + struct bt_uuid uuid; + u16_t val; +}; + +struct bt_uuid_32 { + struct bt_uuid uuid; + u32_t val; +}; + +struct bt_uuid_128 { + struct bt_uuid uuid; + u8_t val[16]; +}; + +#define BT_UUID_INIT_16(value) \ +{ \ + .uuid = { BT_UUID_TYPE_16 }, \ + .val = (value), \ +} + +#define BT_UUID_INIT_32(value) \ +{ \ + .uuid = { BT_UUID_TYPE_32 }, \ + .val = (value), \ +} + +#define BT_UUID_INIT_128(value...) \ +{ \ + .uuid = { BT_UUID_TYPE_128 }, \ + .val = { value }, \ +} + +#define BT_UUID_DECLARE_16(value) \ + ((struct bt_uuid *) (&(struct bt_uuid_16) BT_UUID_INIT_16(value))) +#define BT_UUID_DECLARE_32(value) \ + ((struct bt_uuid *) (&(struct bt_uuid_32) BT_UUID_INIT_32(value))) +#define BT_UUID_DECLARE_128(value...) \ + ((struct bt_uuid *) (&(struct bt_uuid_128) BT_UUID_INIT_128(value))) + +#define BT_UUID_16(__u) CONTAINER_OF(__u, struct bt_uuid_16, uuid) +#define BT_UUID_32(__u) CONTAINER_OF(__u, struct bt_uuid_32, uuid) +#define BT_UUID_128(__u) CONTAINER_OF(__u, struct bt_uuid_128, uuid) + + +/** + * @brief Encode 128 bit UUID into an array values + * + * Helper macro to initialize a 128-bit UUID value from the UUID format. + * Can be combined with BT_UUID_DECLARE_128 to declare a 128-bit UUID from + * the readable form of UUIDs. + * + * Example for how to declare the UUID `6E400001-B5A3-F393-E0A9-E50E24DCCA9E` + * + * @code + * BT_UUID_DECLARE_128( + * BT_UUID_128_ENCODE(0x6E400001, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E)) + * @endcode + * + * Just replace the hyphen by the comma and add `0x` prefixes. + * + * @param w32 First part of the UUID (32 bits) + * @param w1 Second part of the UUID (16 bits) + * @param w2 Third part of the UUID (16 bits) + * @param w3 Fourth part of the UUID (16 bits) + * @param w48 Fifth part of the UUID (48 bits) + * + * @return The comma separated values for UUID 128 initializer that + * may be used directly as an argument for + * @ref BT_UUID_INIT_128 or @ref BT_UUID_DECLARE_128 + */ +#define BT_UUID_128_ENCODE(w32, w1, w2, w3, w48) \ + (((w48) >> 0) & 0xFF), \ + (((w48) >> 8) & 0xFF), \ + (((w48) >> 16) & 0xFF), \ + (((w48) >> 24) & 0xFF), \ + (((w48) >> 32) & 0xFF), \ + (((w48) >> 40) & 0xFF), \ + (((w3) >> 0) & 0xFF), \ + (((w3) >> 8) & 0xFF), \ + (((w2) >> 0) & 0xFF), \ + (((w2) >> 8) & 0xFF), \ + (((w1) >> 0) & 0xFF), \ + (((w1) >> 8) & 0xFF), \ + (((w32) >> 0) & 0xFF), \ + (((w32) >> 8) & 0xFF), \ + (((w32) >> 16) & 0xFF), \ + (((w32) >> 24) & 0xFF) + + + +#define BT_UUID_SCPS BT_UUID_DECLARE_16(0x1813) + + +/** @def BT_UUID_GAP + * @brief Generic Access + */ +#define BT_UUID_GAP BT_UUID_DECLARE_16(0x1800) +/** @def BT_UUID_GATT + * @brief Generic Attribute + */ +#define BT_UUID_GATT BT_UUID_DECLARE_16(0x1801) +/** @def BT_UUID_CTS + * @brief Current Time Service + */ +#define BT_UUID_CTS BT_UUID_DECLARE_16(0x1805) +/** @def BT_UUID_HTS + * @brief Health Thermometer Service + */ +#define BT_UUID_HTS BT_UUID_DECLARE_16(0x1809) +/** @def BT_UUID_DIS + * @brief Device Information Service + */ +#define BT_UUID_DIS BT_UUID_DECLARE_16(0x180a) +/** @def BT_UUID_HRS + * @brief Heart Rate Service + */ +#define BT_UUID_HRS BT_UUID_DECLARE_16(0x180d) +/** @def BT_UUID_BAS + * @brief Battery Service + */ +#define BT_UUID_BAS BT_UUID_DECLARE_16(0x180f) +/** @def BT_UUID_HIDS + * @brief HID Service + */ +#define BT_UUID_HIDS BT_UUID_DECLARE_16(0x1812) +/** @def BT_UUID_CSC + * @brief Cycling Speed and Cadence Service + */ +#define BT_UUID_CSC BT_UUID_DECLARE_16(0x1816) +/** @def BT_UUID_ESS + * @brief Environmental Sensing Service + */ +#define BT_UUID_ESS BT_UUID_DECLARE_16(0x181a) +/** @def BT_UUID_IPSS + * @brief IP Support Service + */ +#define BT_UUID_IPSS BT_UUID_DECLARE_16(0x1820) +/** @def BT_UUID_MESH_PROV + * @brief Mesh Provisioning Service + */ +#define BT_UUID_MESH_PROV BT_UUID_DECLARE_16(0x1827) +/** @def BT_UUID_MESH_PROXY + * @brief Mesh Proxy Service + */ +#define BT_UUID_MESH_PROXY BT_UUID_DECLARE_16(0x1828) +/** @def BT_UUID_GATT_PRIMARY + * @brief GATT Primary Service + */ +#define BT_UUID_GATT_PRIMARY BT_UUID_DECLARE_16(0x2800) +/** @def BT_UUID_GATT_SECONDARY + * @brief GATT Secondary Service + */ +#define BT_UUID_GATT_SECONDARY BT_UUID_DECLARE_16(0x2801) +/** @def BT_UUID_GATT_INCLUDE + * @brief GATT Include Service + */ +#define BT_UUID_GATT_INCLUDE BT_UUID_DECLARE_16(0x2802) +/** @def BT_UUID_GATT_CHRC + * @brief GATT Characteristic + */ +#define BT_UUID_GATT_CHRC BT_UUID_DECLARE_16(0x2803) +/** @def BT_UUID_GATT_CEP + * @brief GATT Characteristic Extended Properties + */ +#define BT_UUID_GATT_CEP BT_UUID_DECLARE_16(0x2900) +/** @def BT_UUID_GATT_CUD + * @brief GATT Characteristic User Description + */ +#define BT_UUID_GATT_CUD BT_UUID_DECLARE_16(0x2901) +/** @def BT_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +#define BT_UUID_GATT_CCC BT_UUID_DECLARE_16(0x2902) +/** @def BT_UUID_GATT_SCC + * @brief GATT Server Characteristic Configuration + */ +#define BT_UUID_GATT_SCC BT_UUID_DECLARE_16(0x2903) +/** @def BT_UUID_GATT_CPF + * @brief GATT Characteristic Presentation Format + */ +#define BT_UUID_GATT_CPF BT_UUID_DECLARE_16(0x2904) +/** @def BT_UUID_VALID_RANGE + * @brief Valid Range Descriptor + */ +#define BT_UUID_VALID_RANGE BT_UUID_DECLARE_16(0x2906) +/** @def BT_UUID_HIDS_EXT_REPORT + * @brief HID External Report Descriptor + */ +#define BT_UUID_HIDS_EXT_REPORT BT_UUID_DECLARE_16(0x2907) +/** @def BT_UUID_HIDS_REPORT_REF + * @brief HID Report Reference Descriptor + */ +#define BT_UUID_HIDS_REPORT_REF BT_UUID_DECLARE_16(0x2908) +/** @def BT_UUID_ES_CONFIGURATION + * @brief Environmental Sensing Configuration Descriptor + */ +#define BT_UUID_ES_CONFIGURATION BT_UUID_DECLARE_16(0x290b) +/** @def BT_UUID_ES_MEASUREMENT + * @brief Environmental Sensing Measurement Descriptor + */ +#define BT_UUID_ES_MEASUREMENT BT_UUID_DECLARE_16(0x290c) +/** @def BT_UUID_ES_TRIGGER_SETTING + * @brief Environmental Sensing Trigger Setting Descriptor + */ +#define BT_UUID_ES_TRIGGER_SETTING BT_UUID_DECLARE_16(0x290d) +/** @def BT_UUID_GAP_DEVICE_NAME + * @brief GAP Characteristic Device Name + */ +#define BT_UUID_GAP_DEVICE_NAME BT_UUID_DECLARE_16(0x2a00) +/** @def BT_UUID_GAP_APPEARANCE + * @brief GAP Characteristic Appearance + */ +#define BT_UUID_GAP_APPEARANCE BT_UUID_DECLARE_16(0x2a01) +/** @def BT_UUID_GAP_PPCP + * @brief GAP Characteristic Peripheral Preferred Connection Parameters + */ +#define BT_UUID_GAP_PPCP BT_UUID_DECLARE_16(0x2a04) +/** @def BT_UUID_GATT_SC + * @brief GATT Characteristic Service Changed + */ +#define BT_UUID_GATT_SC BT_UUID_DECLARE_16(0x2a05) +/** @def BT_UUID_BAS_BATTERY_LEVEL + * @brief BAS Characteristic Battery Level + */ +#define BT_UUID_BAS_BATTERY_LEVEL BT_UUID_DECLARE_16(0x2a19) +/** @def BT_UUID_HTS_MEASUREMENT + * @brief HTS Characteristic Measurement Value + */ +#define BT_UUID_HTS_MEASUREMENT BT_UUID_DECLARE_16(0x2a1c) +/** @def BT_UUID_HIDS_BOOT_KB_IN_REPORT + * @brief HID Characteristic Boot Keyboard Input Report + */ +#define BT_UUID_HIDS_BOOT_KB_IN_REPORT BT_UUID_DECLARE_16(0x2a22) +/** @def BT_UUID_DIS_SYSTEM_ID + * @brief DIS Characteristic System ID + */ +#define BT_UUID_DIS_SYSTEM_ID BT_UUID_DECLARE_16(0x2a23) +/** @def BT_UUID_DIS_MODEL_NUMBER + * @brief DIS Characteristic Model Number String + */ +#define BT_UUID_DIS_MODEL_NUMBER BT_UUID_DECLARE_16(0x2a24) +/** @def BT_UUID_DIS_SERIAL_NUMBER + * @brief DIS Characteristic Serial Number String + */ +#define BT_UUID_DIS_SERIAL_NUMBER BT_UUID_DECLARE_16(0x2a25) +/** @def BT_UUID_DIS_FIRMWARE_REVISION + * @brief DIS Characteristic Firmware Revision String + */ +#define BT_UUID_DIS_FIRMWARE_REVISION BT_UUID_DECLARE_16(0x2a26) +/** @def BT_UUID_DIS_HARDWARE_REVISION + * @brief DIS Characteristic Hardware Revision String + */ +#define BT_UUID_DIS_HARDWARE_REVISION BT_UUID_DECLARE_16(0x2a27) +/** @def BT_UUID_DIS_SOFTWARE_REVISION + * @brief DIS Characteristic Software Revision String + */ +#define BT_UUID_DIS_SOFTWARE_REVISION BT_UUID_DECLARE_16(0x2a28) +/** @def BT_UUID_DIS_MANUFACTURER_NAME + * @brief DIS Characteristic Manufacturer Name String + */ +#define BT_UUID_DIS_MANUFACTURER_NAME BT_UUID_DECLARE_16(0x2a29) +/** @def BT_UUID_DIS_PNP_ID + * @brief DIS Characteristic PnP ID + */ + +#define BT_UUID_SCPS_SCAN_INTVL_WIN BT_UUID_DECLARE_16(0x2a4f) + +#define BT_UUID_DIS_PNP_ID BT_UUID_DECLARE_16(0x2a50) +/** @def BT_UUID_CTS_CURRENT_TIME + * @brief CTS Characteristic Current Time + */ +#define BT_UUID_CTS_CURRENT_TIME BT_UUID_DECLARE_16(0x2a2b) +/** @def BT_UUID_MAGN_DECLINATION + * @brief Magnetic Declination Characteristic + */ +#define BT_UUID_MAGN_DECLINATION BT_UUID_DECLARE_16(0x2a2c) +/** @def BT_UUID_HIDS_BOOT_KB_OUT_REPORT + * @brief HID Boot Keyboard Output Report Characteristic + */ +#define BT_UUID_HIDS_BOOT_KB_OUT_REPORT BT_UUID_DECLARE_16(0x2a32) +/** @def BT_UUID_HIDS_BOOT_MOUSE_IN_REPORT + * @brief HID Boot Mouse Input Report Characteristic + */ +#define BT_UUID_HIDS_BOOT_MOUSE_IN_REPORT BT_UUID_DECLARE_16(0x2a33) +/** @def BT_UUID_HRS_MEASUREMENT + * @brief HRS Characteristic Measurement Interval + */ +#define BT_UUID_HRS_MEASUREMENT BT_UUID_DECLARE_16(0x2a37) +/** @def BT_UUID_HRS_BODY_SENSOR + * @brief HRS Characteristic Body Sensor Location + */ +#define BT_UUID_HRS_BODY_SENSOR BT_UUID_DECLARE_16(0x2a38) +/** @def BT_UUID_HRS_CONTROL_POINT + * @brief HRS Characteristic Control Point + */ +#define BT_UUID_HRS_CONTROL_POINT BT_UUID_DECLARE_16(0x2a39) +/** @def BT_UUID_HIDS_INFO + * @brief HID Information Characteristic + */ +#define BT_UUID_HIDS_INFO BT_UUID_DECLARE_16(0x2a4a) +/** @def BT_UUID_HIDS_REPORT_MAP + * @brief HID Report Map Characteristic + */ +#define BT_UUID_HIDS_REPORT_MAP BT_UUID_DECLARE_16(0x2a4b) +/** @def BT_UUID_HIDS_CTRL_POINT + * @brief HID Control Point Characteristic + */ +#define BT_UUID_HIDS_CTRL_POINT BT_UUID_DECLARE_16(0x2a4c) +/** @def BT_UUID_HIDS_REPORT + * @brief HID Report Characteristic + */ +#define BT_UUID_HIDS_REPORT BT_UUID_DECLARE_16(0x2a4d) +/** @def BT_UUID_HIDS_PROTOCOL_MODE + * @brief HID Protocol Mode Characteristic + */ +#define BT_UUID_HIDS_PROTOCOL_MODE BT_UUID_DECLARE_16(0x2a4e) +/** @def BT_UUID_CSC_MEASUREMENT + * @brief CSC Measurement Characteristic + */ +#define BT_UUID_CSC_MEASUREMENT BT_UUID_DECLARE_16(0x2a5b) +/** @def BT_UUID_CSC_FEATURE + * @brief CSC Feature Characteristic + */ +#define BT_UUID_CSC_FEATURE BT_UUID_DECLARE_16(0x2a5c) +/** @def BT_UUID_SENSOR_LOCATION + * @brief Sensor Location Characteristic + */ +#define BT_UUID_SENSOR_LOCATION BT_UUID_DECLARE_16(0x2a5d) +/** @def BT_UUID_SC_CONTROL_POINT + * @brief SC Control Point Characteristic + */ +#define BT_UUID_SC_CONTROL_POINT BT_UUID_DECLARE_16(0x2a55) +/** @def BT_UUID_ELEVATION + * @brief Elevation Characteristic + */ +#define BT_UUID_ELEVATION BT_UUID_DECLARE_16(0x2a6c) +/** @def BT_UUID_PRESSURE + * @brief Pressure Characteristic + */ +#define BT_UUID_PRESSURE BT_UUID_DECLARE_16(0x2a6d) +/** @def BT_UUID_TEMPERATURE + * @brief Temperature Characteristic + */ +#define BT_UUID_TEMPERATURE BT_UUID_DECLARE_16(0x2a6e) +/** @def BT_UUID_HUMIDITY + * @brief Humidity Characteristic + */ +#define BT_UUID_HUMIDITY BT_UUID_DECLARE_16(0x2a6f) +/** @def BT_UUID_TRUE_WIND_SPEED + * @brief True Wind Speed Characteristic + */ +#define BT_UUID_TRUE_WIND_SPEED BT_UUID_DECLARE_16(0x2a70) +/** @def BT_UUID_TRUE_WIND_DIR + * @brief True Wind Direction Characteristic + */ +#define BT_UUID_TRUE_WIND_DIR BT_UUID_DECLARE_16(0x2a71) +/** @def BT_UUID_APPARENT_WIND_SPEED + * @brief Apparent Wind Speed Characteristic + */ +#define BT_UUID_APPARENT_WIND_SPEED BT_UUID_DECLARE_16(0x2a72) +/** @def BT_UUID_APPARENT_WIND_DIR + * @brief Apparent Wind Direction Characteristic + */ +#define BT_UUID_APPARENT_WIND_DIR BT_UUID_DECLARE_16(0x2a73) +/** @def BT_UUID_GUST_FACTOR + * @brief Gust Factor Characteristic + */ +#define BT_UUID_GUST_FACTOR BT_UUID_DECLARE_16(0x2a74) +/** @def BT_UUID_POLLEN_CONCENTRATION + * @brief Pollen Concentration Characteristic + */ +#define BT_UUID_POLLEN_CONCENTRATION BT_UUID_DECLARE_16(0x2a75) +/** @def BT_UUID_UV_INDEX + * @brief UV Index Characteristic + */ +#define BT_UUID_UV_INDEX BT_UUID_DECLARE_16(0x2a76) +/** @def BT_UUID_IRRADIANCE + * @brief Irradiance Characteristic + */ +#define BT_UUID_IRRADIANCE BT_UUID_DECLARE_16(0x2a77) +/** @def BT_UUID_RAINFALL + * @brief Rainfall Characteristic + */ +#define BT_UUID_RAINFALL BT_UUID_DECLARE_16(0x2a78) +/** @def BT_UUID_WIND_CHILL + * @brief Wind Chill Characteristic + */ +#define BT_UUID_WIND_CHILL BT_UUID_DECLARE_16(0x2a79) +/** @def BT_UUID_HEAT_INDEX + * @brief Heat Index Characteristic + */ +#define BT_UUID_HEAT_INDEX BT_UUID_DECLARE_16(0x2a7a) +/** @def BT_UUID_DEW_POINT + * @brief Dew Point Characteristic + */ +#define BT_UUID_DEW_POINT BT_UUID_DECLARE_16(0x2a7b) +/** @def BT_UUID_DESC_VALUE_CHANGED + * @brief Descriptor Value Changed Characteristic + */ +#define BT_UUID_DESC_VALUE_CHANGED BT_UUID_DECLARE_16(0x2a7d) +/** @def BT_UUID_MAGN_FLUX_DENSITY_2D + * @brief Magnetic Flux Density - 2D Characteristic + */ +#define BT_UUID_MAGN_FLUX_DENSITY_2D BT_UUID_DECLARE_16(0x2aa0) +/** @def BT_UUID_MAGN_FLUX_DENSITY_3D + * @brief Magnetic Flux Density - 3D Characteristic + */ +#define BT_UUID_MAGN_FLUX_DENSITY_3D BT_UUID_DECLARE_16(0x2aa1) +/** @def BT_UUID_BAR_PRESSURE_TREND + * @brief Barometric Pressure Trend Characteristic + */ +#define BT_UUID_BAR_PRESSURE_TREND BT_UUID_DECLARE_16(0x2aa3) +/** @def BT_UUID_CENTRAL_ADDR_RES + * @brief Central Address Resolution Characteristic + */ +#define BT_UUID_CENTRAL_ADDR_RES BT_UUID_DECLARE_16(0x2aa6) +/** @def BT_UUID_MESH_PROV_DATA_IN + * @brief Mesh Provisioning Data In + */ +#define BT_UUID_MESH_PROV_DATA_IN BT_UUID_DECLARE_16(0x2adb) +/** @def BT_UUID_MESH_PROV_DATA_OUT + * @brief Mesh Provisioning Data Out + */ +#define BT_UUID_MESH_PROV_DATA_OUT BT_UUID_DECLARE_16(0x2adc) +/** @def BT_UUID_MESH_PROXY_DATA_IN + * @brief Mesh Proxy Data In + */ +#define BT_UUID_MESH_PROXY_DATA_IN BT_UUID_DECLARE_16(0x2add) +/** @def BT_UUID_MESH_PROXY_DATA_OUT + * @brief Mesh Proxy Data Out + */ +#define BT_UUID_MESH_PROXY_DATA_OUT BT_UUID_DECLARE_16(0x2ade) +/** @def BT_UUID_GATT_CLIENT_FEATURES + * @brief Client Supported Features + */ +#define BT_UUID_GATT_CLIENT_FEATURES BT_UUID_DECLARE_16(0x2b29) +/** @def BT_UUID_GATT_DB_HASH + * @brief Database Hash + */ +#define BT_UUID_GATT_DB_HASH BT_UUID_DECLARE_16(0x2b2a) + +#if defined(CONFIG_BT_STACK_PTS) && defined(PTS_GAP_SLAVER_CONFIG_READ_CHARC) +#define BT_UUID_PTS BT_UUID_DECLARE_16(0x2b2b) +#define BT_UUID_PTS_CHAR_READ_AUTHEN BT_UUID_DECLARE_16(0x2b2c) +#define BT_UUID_PTS_CHAR_READ_NOPERM BT_UUID_DECLARE_16(0x2b2d) +#define BT_UUID_PTS_CHAR_READ_LONGVAL BT_UUID_DECLARE_16(0x2b2e) +#define BT_UUID_PTS_CHAR_READ_L_NOPERM BT_UUID_DECLARE_16(0x2b2f) +#define BT_UUID_PTS_CHAR_READ_LVAL_REF BT_UUID_DECLARE_16(0x2b30) +#endif + +#if defined(CONFIG_BT_STACK_PTS)&& defined(PTS_GAP_SLAVER_CONFIG_WRITE_CHARC) +#define BT_UUID_PTS_CHAR_WRITE_VALUE BT_UUID_DECLARE_16(0x2b31) +#define BT_UUID_PTS_CHAR_WRITE_AUTHEN BT_UUID_DECLARE_16(0x2b32) +#define BT_UUID_PTS_CHAR_WRITE_LONGVAL BT_UUID_DECLARE_16(0x2b33) +#define BT_UUID_PTS_CHAR_WRITE_NORSP BT_UUID_DECLARE_16(0x2b34) +#define BT_UUID_PTS_CHAR_WRITE_2LONGVAL BT_UUID_DECLARE_16(0x2b35) +#define BT_UUID_PTS_CHAR_WRITE_L_DES BT_UUID_DECLARE_16(0x2b36) +#endif + +#if defined(CONFIG_BT_STACK_PTS)&& defined(PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC) +#define BT_UUID_PTS_CHAR_NOTIFY_CHAR BT_UUID_DECLARE_16(0x2b37) +#endif + +#if defined(CONFIG_BT_STACK_PTS)&& defined(PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC) +#define BT_UUID_PTS_CHAR_INDICATE_CHAR BT_UUID_DECLARE_16(0x2b38) +#endif + +#if defined(CONFIG_BT_STACK_PTS) && defined(PTS_TEST_CASE_INSUFFICIENT_KEY) +#define BT_UUID_PTS_ENC_KEY BT_UUID_DECLARE_16(0x2b3a) +#endif +#if defined(CONFIG_BT_STACK_PTS) && defined(PTS_CHARC_LEN_EQUAL_MTU_SIZE) +#define BT_UUID_PTS_READ_MTU_SIZE_CHAR BT_UUID_DECLARE_16(0x2b3b) +#endif + +#if defined(CONFIG_BT_STACK_PTS) +#define BT_UUID_PTS_AUTH_CHAR BT_UUID_DECLARE_16(0x2b39) +#endif + +/* + * Protocol UUIDs + */ +#define BT_UUID_SDP BT_UUID_DECLARE_16(0x0001) +#define BT_UUID_UDP BT_UUID_DECLARE_16(0x0002) +#define BT_UUID_RFCOMM BT_UUID_DECLARE_16(0x0003) +#define BT_UUID_TCP BT_UUID_DECLARE_16(0x0004) +#define BT_UUID_TCS_BIN BT_UUID_DECLARE_16(0x0005) +#define BT_UUID_TCS_AT BT_UUID_DECLARE_16(0x0006) +#define BT_UUID_ATT BT_UUID_DECLARE_16(0x0007) +#define BT_UUID_OBEX BT_UUID_DECLARE_16(0x0008) +#define BT_UUID_IP BT_UUID_DECLARE_16(0x0009) +#define BT_UUID_FTP BT_UUID_DECLARE_16(0x000a) +#define BT_UUID_HTTP BT_UUID_DECLARE_16(0x000c) +#define BT_UUID_BNEP BT_UUID_DECLARE_16(0x000f) +#define BT_UUID_UPNP BT_UUID_DECLARE_16(0x0010) +#define BT_UUID_HIDP BT_UUID_DECLARE_16(0x0011) +#define BT_UUID_HCRP_CTRL BT_UUID_DECLARE_16(0x0012) +#define BT_UUID_HCRP_DATA BT_UUID_DECLARE_16(0x0014) +#define BT_UUID_HCRP_NOTE BT_UUID_DECLARE_16(0x0016) +#define BT_UUID_AVCTP BT_UUID_DECLARE_16(0x0017) +#define BT_UUID_AVDTP BT_UUID_DECLARE_16(0x0019) +#define BT_UUID_CMTP BT_UUID_DECLARE_16(0x001b) +#define BT_UUID_UDI BT_UUID_DECLARE_16(0x001d) +#define BT_UUID_MCAP_CTRL BT_UUID_DECLARE_16(0x001e) +#define BT_UUID_MCAP_DATA BT_UUID_DECLARE_16(0x001f) +#define BT_UUID_L2CAP BT_UUID_DECLARE_16(0x0100) + + + + +/** @brief Compare Bluetooth UUIDs. + * + * Compares 2 Bluetooth UUIDs, if the types are different both UUIDs are + * first converted to 128 bits format before comparing. + * + * @param u1 First Bluetooth UUID to compare + * @param u2 Second Bluetooth UUID to compare + * + * @return negative value if @a u1 < @a u2, 0 if @a u1 == @a u2, else positive + */ +int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2); + +/** @brief Create a bt_uuid from a little-endian data buffer. + * + * Create a bt_uuid from a little-endian data buffer. The data_len parameter + * is used to determine whether the UUID is in 16, 32 or 128 bit format + * (length 2, 4 or 16). Note: 32 bit format is not allowed over the air. + * + * @param uuid Pointer to the bt_uuid variable + * @param data pointer to UUID stored in little-endian data buffer + * @param data_len length of the UUID in the data buffer + * + * @return true if the data was valid and the UUID was successfully created. + */ +bool bt_uuid_create(struct bt_uuid *uuid, const u8_t *data, u8_t data_len); + +#if defined(CONFIG_BT_DEBUG) +/** @brief Convert Bluetooth UUID to string. + * + * Converts Bluetooth UUID to string. UUID has to be in 16 bits or 128 bits + * format. + * + * @param uuid Bluetooth UUID + * @param str pointer where to put converted string + * @param len length of str + * + * @return N/A + */ +void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len); + +const char *bt_uuid_str_real(const struct bt_uuid *uuid); + +/** @def bt_uuid_str + * @brief Convert Bluetooth UUID to string in place. + * + * Converts Bluetooth UUID to string in place. UUID has to be in 16 bits or + * 128 bits format. + * + * @param uuid Bluetooth UUID + * + * @return String representation of the UUID given + */ +#define bt_uuid_str(_uuid) log_strdup(bt_uuid_str_real(_uuid)) +#else +static inline void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, + size_t len) +{ + if (len > 0) { + str[0] = '\0'; + } +} + +static inline const char *bt_uuid_str(const struct bt_uuid *uuid) +{ + return ""; +} +#endif /* CONFIG_BT_DEBUG */ + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_UUID_H_ */ diff --git a/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h b/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h new file mode 100644 index 00000000..04839996 --- /dev/null +++ b/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h @@ -0,0 +1,208 @@ +/** @file + * @brief Bluetooth HCI driver API. + */ + +/* + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DRIVERS_BLUETOOTH_HCI_DRIVER_H_ +#define ZEPHYR_INCLUDE_DRIVERS_BLUETOOTH_HCI_DRIVER_H_ + +/** + * @brief HCI drivers + * @defgroup bt_hci_driver HCI drivers + * @ingroup bluetooth + * @{ + */ + +#include +#include +#include <../../bluetooth/buf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + /* The host should never send HCI_Reset */ + BT_QUIRK_NO_RESET = BIT(0), +}; + +/** + * @brief Check if an HCI event is high priority or not. + * + * Helper for the HCI driver to know which events are ok to be passed + * through the RX thread and which must be given to bt_recv_prio() from + * another context (e.g. ISR). If this function returns true it's safe + * to pass the event through the RX thread, however if it returns false + * then this risks a deadlock. + * + * @param evt HCI event code. + * + * @return true if the event can be processed in the RX thread, false + * if it cannot. + */ +static inline bool bt_hci_evt_is_prio(u8_t evt) +{ + switch (evt) { + case BT_HCI_EVT_CMD_COMPLETE: + case BT_HCI_EVT_CMD_STATUS: + /* fallthrough */ +#if defined(CONFIG_BT_CONN) + case BT_HCI_EVT_NUM_COMPLETED_PACKETS: + case BT_HCI_EVT_DATA_BUF_OVERFLOW: +#endif + return true; + default: + return false; + } +} + +/** + * @brief Receive data from the controller/HCI driver. + * + * This is the main function through which the HCI driver provides the + * host with data from the controller. The buffer needs to have its type + * set with the help of bt_buf_set_type() before calling this API. This API + * should not be used for so-called high priority HCI events, which should + * instead be delivered to the host stack through bt_recv_prio(). + * + * @param buf Network buffer containing data from the controller. + * + * @return 0 on success or negative error number on failure. + */ +int bt_recv(struct net_buf *buf); + +/** + * @brief Receive high priority data from the controller/HCI driver. + * + * This is the same as bt_recv(), except that it should be used for + * so-called high priority HCI events. There's a separate + * bt_hci_evt_is_prio() helper that can be used to identify which events + * are high priority. + * + * As with bt_recv(), the buffer needs to have its type set with the help of + * bt_buf_set_type() before calling this API. The only exception is so called + * high priority HCI events which should be delivered to the host stack through + * bt_recv_prio() instead. + * + * @param buf Network buffer containing data from the controller. + * + * @return 0 on success or negative error number on failure. + */ +int bt_recv_prio(struct net_buf *buf); + +/** Possible values for the 'bus' member of the bt_hci_driver struct */ +enum bt_hci_driver_bus { + BT_HCI_DRIVER_BUS_VIRTUAL = 0, + BT_HCI_DRIVER_BUS_USB = 1, + BT_HCI_DRIVER_BUS_PCCARD = 2, + BT_HCI_DRIVER_BUS_UART = 3, + BT_HCI_DRIVER_BUS_RS232 = 4, + BT_HCI_DRIVER_BUS_PCI = 5, + BT_HCI_DRIVER_BUS_SDIO = 6, + BT_HCI_DRIVER_BUS_SPI = 7, + BT_HCI_DRIVER_BUS_I2C = 8, + BT_HCI_DRIVER_BUS_IPM = 9, +}; + +/** + * @brief Abstraction which represents the HCI transport to the controller. + * + * This struct is used to represent the HCI transport to the Bluetooth + * controller. + */ +struct bt_hci_driver { + /** Name of the driver */ + const char *name; + + /** Bus of the transport (BT_HCI_DRIVER_BUS_*) */ + enum bt_hci_driver_bus bus; + + /** Specific controller quirks. These are set by the HCI driver + * and acted upon by the host. They can either be statically + * set at buildtime, or set at runtime before the HCI driver's + * open() callback returns. + */ + u32_t quirks; + + /** + * @brief Open the HCI transport. + * + * Opens the HCI transport for operation. This function must not + * return until the transport is ready for operation, meaning it + * is safe to start calling the send() handler. + * + * If the driver uses its own RX thread, i.e. + * CONFIG_BT_RECV_IS_RX_THREAD is set, then this + * function is expected to start that thread. + * + * @return 0 on success or negative error number on failure. + */ + int (*open)(void); + + /** + * @brief Send HCI buffer to controller. + * + * Send an HCI command or ACL data to the controller. The exact + * type of the data can be checked with the help of bt_buf_get_type(). + * + * @note This function must only be called from a cooperative thread. + * + * @param buf Buffer containing data to be sent to the controller. + * + * @return 0 on success or negative error number on failure. + */ + int (*send)(struct net_buf *buf); +}; + +/** + * @brief Register a new HCI driver to the Bluetooth stack. + * + * This needs to be called before any application code runs. The bt_enable() + * API will fail if there is no driver registered. + * + * @param drv A bt_hci_driver struct representing the driver. + * + * @return 0 on success or negative error number on failure. + */ +int bt_hci_driver_register(const struct bt_hci_driver *drv); + +#if !defined(BFLB_BLE) /*Don't use*/ +/** + * @brief Setup the HCI transport, which usually means to reset the + * Bluetooth IC. + * + * @note A weak version of this function is included in the H4 driver, so + * defining it is optional per board. + * + * @param dev The device structure for the bus connecting to the IC + * + * @return 0 on success, negative error value on failure + */ +int bt_hci_transport_setup(struct device *dev); +#endif + +#if defined(BFLB_BLE) +/** + * @brief enqueue buffer to hci received queue. + * @param buf Buffer containing data received from firmware. + */ +void hci_driver_enque_recvq(struct net_buf *buf); + +int hci_driver_init(void); + +#if (BFLB_BLE_CO_THREAD) +void co_tx_thread(); +#endif + +#endif //#if (BFLB_BLE) + +#ifdef __cplusplus +} +#endif + + +#endif /* __BT_HCI_DRIVER_H */ diff --git a/components/ble/ble_stack/port/bl_port.c b/components/ble/ble_stack/port/bl_port.c new file mode 100644 index 00000000..82c28fbf --- /dev/null +++ b/components/ble/ble_stack/port/bl_port.c @@ -0,0 +1,381 @@ +#include +#include +#include + +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_CORE) + +#include +#include +#include +#include +#include "atomic.h" + +#include "errno.h" +#include +#include +#include +#include + +#if defined(BL_MCU_SDK) +#define TRNG_LOOP_COUNTER (17) +extern BL_Err_Type Sec_Eng_Trng_Get_Random(uint8_t *data,uint32_t len); +extern BL_Err_Type Sec_Eng_Trng_Enable(void); +int bl_rand(); +#else +extern int bl_rand(); +#endif + + +#if defined(BL_MCU_SDK) +int bl_rand() +{ + unsigned int val; + int counter = 0; + int32_t ret = 0; + Sec_Eng_Trng_Enable(); + do { + ret = Sec_Eng_Trng_Get_Random((uint8_t*)&val,4); + if(ret < -1){ + return -1; + } + if ((counter++) > TRNG_LOOP_COUNTER) { + break; + } + } while (0 == val); + val >>= 1;//leave signe bit alone + return val; +} +#endif + +void k_queue_init(struct k_queue *queue, int size) +{ + //int size = 20; + uint8_t blk_size = sizeof(void *) + 1; + + + queue->hdl = xQueueCreate(size, blk_size); + BT_ASSERT(queue->hdl != NULL); + + sys_dlist_init(&queue->poll_events); +} + +void k_queue_insert(struct k_queue *queue, void *prev, void *data) +{ + BaseType_t ret; + (void) ret; + + ret = xQueueSend(queue->hdl, &data, portMAX_DELAY); + BT_ASSERT(ret == pdPASS); +} + +void k_queue_append(struct k_queue *queue, void *data) +{ + k_queue_insert(queue, NULL, data); +} + +void k_queue_insert_from_isr(struct k_queue *queue, void *prev, void *data) +{ + BaseType_t xHigherPriorityTaskWoken; + + xQueueSendFromISR(queue->hdl, &data, &xHigherPriorityTaskWoken); + if(xHigherPriorityTaskWoken == pdTRUE) + { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} + +void k_queue_append_from_isr(struct k_queue *queue, void *data) +{ + k_queue_insert_from_isr(queue, NULL, data); +} + +void k_queue_free(struct k_queue *queue) +{ + if(NULL == queue || NULL == queue->hdl) + { + BT_ERR("Queue is NULL\n"); + return; + } + + vQueueDelete(queue->hdl); + queue->hdl = NULL; + return; +} + +void k_queue_prepend(struct k_queue *queue, void *data) +{ + k_queue_insert(queue, NULL, data); +} + +void k_queue_append_list(struct k_queue *queue, void *head, void *tail) +{ + struct net_buf *buf_tail = (struct net_buf *)head; + + for (buf_tail = (struct net_buf *)head; buf_tail; buf_tail = buf_tail->frags) { + k_queue_append(queue, buf_tail); + } +} + +void *k_queue_get(struct k_queue *queue, s32_t timeout) +{ + void *msg = NULL; + unsigned int t = timeout; + BaseType_t ret; + + (void) ret; + + if (timeout == K_FOREVER) { + t = BL_WAIT_FOREVER; + } else if (timeout == K_NO_WAIT) { + t = BL_NO_WAIT; + } + + ret = xQueueReceive(queue->hdl, &msg, t == BL_WAIT_FOREVER ? portMAX_DELAY : ms2tick(t)); + if (ret == pdPASS) { + return msg; + } else { + return NULL; + } +} + +int k_queue_is_empty(struct k_queue *queue) +{ + return uxQueueMessagesWaiting(queue->hdl)? 0:1; +} + +int k_queue_get_cnt(struct k_queue *queue) +{ + return uxQueueMessagesWaiting(queue->hdl); +} + +int k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit) +{ + if (NULL == sem) { + BT_ERR("sem is NULL\n"); + return -EINVAL; + } + + sem->sem.hdl = xSemaphoreCreateCounting(limit, initial_count); + sys_dlist_init(&sem->poll_events); + return 0; +} + +int k_sem_take(struct k_sem *sem, uint32_t timeout) +{ + BaseType_t ret; + unsigned int t = timeout; + + (void) ret; + if (timeout == K_FOREVER) { + t = BL_WAIT_FOREVER; + } else if (timeout == K_NO_WAIT) { + t = BL_NO_WAIT; + } + + if(NULL == sem){ + return -1; + } + + ret = xSemaphoreTake(sem->sem.hdl, t == BL_WAIT_FOREVER ? portMAX_DELAY : ms2tick(t)); + return ret == pdPASS ? 0 : -1; +} + +int k_sem_give(struct k_sem *sem) +{ + BaseType_t ret; + (void) ret; + + if (NULL == sem) { + BT_ERR("sem is NULL\n"); + return -EINVAL; + } + + ret = xSemaphoreGive(sem->sem.hdl); + return ret == pdPASS ? 0 : -1; +} + +int k_sem_delete(struct k_sem *sem) +{ + if (NULL == sem || NULL == sem->sem.hdl) { + BT_ERR("sem is NULL\n"); + return -EINVAL; + } + + vSemaphoreDelete(sem->sem.hdl); + sem->sem.hdl = NULL; + return 0; +} + +unsigned int k_sem_count_get(struct k_sem *sem) +{ + return uxQueueMessagesWaiting(sem->sem.hdl); +} + +void k_mutex_init(struct k_mutex *mutex) +{ + + if (NULL == mutex) { + BT_ERR("mutex is NULL\n"); + return ; + } + + mutex->mutex.hdl = xSemaphoreCreateMutex(); + BT_ASSERT(mutex->mutex.hdl != NULL); + sys_dlist_init(&mutex->poll_events); +} + +int64_t k_uptime_get() +{ + return k_now_ms(); +} + +u32_t k_uptime_get_32(void) +{ + return (u32_t)k_now_ms(); +} + +int k_thread_create(struct k_thread *new_thread, const char *name, + size_t stack_size, k_thread_entry_t entry, + int prio) +{ + stack_size /= sizeof(StackType_t); + xTaskCreate(entry, name, stack_size, NULL, prio, (void *)(&new_thread->task)); + + return new_thread->task? 0 : -1; +} + +void k_thread_delete(struct k_thread *new_thread) +{ + if(NULL == new_thread || 0 == new_thread->task) + { + BT_ERR("task is NULL\n"); + return; + } + + vTaskDelete((void *)(new_thread->task)); + new_thread->task = 0; + return; +} + +int k_yield(void) +{ + taskYIELD(); + return 0; +} + +void k_sleep(s32_t dur_ms) +{ + TickType_t ticks; + ticks = pdMS_TO_TICKS(dur_ms); + vTaskDelay(ticks); +} + +unsigned int irq_lock(void) +{ + taskENTER_CRITICAL(); + return 1; +} + +void irq_unlock(unsigned int key) +{ + taskEXIT_CRITICAL(); +} + +int k_is_in_isr(void) +{ + #if defined(ARCH_RISCV) + return (xPortIsInsideInterrupt()); + #else + /* IRQs + PendSV (14) + SYSTICK (15) are interrupts. */ + return (__get_IPSR() > 13); + #endif + + return 0; +} + +void k_timer_init(k_timer_t *timer, k_timer_handler_t handle, void *args) +{ + BT_ASSERT(timer != NULL); + timer->handler = handle; + timer->args = args; + /* Set args as timer id */ + timer->timer.hdl = xTimerCreate("Timer", pdMS_TO_TICKS(1000), 0, args, (TimerCallbackFunction_t)(timer->handler)); + BT_ASSERT(timer->timer.hdl != NULL); +} + +void* k_timer_get_id(void* hdl) +{ + return pvTimerGetTimerID((TimerHandle_t)hdl); +} + +void k_timer_start(k_timer_t *timer, uint32_t timeout) +{ + BaseType_t ret; + (void) ret; + + BT_ASSERT(timer != NULL); + timer->timeout = timeout; + timer->start_ms = k_now_ms(); + + ret = xTimerChangePeriod(timer->timer.hdl, pdMS_TO_TICKS(timeout), 0); + BT_ASSERT(ret == pdPASS); + ret = xTimerStart(timer->timer.hdl, 0); + BT_ASSERT(ret == pdPASS); +} + +void k_timer_reset(k_timer_t *timer) +{ + BaseType_t ret; + + (void) ret; + BT_ASSERT(timer != NULL); + + ret = xTimerReset(timer->timer.hdl, 0); + BT_ASSERT(ret == pdPASS); +} + +void k_timer_stop(k_timer_t *timer) +{ + BaseType_t ret; + + (void) ret; + BT_ASSERT(timer != NULL); + + ret = xTimerStop(timer->timer.hdl, 0); + BT_ASSERT(ret == pdPASS); +} + +void k_timer_delete(k_timer_t *timer) +{ + BaseType_t ret; + (void) ret; + + BT_ASSERT(timer != NULL); + + ret = xTimerDelete(timer->timer.hdl, 0); + BT_ASSERT(ret == pdPASS); +} + +long long k_now_ms(void) +{ + return (long long)(xTaskGetTickCount() * 1000)/configTICK_RATE_HZ; +} + +void k_get_random_byte_array(uint8_t *buf, size_t len) +{ + // bl_rand() return a word, but *buf may not be word-aligned + for(int i = 0; i < len; i++) + { + *(buf + i) = (uint8_t)(bl_rand() & 0xFF); + } +} + +void *k_malloc(size_t size) +{ + return pvPortMalloc(size); +} + +void k_free(void *buf) +{ + return vPortFree(buf); +} diff --git a/components/ble/ble_stack/port/include/bl_port.h b/components/ble/ble_stack/port/include/bl_port.h new file mode 100644 index 00000000..f4ec7577 --- /dev/null +++ b/components/ble/ble_stack/port/include/bl_port.h @@ -0,0 +1,275 @@ +#ifndef BL_PORT_H +#define BL_PORT_H +#if defined(BL_MCU_SDK) +#include "misc.h" +#endif +#include "config.h" +#include +#include +#include +#include +#include +#include "types.h" +#include "bl_port.h" + +#define BT_UINT_MAX 0xffffffff +#define BL_WAIT_FOREVER 0xffffffffu +#define BL_NO_WAIT 0x0 +#define ms2tick pdMS_TO_TICKS + +typedef struct { + void *hdl; +} bl_hdl_t; + +typedef bl_hdl_t _queue_t; +typedef bl_hdl_t _sem_t; +typedef uint32_t _stack_element_t; +typedef bl_hdl_t _mutex_t; +typedef bl_hdl_t bl_timer_t; +typedef uint32_t _task_t; + + +#define _POLL_EVENT_OBJ_INIT(obj) \ + .poll_events = SYS_DLIST_STATIC_INIT(&obj.poll_events), +#define _POLL_EVENT sys_dlist_t poll_events + + +#define _K_SEM_INITIALIZER(obj, initial_count, count_limit) { } + +#define K_SEM_INITIALIZER DEPRECATED_MACRO _K_SEM_INITIALIZER + +#define K_SEM_DEFINE(name, initial_count, count_limit) \ + struct k_sem name \ + __in_section(_k_sem, static, name) = \ + _K_SEM_INITIALIZER(name, initial_count, count_limit) + +#define K_MUTEX_DEFINE(name) \ + struct k_mutex name \ + __in_section(_k_mutex, static, name) = \ + _K_MUTEX_INITIALIZER(name) + +typedef sys_dlist_t _wait_q_t; + +struct k_queue { + void *hdl; + sys_dlist_t poll_events; +}; + +/*attention: this is intialied as zero,the queue variable shoule use k_queue_init\k_lifo_init\k_fifo_init again*/ +#define _K_QUEUE_INITIALIZER(obj) { 0 } +#define K_QUEUE_INITIALIZER DEPRECATED_MACRO _K_QUEUE_INITIALIZER + +void k_queue_init(struct k_queue *queue, int size); +void k_queue_free(struct k_queue *queue); +void k_queue_append(struct k_queue *queue, void *data); +void k_queue_prepend(struct k_queue *queue, void *data); +void k_queue_insert(struct k_queue *queue, void *prev, void *data); +void k_queue_append_list(struct k_queue *queue, void *head, void *tail); +void *k_queue_get(struct k_queue *queue, s32_t timeout); +int k_queue_is_empty(struct k_queue *queue); +int k_queue_get_cnt(struct k_queue *queue); + +struct k_lifo { + struct k_queue _queue; +}; + +#define _K_LIFO_INITIALIZER(obj) \ + { \ + ._queue = _K_QUEUE_INITIALIZER(obj._queue) \ + } + +#define K_LIFO_INITIALIZER DEPRECATED_MACRO _K_LIFO_INITIALIZER + +#define k_lifo_init(lifo, size) \ + k_queue_init((struct k_queue *) lifo, size) + +#define k_lifo_put(lifo, data) \ + k_queue_prepend((struct k_queue *) lifo, data) + +#define k_lifo_get(lifo, timeout) \ + k_queue_get((struct k_queue *) lifo, timeout) + +#define K_LIFO_DEFINE(name) \ + struct k_lifo name \ + __in_section(_k_queue, static, name) = \ + _K_LIFO_INITIALIZER(name) + +struct k_fifo { + struct k_queue _queue; +}; + +#define _K_FIFO_INITIALIZER(obj) \ + { \ + ._queue = _K_QUEUE_INITIALIZER(obj._queue) \ + } +#define K_FIFO_INITIALIZER DEPRECATED_MACRO _K_FIFO_INITIALIZER + +#define k_fifo_init(fifo, size) \ + k_queue_init((struct k_queue *) fifo, size) + +#define k_fifo_put(fifo, data) \ + k_queue_append((struct k_queue *) fifo, data) + +#define k_fifo_put_from_isr(fifo, data) \ + k_queue_append_from_isr((struct k_queue *) fifo, data) + +#define k_fifo_put_list(fifo, head, tail) \ + k_queue_append_list((struct k_queue *) fifo, head, tail) + +#define k_fifo_get(fifo, timeout) \ + k_queue_get((struct k_queue *) fifo, timeout) + +#define K_FIFO_DEFINE(name) \ + struct k_fifo name \ + __in_section(_k_queue, static, name) = \ + _K_FIFO_INITIALIZER(name) + +/* sem define*/ +struct k_sem { + _sem_t sem; + sys_dlist_t poll_events; +}; + +/** + * @brief Initialize a semaphore. + */ +int k_sem_init(struct k_sem *sem, unsigned int initial_count, unsigned int limit); + +/** + * @brief Take a semaphore. + */ +int k_sem_take(struct k_sem *sem, uint32_t timeout); + +/** + * @brief Give a semaphore. + */ +int k_sem_give(struct k_sem *sem); + +/** + * @brief Delete a semaphore. + */ +int k_sem_delete(struct k_sem *sem); + +/** + * @brief Get a semaphore's count. + */ +unsigned int k_sem_count_get(struct k_sem *sem); + +struct k_mutex { + _mutex_t mutex; + sys_dlist_t poll_events; +}; + +typedef void (*k_timer_handler_t)(void *timer); + +typedef struct k_timer { + bl_timer_t timer; + k_timer_handler_t handler; + void *args; + uint32_t timeout; + uint32_t start_ms; +} k_timer_t; + +/** + * @brief Initialize a timer. + */ +void k_timer_init(k_timer_t *timer, k_timer_handler_t handle, void *args); + +void* k_timer_get_id(void* hdl); + +/** + * @brief Start a timer. + * + */ +void k_timer_start(k_timer_t *timer, uint32_t timeout); + +void k_timer_reset(k_timer_t *timer); + +/** + * @brief Stop a timer. + */ +void k_timer_stop(k_timer_t *timer); + +/** + * @brief Delete a timer. + * + */ +void k_timer_delete(k_timer_t *timer); + +/*time define*/ +#define MSEC_PER_SEC 1000 +#define K_MSEC(ms) (ms) +#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC) +#define K_MINUTES(m) K_SECONDS((m) * 60) +#define K_HOURS(h) K_MINUTES((h) * 60) + +#define K_PRIO_COOP(x) x + +/** + * @brief Get time now. + * + * @return time(in milliseconds) + */ +int64_t k_uptime_get(); +u32_t k_uptime_get_32(void); + +struct k_thread { + _task_t task; +}; + +typedef _stack_element_t k_thread_stack_t; + +inline void k_call_stacks_analyze(void) { } + +#define K_THREAD_STACK_DEFINE(sym, size) _stack_element_t sym[size] +#define K_THREAD_STACK_SIZEOF(sym) sizeof(sym) + +static inline char *K_THREAD_STACK_BUFFER(k_thread_stack_t *sym) +{ + return (char *)sym; +} + +typedef void (*k_thread_entry_t)(void *args); + +int k_thread_create(struct k_thread *new_thread, const char *name, + size_t stack_size, k_thread_entry_t entry, + int prio); + +void k_thread_delete(struct k_thread *new_thread); + +/** + * @brief Yield the current thread. + */ +int k_yield(); + +/** + * @brief suspend the current thread for a certain time + */ + +void k_sleep(s32_t duration); + +/** + * @brief Lock interrupts. + */ +unsigned int irq_lock(); + +/** + * @brief Unlock interrupts. + */ +void irq_unlock(unsigned int key); + +int k_is_in_isr(void); + +#ifdef BIT +#undef BIT +#define BIT(n) (1UL << (n)) +#else +#define BIT(n) (1UL << (n)) +#endif + +long long k_now_ms(void); +void k_get_random_byte_array(uint8_t *buf, size_t len); +void *k_malloc(size_t size); +void k_free(void *buf); +#endif /* BL_PORT_H */ + diff --git a/components/ble/ble_stack/port/include/config.h b/components/ble/ble_stack/port/include/config.h new file mode 100644 index 00000000..8ee65ccf --- /dev/null +++ b/components/ble/ble_stack/port/include/config.h @@ -0,0 +1,615 @@ +#ifndef BLE_CONFIG_H +#define BLE_CONFIG_H + +#include "FreeRTOSConfig.h" + +/** + * CONFIG_BLUETOOTH: Enable the bluetooh stack + */ +//#ifndef CONFIG_BLUETOOTH +//#error "CONFIG_BLUETOOTH not defined,this header shoudn't include" +//#endif + +#ifdef CONFIG_BT_BONDABLE +#undef CONFIG_BT_BONDABLE +#define CONFIG_BT_BONDABLE 1 +#endif + +#define CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE 1 + +#if defined(CONFIG_BT_STACK_PTS) + +#ifndef PTS_CHARC_LEN_EQUAL_MTU_SIZE +#define PTS_CHARC_LEN_EQUAL_MTU_SIZE +#endif + + +//#ifndef CONFIG_BT_STACK_PTS_SM_SLA_KDU_BI_01 +//#define CONFIG_BT_STACK_PTS_SM_SLA_KDU_BI_01 +//#endif + +//#ifndef PTS_GAP_SLAVER_CONFIG_READ_CHARC +//#define PTS_GAP_SLAVER_CONFIG_READ_CHARC +//#endif + +//#ifndef PTS_GAP_SLAVER_CONFIG_WRITE_CHARC +//#define PTS_GAP_SLAVER_CONFIG_WRITE_CHARC +//#endif + +//#ifndef PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC +//#define PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC +//#endif + +//#ifndef PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC +//#define PTS_GAP_SLAVER_CONFIG_INDICATE_CHARC +//#endif +#define CONFIG_BT_GATT_READ_MULTIPLE 1 +#endif + +/** + * CONFIG_BT_HCI_RX_STACK_SIZE: rx thread stack size + */ +#ifndef CONFIG_BT_HCI_RX_STACK_SIZE +#define CONFIG_BT_HCI_RX_STACK_SIZE 512 +#endif + +#ifndef CONFIG_BT_RX_STACK_SIZE +#if defined(CONFIG_BT_MESH) +#define CONFIG_BT_RX_STACK_SIZE 3072//2048//1536//1024 +#else +#define CONFIG_BT_RX_STACK_SIZE 2048//1536//1024 +#endif +#endif + +#ifndef CONFIG_BT_CTLR_RX_PRIO_STACK_SIZE +#define CONFIG_BT_CTLR_RX_PRIO_STACK_SIZE 156 +#endif + +#define CONFIG_BT_HCI_ECC_STACK_SIZE 384 + +#ifndef CONFIG_BT_RX_PRIO +#define CONFIG_BT_RX_PRIO (configMAX_PRIORITIES - 4) +#endif +/** + * CONFIG_BT: Tx thread stack size + */ + +#ifndef CONFIG_BT_HCI_TX_STACK_SIZE +#define CONFIG_BT_HCI_TX_STACK_SIZE 1536//1024//200 +#endif + +/** + * CONFIG_BT_HCI_TX_PRIO: tx thread priority + */ +#ifndef CONFIG_BT_HCI_TX_PRIO +#define CONFIG_BT_HCI_TX_PRIO (configMAX_PRIORITIES - 3) +#endif + +#ifndef CONFIG_BT_CTLR_RX_PRIO +#define CONFIG_BT_CTLR_RX_PRIO (configMAX_PRIORITIES - 4) +#endif + + +/** + * BL_BLE_CO_THREAD: combine tx rx thread + */ + #ifndef BFLB_BLE_CO_THREAD + #define BFLB_BLE_CO_THREAD 0 +#endif + +/** +* CONFIG_BT_HCI_CMD_COUNT: hci cmd buffer count,range 2 to 64 +*/ +#ifndef CONFIG_BT_HCI_CMD_COUNT +#define CONFIG_BT_HCI_CMD_COUNT 2 +#endif + +/** +* CONFIG_BT_RX_BUF_COUNT: number of buffer for incoming ACL packages or HCI +* events,range 2 to 255 +*/ +#ifndef CONFIG_BT_RX_BUF_COUNT + +#if defined(CONFIG_BT_MESH) +#define CONFIG_BT_RX_BUF_COUNT 10 +#else +#define CONFIG_BT_RX_BUF_COUNT 5 +#endif //CONFIG_BT_MESH +#endif + +/** +* CONFIG_BT_RX_BUF_RSV_COUNT: number of buffer that HCI_LE_EVENT reserved +* events,range 1 to CONFIG_BT_RX_BUF_COUNT +*/ +#define CONFIG_BT_RX_BUF_RSV_COUNT (1) +#if (CONFIG_BT_RX_BUF_RSV_COUNT >= CONFIG_BT_RX_BUF_COUNT) +#error "CONFIG_BT_RX_BUF_RSV_COUNT config error" +#endif + +/** +* CONFIG_BT_RX_BUF_LEN: the max length for rx buffer +* range 73 to 2000 +*/ +#ifndef CONFIG_BT_RX_BUF_LEN +#if defined(CONFIG_BT_BREDR) +#define CONFIG_BT_RX_BUF_LEN 680 //CONFIG_BT_L2CAP_RX_MTU + 4 + 4 +#else +#define CONFIG_BT_RX_BUF_LEN 255 //108 //76 +#endif +#endif + +/** +* CONFIG_BT_CENTRAL: Enable central Role +*/ +#ifdef CONFIG_BT_CENTRAL +#undef CONFIG_BT_CENTRAL +#define CONFIG_BT_CENTRAL 1 +#endif + +/** +* CONFIG_BT_WHITELIST : Enable autoconnect whilt list device */ +#ifndef CONFIG_BT_WHITELIST +#define CONFIG_BT_WHITELIST 1 +#endif + +/** +* CONFIG_BT_PERIPHERAL: Enable peripheral Role +*/ +#ifdef CONFIG_BT_PERIPHERAL +#undef CONFIG_BT_PERIPHERAL +#define CONFIG_BT_PERIPHERAL 1 +#endif + +#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_PERIPHERAL) +#undef CONFIG_BT_CONN +#define CONFIG_BT_CONN 1 +#endif + +#ifdef CONFIG_BT_CONN + +#ifndef CONFIG_BT_CREATE_CONN_TIMEOUT +#define CONFIG_BT_CREATE_CONN_TIMEOUT 3 +#endif + +#if defined(BFLB_BLE) +#ifndef CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT +#define CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT 5 +#endif +#endif +/** +* CONFIG_BLUETOOTH_L2CAP_TX_BUF_COUNT: number of buffer for outgoing L2CAP packages +* range 2 to 255 +*/ +#ifndef CONFIG_BT_L2CAP_TX_BUF_COUNT +#define CONFIG_BT_L2CAP_TX_BUF_COUNT CFG_BLE_TX_BUFF_DATA +#endif + +/** +* CONFIG_BT_L2CAP_TX_MTU: Max L2CAP MTU for L2CAP tx buffer +* range 65 to 2000 if SMP enabled,otherwise range 23 to 2000 +*/ +#ifndef CONFIG_BT_L2CAP_TX_MTU +#ifdef CONFIG_BT_SMP +#define CONFIG_BT_L2CAP_TX_MTU 247 //96 //65 +#else +#define CONFIG_BT_L2CAP_TX_MTU 247 //23 +#endif +#endif + +/** +* CONFIG_BT_L2CAP_TX_USER_DATA_SIZE: the max length for L2CAP tx buffer user data size +* range 4 to 65535 +*/ +#ifndef CONFIG_BT_L2CAP_TX_USER_DATA_SIZE +#define CONFIG_BT_L2CAP_TX_USER_DATA_SIZE 4 +#endif + +#if defined(CONFIG_BT_STACK_PTS)&& (defined(PTS_GAP_SLAVER_CONFIG_WRITE_CHARC) || defined(PTS_TEST_CASE_INSUFFICIENT_KEY)) +#define CONFIG_BT_ATT_PREPARE_COUNT 64 +#else +/** +* CONFIG_BT_ATT_PREPARE_COUNT: Number of buffers available for ATT prepare write, setting +* this to 0 disables GATT long/reliable writes. +* range 0 to 64 +*/ +#ifndef CONFIG_BT_ATT_PREPARE_COUNT +#define CONFIG_BT_ATT_PREPARE_COUNT 0 +#endif +#endif + +/** +* CONFIG_BLUETOOTH_SMP:Eable the Security Manager Protocol +* (SMP), making it possible to pair devices over LE +*/ +#ifdef CONFIG_BT_SMP +#undef CONFIG_BT_SMP +#define CONFIG_BT_SMP 1 + +/** +* CONFIG_BT_SIGNING:enables data signing which is used for transferring +* authenticated data in an unencrypted connection +*/ +#ifdef CONFIG_BT_SIGNING +#undef CONFIG_BT_SIGNING +#define CONFIG_BT_SIGNING 1 +#endif + +/** +* CONFIG_BT_SMP_SC_ONLY:enables support for Secure Connection Only Mode. In this +* mode device shall only use Security Mode 1 Level 4 with exception +* for services that only require Security Mode 1 Level 1 (no security). +* Security Mode 1 Level 4 stands for authenticated LE Secure Connections +* pairing with encryption. Enabling this option disables legacy pairing +*/ +#ifdef CONFIG_BT_SMP_SC_ONLY +#undef CONFIG_BT_SMP_SC_ONLY +#define CONFIG_BT_SMP_SC_ONLY 1 +#endif + +/** +* CONFIG_BT_USE_DEBUG_KEYS:This option places Security Manager in +* a Debug Mode. In this mode predefined +* Diffie-Hellman private/public key pair is used as described +* in Core Specification Vol. 3, Part H, 2.3.5.6.1. This option should +* only be enabled for debugging and should never be used in production. +* If this option is enabled anyone is able to decipher encrypted air +* traffic. +*/ +#ifdef CONFIG_BT_USE_DEBUG_KEYS +#ifndef CONFIG_BT_TINYCRYPT_ECC +#error "CONFIG_BT_USE_DEBUG_KEYS depends on CONFIG_BT_TINYCRYPT_ECC" +#endif +#undef CONFIG_BT_USE_DEBUG_KEYS +#define CONFIG_BT_USE_DEBUG_KEYS 1 +#endif + +/** +* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL:enables support for LE Connection +* oriented Channels,allowing the creation of dynamic L2CAP Channels +*/ +#ifdef CONFIG_BT_L2CAP_DYNAMIC_CHANNEL +#undef CONFIG_BT_L2CAP_DYNAMIC_CHANNEL +#define CONFIG_BT_L2CAP_DYNAMIC_CHANNEL 1 +#endif + +#endif + +/** +* CONFIG_BT_PRIVACY:Enable local Privacy Feature support. This makes it possible +* to use Resolvable Private Addresses (RPAs). +*/ +#ifdef CONFIG_BT_PRIVACY +#ifndef CONFIG_BT_SMP +#error "CONFIG_BT_PRIVACY depends on CONFIG_BT_SMP" +#endif +#undef CONFIG_BT_PRIVACY +#define CONFIG_BT_PRIVACY 1 + +/** +* CONFIG_BT_RPA_TIMEOUT:Resolvable Private Address timeout +* range 1 to 65535,seconds +*/ +#ifndef CONFIG_BT_RPA_TIMEOUT +#define CONFIG_BT_RPA_TIMEOUT 900 +#endif +#endif + +/** +* CONFIG_BT_GATT_DYNAMIC_DB:enables GATT services to be added dynamically to database +*/ +#ifdef CONFIG_BT_GATT_DYNAMIC_DB +#undef CONFIG_BT_GATT_DYNAMIC_DB +#define CONFIG_BT_GATT_DYNAMIC_DB 1 +#endif + +/** +* CONFIG_BT_GATT_CLIENT:GATT client role support +*/ +#ifdef CONFIG_BT_GATT_CLIENT +#undef CONFIG_BT_GATT_CLIENT +#define CONFIG_BT_GATT_CLIENT 1 +#endif + +/** +* CONFIG_BT_MAX_PAIRED:Maximum number of paired devices +* range 1 to 128 +*/ +#ifndef CONFIG_BT_MAX_PAIRED +#define CONFIG_BT_MAX_PAIRED CONFIG_BT_MAX_CONN +#endif +#endif + +/** +* If this option is set TinyCrypt library is used for emulating the +* ECDH HCI commands and events needed by e.g. LE Secure Connections. +* In builds including the BLE Host, if not set the controller crypto is +* used for ECDH and if the controller doesn't support the required HCI +* commands the LE Secure Connections support will be disabled. +* In builds including the HCI Raw interface and the BLE Controller, this +* option injects support for the 2 HCI commands required for LE Secure +* Connections so that Hosts can make use of those +*/ +#ifdef CONFIG_BT_TINYCRYPT_ECC +#undef CONFIG_BT_TINYCRYPT_ECC +#define CONFIG_BT_TINYCRYPT_ECC 1 +#endif +/** +* CONFIG_BLUETOOTH_MAX_CONN:Maximum number of connections +* range 1 to 128 +*/ +#ifndef CONFIG_BT_MAX_CONN +#define CONFIG_BT_MAX_CONN CFG_CON +#endif + +/** +* CONFIG_BT_DEVICE_NAME:Bluetooth device name. Name can be up +* to 248 bytes long (excluding NULL termination). Can be empty string +*/ +#ifndef CONFIG_BT_DEVICE_NAME +#if defined(CONFIG_AUTO_PTS) +#define CONFIG_BT_DEVICE_NAME "AUTO_PTS_TEST0123456789012345" +#else +#if defined(BL602) +#define CONFIG_BT_DEVICE_NAME "BL602-BLE-DEV" +#elif defined(BL702) +#define CONFIG_BT_DEVICE_NAME "BL702-BLE-DEV" +#else +#define CONFIG_BT_DEVICE_NAME "BL606P-BTBLE" +#endif +#endif +#endif +/** +* CONFIG_BT_CONTROLLER_NAME:Bluetooth controller name. +*/ +#ifndef CONFIG_BT_CONTROLLER_NAME +#if defined(BL602) +#define CONFIG_BT_CONTROLLER_NAME "BL602-BLE-DEV" +#else +#define CONFIG_BT_CONTROLLER_NAME "BL702-BLE-DEV" +#endif +#endif + +/** +* CONFIG_BT_MAX_SCO_CONN:Maximum number of simultaneous SCO connections. +*/ +#ifndef CONFIG_BT_MAX_SCO_CONN +#define CONFIG_BT_MAX_SCO_CONN CONFIG_MAX_SCO +#endif + +/** +* CONFIG_BT_WORK_QUEUE_STACK_SIZE:Work queue stack size. +*/ +#ifndef CONFIG_BT_WORK_QUEUE_STACK_SIZE +#ifndef CONFIG_BT_MESH +#define CONFIG_BT_WORK_QUEUE_STACK_SIZE 1536//1280//512 +#else +#define CONFIG_BT_WORK_QUEUE_STACK_SIZE 2048 +#endif /* CONFIG_BT_MESH */ +#endif + +/** +* CONFIG_BT_WORK_QUEUE_PRIO:Work queue priority. +*/ +#ifndef CONFIG_BT_WORK_QUEUE_PRIO +#define CONFIG_BT_WORK_QUEUE_PRIO (configMAX_PRIORITIES - 2) +#endif + +/** +* CONFIG_BT_HCI_RESERVE:Headroom that the driver needs for sending and receiving buffers. +*/ +#ifndef CONFIG_BT_HCI_RESERVE +#ifdef CONFIG_BLUETOOTH_H4 +#define CONFIG_BT_HCI_RESERVE 0 +#elif defined(CONFIG_BLUETOOTH_H5) || defined(CONFIG_BLUETOOTH_SPI) +#define CONFIG_BT_HCI_RESERVE 1 +#else +#define CONFIG_BT_HCI_RESERVE 1 +#endif +#endif + +/** +* CONFIG_BLUETOOTH_DEBUG_LOG:Enable bluetooth debug log. +*/ +#if defined(BFLB_BLE) +#if defined(CFG_BLE_STACK_DBG_PRINT) +#undef CONFIG_BT_DEBUG +#define CONFIG_BT_DEBUG 1 +#endif +#else +#ifdef CONFIG_BT_DEBUG_LOG +#undef CONFIG_BT_DEBUG_LOG +#define CONFIG_BT_DEBUG_LOG 1 +#undef CONFIG_BT_DEBUG +#define CONFIG_BT_DEBUG 1 +#endif +#endif +/** +* CONFIG_BT_DEBUG_L2CAP:Enable bluetooth l2cap debug log. +*/ +#ifdef CONFIG_BT_DEBUG_L2CAP +#undef CONFIG_BT_DEBUG_L2CAP +#define CONFIG_BT_DEBUG_L2CAP 1 +#endif + +/** +* CONFIG_BT_DEBUG_CONN:Enable bluetooth conn debug log. +*/ +#ifdef CONFIG_BT_DEBUG_CONN +#undef CONFIG_BT_DEBUG_CONN +#define CONFIG_BT_DEBUG_CONN 1 +#endif + +/** +* CONFIG_BT_DEBUG_ATT:Enable bluetooth att debug log. +*/ +#ifdef CONFIG_BT_DEBUG_ATT +#undef CONFIG_BT_DEBUG_ATT +#define CONFIG_BT_DEBUG_ATT 1 +#endif + +/** +* CONFIG_BT_DEBUG_GATT:Enable bluetooth gatt debug log. +*/ +#ifdef CONFIG_BT_DEBUG_GATT +#undef CONFIG_BT_DEBUG_GATT +#define CONFIG_BT_DEBUG_GATT 1 +#endif + +/** +* CONFIG_BT_DEBUG_HCI_CORE:Enable bluetooth hci core debug log. +*/ +#ifdef CONFIG_BT_DEBUG_HCI_CORE +#undef CONFIG_BT_DEBUG_HCI_CORE +#define CONFIG_BT_DEBUG_HCI_CORE 1 +#endif + +/** +* CONFIG_BT_DEBUG_HCI_DRIVER:Enable bluetooth hci driver debug log. +*/ +#ifdef CONFIG_BT_DEBUG_HCI_DRIVER +#undef CONFIG_BT_DEBUG_HCI_DRIVER +#define CONFIG_BT_DEBUG_HCI_DRIVER 1 +#endif + +/** +* CONFIG_BT_TEST:Enable bluetooth test. +*/ +#ifdef CONFIG_BT_TEST +#undef CONFIG_BT_TEST +#define CONFIG_BT_TEST 1 +#endif + +/** +* CONFIG_BT_DEBUG_CORE:Enable bluetooth core debug log. +*/ +#ifdef CONFIG_BT_DEBUG_CORE +#undef CONFIG_BT_DEBUG_CORE +#define CONFIG_BT_DEBUG_CORE 1 +#endif + +#ifndef CONFIG_BT_ATT_TX_MAX +/* +* Take throuthput test into consideration, set att tx max the same with lowstack tx buffer count. +* att semaphore determine the max numble packets can send to lowsatck at once. +*/ +#define CONFIG_BT_ATT_TX_MAX 10 +#endif + +#ifndef CONFIG_BT_CONN_TX_MAX +/* +* Take throuthput test into consideration, set upperstack conn tx max the same with lowstack tx buffer count. +*/ +#define CONFIG_BT_CONN_TX_MAX 10 +#endif + +#ifndef CONFIG_BT_DEVICE_APPEARANCE +#define CONFIG_BT_DEVICE_APPEARANCE 833 +#endif + +#if defined(BFLB_BLE) +#ifndef CONFIG_BT_RECV_IS_RX_THREAD +#define CONFIG_BT_RECV_IS_RX_THREAD +#endif + +#ifndef CONFIG_NET_BUF_USER_DATA_SIZE +#define CONFIG_NET_BUF_USER_DATA_SIZE 10 +#endif + +#ifndef CONFIG_BT_ID_MAX +#define CONFIG_BT_ID_MAX 1 +#endif + +//#define PTS_GAP_SLAVER_CONFIG_NOTIFY_CHARC 1 + +#ifndef CONFIG_BT_L2CAP_TX_FRAG_COUNT +#define CONFIG_BT_L2CAP_TX_FRAG_COUNT 0 +#endif + +#ifndef CONFIG_BT_DEVICE_NAME_DYNAMIC +#define CONFIG_BT_DEVICE_NAME_DYNAMIC 1 +#endif + +// max lenght of ADV payload is 37 bytes (by BT core spec) +// AdvA field takes up 6 bytes +// if only Local Name is appended, then max lenght of Local Name shall be +// 37-6-2=31 bytes, where UUID of Local Name takes up 2 bytes +#define CONFIG_BT_DEVICE_NAME_MAX 29 + +#if defined(CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS) +#define CONFIG_BT_PERIPHERAL_PREF_MIN_INT 0x0018 +#define CONFIG_BT_PERIPHERAL_PREF_MAX_INT 0x0028 +#define CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY 0 +#define CONFIG_BT_PERIPHERAL_PREF_TIMEOUT 400 +#endif + +#if defined(CONFIG_BT_BREDR) +#define CONFIG_BT_PAGE_TIMEOUT 0x2000 //5.12s +#define CONFIG_BT_L2CAP_RX_MTU 672 + +#ifndef CONFIG_BT_RFCOMM_TX_STACK_SIZE +#define CONFIG_BT_RFCOMM_TX_STACK_SIZE 1024 +#endif +#ifndef CONFIG_BT_RFCOMM_TX_PRIO +#define CONFIG_BT_RFCOMM_TX_PRIO (configMAX_PRIORITIES - 5) +#endif + +#define PCM_PRINTF 0 +#endif + +#if defined(CONFIG_BT_AUDIO) +#define CONFIG_BT_MAX_ISO_CONN 8 //range 1 to 64 + +#endif + +/*******************************Bouffalo Lab Modification******************************/ + +//#define BFLB_BLE_DISABLE_STATIC_ATTR +//#define BFLB_BLE_DISABLE_STATIC_CHANNEL +#define BFLB_DISABLE_BT +#define BFLB_FIXED_IRK 0 +#define BFLB_DYNAMIC_ALLOC_MEM +#if defined(CONFIG_AUTO_PTS) +#define CONFIG_BT_DEVICE_NAME_GATT_WRITABLE 1 +#define CONFIG_BT_GATT_SERVICE_CHANGED 1 +#define CONFIG_BT_GATT_CACHING 1 +#define CONFIG_BT_SCAN_WITH_IDENTITY 1 +#define CONFIG_BT_ADV_WITH_PUBLIC_ADDR 1 +#define CONFIG_BT_ATT_PREPARE_COUNT 64 +#endif +#endif //BFLB_BLE + +/*******************************Bouffalo Lab Patch******************************/ +/*Fix the issue that DHKEY_check_failed error happens in smp procedure if CONFIG_BT_PRIVACY is enabled.*/ +#define BFLB_BLE_PATCH_DHKEY_CHECK_FAILED +/*To notify upper layer that write_ccc is completed*/ +#define BFLB_BLE_PATCH_NOTIFY_WRITE_CCC_RSP +/*Timer/Queue/Sem allocated during connection establishment is not released when disconnection +happens, which cause memory leak issue.*/ +#define BFLB_BLE_PATCH_FREE_ALLOCATED_BUFFER_IN_OS +/*To avoid duplicated pubkey callback.*/ +#define BFLB_BLE_PATCH_AVOID_DUPLI_PUBKEY_CB +/*The flag @conn_ref is not clean up after disconnect*/ +#define BFLB_BLE_PATCH_CLEAN_UP_CONNECT_REF +/*To avoid sevice changed indication sent at the very beginning, without any new service added.*/ +#define BFLB_BLE_PATCH_SET_SCRANGE_CHAGD_ONLY_IN_CONNECTED_STATE +#ifdef CONFIG_BT_SETTINGS +/*Semaphore is used during flash operation. Make sure that freertos has already run up when it + intends to write information to flash.*/ +#define BFLB_BLE_PATCH_SETTINGS_LOAD +#endif +#define BFLB_BLE_SMP_LOCAL_AUTH +#define BFLB_BLE_MTU_CHANGE_CB +#if defined(CFG_BT_RESET) +#define BFLB_HOST_ASSISTANT +#endif + +#define BFLB_RELEASE_CMD_SEM_IF_CONN_DISC +/*Fix the issue when local auth_req is 0(no boinding), +BT_SMP_DIST_ENC_KEY bit is not cleared while remote ENC_KEY is received.*/ +#define BFLB_BLE_PATCH_CLEAR_REMOTE_KEY_BIT + +#if defined(__cplusplus) +} +#endif + +#endif /* BLE_CONFIG_H */ diff --git a/components/ble/ble_stack/port/include/zephyr.h b/components/ble/ble_stack/port/include/zephyr.h new file mode 100644 index 00000000..2acbea7e --- /dev/null +++ b/components/ble/ble_stack/port/include/zephyr.h @@ -0,0 +1,157 @@ +#ifndef ZEPHYR_H +#define ZEPHYR_H +#include +#include + +#include +#include +#include +#include "bl_port.h" +#include "work_q.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define _STRINGIFY(x) #x +#if 0 +#define ___in_section(a, b, c) \ + __attribute__((section("." _STRINGIFY(a) \ + "." _STRINGIFY(b) \ + "." _STRINGIFY(c)))) + +#endif +#define ARG_UNUSED(x) (void)(x) + +#ifndef __aligned +#define __aligned(x) __attribute__((__aligned__(x))) +#endif + +#ifndef __printf_like +#define __printf_like(f, a) __attribute__((format (printf, f, a))) +#endif +#define STACK_ALIGN 4 + +#define ASSERT(test, fmt, ...) + +#define K_FOREVER -1 +#define K_NO_WAIT 0 + +/* Unaligned access */ +#define UNALIGNED_GET(p) \ + __extension__ ({ \ + struct __attribute__((__packed__)) { \ + __typeof__(*(p)) __v; \ + } *__p = (__typeof__(__p)) (p); \ + __p->__v; \ + }) + +#ifndef UNUSED +#define UNUSED(x) (void)x +#endif + +enum _poll_types_bits { + _POLL_TYPE_IGNORE, + _POLL_TYPE_SIGNAL, + _POLL_TYPE_SEM_AVAILABLE, + _POLL_TYPE_DATA_AVAILABLE, + _POLL_NUM_TYPES +}; + +#define _POLL_TYPE_BIT(type) (1 << ((type) - 1)) + +enum _poll_states_bits { + _POLL_STATE_NOT_READY, + _POLL_STATE_SIGNALED, + _POLL_STATE_SEM_AVAILABLE, + _POLL_STATE_DATA_AVAILABLE, + _POLL_NUM_STATES +}; + +#define _POLL_STATE_BIT(state) (1 << ((state) - 1)) + +#define _POLL_EVENT_NUM_UNUSED_BITS \ + (32 - (0 \ + + 8 /* tag */ \ + + _POLL_NUM_TYPES \ + + _POLL_NUM_STATES \ + + 1 /* modes */ \ + )) + +#define K_POLL_SIGNAL_INITIALIZER(obj) \ + { \ + .poll_events = SYS_DLIST_STATIC_INIT(&obj.poll_events), \ + .signaled = 0, \ + .result = 0, \ + } + +struct k_poll_event { + sys_dnode_t _node; + struct _poller *poller; + u32_t tag:8; + u32_t type:_POLL_NUM_TYPES; + u32_t state:_POLL_NUM_STATES; + u32_t mode:1; + u32_t unused:_POLL_EVENT_NUM_UNUSED_BITS; + union { + void *obj; + struct k_poll_signal *signal; + struct k_sem *sem; + struct k_fifo *fifo; + struct k_queue *queue; + }; +}; + +struct k_poll_signal { + sys_dlist_t poll_events; + unsigned int signaled; + int result; +}; + +#define K_POLL_STATE_NOT_READY 0 +#define K_POLL_STATE_EADDRINUSE 1 +#define K_POLL_STATE_SIGNALED 2 +#define K_POLL_STATE_SEM_AVAILABLE 3 +#define K_POLL_STATE_DATA_AVAILABLE 4 +#define K_POLL_STATE_FIFO_DATA_AVAILABLE K_POLL_STATE_DATA_AVAILABLE + +#define K_POLL_TYPE_IGNORE 0 +#define K_POLL_TYPE_SIGNAL 1 +#define K_POLL_TYPE_SEM_AVAILABLE 2 +#define K_POLL_TYPE_DATA_AVAILABLE 3 +#define K_POLL_TYPE_FIFO_DATA_AVAILABLE K_POLL_TYPE_DATA_AVAILABLE + +#define K_POLL_EVENT_STATIC_INITIALIZER(event_type, event_mode, event_obj, \ + event_tag) \ + { \ + .type = event_type, \ + .tag = event_tag, \ + .state = K_POLL_STATE_NOT_READY, \ + .mode = event_mode, \ + .unused = 0, \ + { .obj = event_obj }, \ + } + +extern int k_poll_signal_raise(struct k_poll_signal *signal, int result); +extern int k_poll(struct k_poll_event *events, int num_events, s32_t timeout); +extern void k_poll_event_init(struct k_poll_event *event, u32_t type, int mode, void *obj); + +/* public - polling modes */ +enum k_poll_modes { + /* polling thread does not take ownership of objects when available */ + K_POLL_MODE_NOTIFY_ONLY = 0, + + K_POLL_NUM_MODES +}; + +#define k_oops() + +//void k_sleep(s32_t duration); + +#if defined(__cplusplus) + } +#endif + +#endif /* ZEPHYR_H */ + diff --git a/components/ble/ble_stack/sbc/dec/alloc.c b/components/ble/ble_stack/sbc/dec/alloc.c new file mode 100644 index 00000000..8e59674b --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/alloc.c @@ -0,0 +1,82 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 +#include + +#if defined(SBC_DEC_INCLUDED) + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +PRIVATE OI_STATUS OI_CODEC_SBC_Alloc(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT32 *codecDataAligned, + OI_UINT32 codecDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride) +{ + int i; + size_t filterBufferCount; + size_t subdataSize; + OI_BYTE *codecData = (OI_BYTE *)codecDataAligned; + + if (maxChannels < 1 || maxChannels > 2) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (pcmStride < 1 || pcmStride > maxChannels) { + return OI_STATUS_INVALID_PARAMETERS; + } + + common->maxChannels = maxChannels; + common->pcmStride = pcmStride; + + /* Compute sizes needed for the memory regions, and bail if we don't have + * enough memory for them. */ + subdataSize = maxChannels * sizeof(common->subdata[0]) * SBC_MAX_BANDS * SBC_MAX_BLOCKS; + if (subdataSize > codecDataBytes) { + return OI_STATUS_OUT_OF_MEMORY; + } + + filterBufferCount = (codecDataBytes - subdataSize) / (sizeof(common->filterBuffer[0][0]) * SBC_MAX_BANDS * maxChannels); + if (filterBufferCount < SBC_CODEC_MIN_FILTER_BUFFERS) { + return OI_STATUS_OUT_OF_MEMORY; + } + common->filterBufferLen = filterBufferCount * SBC_MAX_BANDS; + + /* Allocate memory for the subband data */ + common->subdata = (OI_INT32 *)codecData; + codecData += subdataSize; + OI_ASSERT(codecDataBytes >= subdataSize); + codecDataBytes -= subdataSize; + + /* Allocate memory for the synthesis buffers */ + for (i = 0; i < maxChannels; ++i) { + size_t allocSize = common->filterBufferLen * sizeof(common->filterBuffer[0][0]); + common->filterBuffer[i] = (SBC_BUFFER_T *)codecData; + OI_ASSERT(codecDataBytes >= allocSize); + codecData += allocSize; + codecDataBytes -= allocSize; + } + + return OI_OK; +} + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c b/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c new file mode 100644 index 00000000..b9ff025a --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c @@ -0,0 +1,167 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ +#include + +#if defined(SBC_DEC_INCLUDED) + +static void dualBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_UINT bitcountL; + OI_UINT bitcountR; + OI_UINT bitpoolPreferenceL = 0; + OI_UINT bitpoolPreferenceR = 0; + BITNEED_UNION1 bitneedsL; + BITNEED_UNION1 bitneedsR; + + bitcountL = computeBitneed(common, bitneedsL.uint8, 0, &bitpoolPreferenceL); + bitcountR = computeBitneed(common, bitneedsR.uint8, 1, &bitpoolPreferenceR); + + oneChannelBitAllocation(common, &bitneedsL, 0, bitcountL); + oneChannelBitAllocation(common, &bitneedsR, 1, bitcountR); +} + +static void stereoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + BITNEED_UNION2 bitneeds; + OI_UINT excess; + OI_INT bitadjust; + OI_UINT bitcount; + OI_UINT sbL; + OI_UINT sbR; + OI_UINT bitpoolPreference = 0; + + bitcount = computeBitneed(common, &bitneeds.uint8[0], 0, &bitpoolPreference); + bitcount += computeBitneed(common, &bitneeds.uint8[nrof_subbands], 1, &bitpoolPreference); + + { + OI_UINT ex; + bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds.uint32, 2 * nrof_subbands, bitcount, &ex); + /* We want the compiler to put excess into a register */ + excess = ex; + } + sbL = 0; + sbR = nrof_subbands; + while (sbL < nrof_subbands) { + excess = allocAdjustedBits(&common->bits.uint8[sbL], bitneeds.uint8[sbL] + bitadjust, excess); + ++sbL; + excess = allocAdjustedBits(&common->bits.uint8[sbR], bitneeds.uint8[sbR] + bitadjust, excess); + ++sbR; + } + sbL = 0; + sbR = nrof_subbands; + while (excess) { + excess = allocExcessBits(&common->bits.uint8[sbL], excess); + ++sbL; + if (!excess) { + break; + } + excess = allocExcessBits(&common->bits.uint8[sbR], excess); + ++sbR; + } + +} + +static const BIT_ALLOC balloc[] = { + monoBitAllocation, /* SBC_MONO */ + dualBitAllocation, /* SBC_DUAL_CHANNEL */ + stereoBitAllocation, /* SBC_STEREO */ + stereoBitAllocation /* SBC_JOINT_STEREO */ +}; + + +PRIVATE void OI_SBC_ComputeBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_ASSERT(common->frameInfo.bitpool <= OI_SBC_MaxBitpool(&common->frameInfo)); + OI_ASSERT(common->frameInfo.mode < OI_ARRAYSIZE(balloc)); + + /* + * Using an array of function pointers prevents the compiler from creating a suboptimal + * monolithic inlined bit allocation function. + */ + balloc[common->frameInfo.mode](common); +} + +OI_UINT32 OI_CODEC_SBC_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame) +{ + return internal_CalculateBitrate(frame); +} + +/* + * Return the current maximum bitneed and clear it. + */ +OI_UINT8 OI_CODEC_SBC_GetMaxBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + OI_UINT8 max = common->maxBitneed; + + common->maxBitneed = 0; + return max; +} + +/* + * Calculates the bitpool size for a given frame length + */ +OI_UINT16 OI_CODEC_SBC_CalculateBitpool(OI_CODEC_SBC_FRAME_INFO *frame, + OI_UINT16 frameLen) +{ + OI_UINT16 nrof_subbands = frame->nrof_subbands; + OI_UINT16 nrof_blocks = frame->nrof_blocks; + OI_UINT16 hdr; + OI_UINT16 bits; + + if (frame->mode == SBC_JOINT_STEREO) { + hdr = 9 * nrof_subbands; + } else { + if (frame->mode == SBC_MONO) { + hdr = 4 * nrof_subbands; + } else { + hdr = 8 * nrof_subbands; + } + if (frame->mode == SBC_DUAL_CHANNEL) { + nrof_blocks *= 2; + } + } + bits = 8 * (frameLen - SBC_HEADER_LEN) - hdr; + return DIVIDE(bits, nrof_blocks); +} + +OI_UINT16 OI_CODEC_SBC_CalculatePcmBytes(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + return sizeof(OI_INT16) * common->pcmStride * common->frameInfo.nrof_subbands * common->frameInfo.nrof_blocks; +} + + +OI_UINT16 OI_CODEC_SBC_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame) +{ + return internal_CalculateFramelen(frame); +} + +/**@}*/ +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/bitalloc.c b/components/ble/ble_stack/sbc/dec/bitalloc.c new file mode 100644 index 00000000..e264196a --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/bitalloc.c @@ -0,0 +1,404 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file + +The functions in this file relate to the allocation of available bits to +subbands within the SBC/eSBC frame, along with support functions for computing +frame length and bitrate. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_utils.h" +#include + +#if defined(SBC_DEC_INCLUDED) + +OI_UINT32 OI_SBC_MaxBitpool(OI_CODEC_SBC_FRAME_INFO *frame) +{ + switch (frame->mode) { + case SBC_MONO: + case SBC_DUAL_CHANNEL: + return 16 * frame->nrof_subbands; + case SBC_STEREO: + case SBC_JOINT_STEREO: + return 32 * frame->nrof_subbands; + } + + ERROR(("Invalid frame mode %d", frame->mode)); + OI_ASSERT(FALSE); + return 0; /* Should never be reached */ +} + + +PRIVATE OI_UINT16 internal_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame) +{ + OI_UINT16 nbits = frame->nrof_blocks * frame->bitpool; + OI_UINT16 nrof_subbands = frame->nrof_subbands; + OI_UINT16 result = nbits; + + if (frame->mode == SBC_JOINT_STEREO) { + result += nrof_subbands + (8 * nrof_subbands); + } else { + if (frame->mode == SBC_DUAL_CHANNEL) { + result += nbits; + } + if (frame->mode == SBC_MONO) { + result += 4 * nrof_subbands; + } else { + result += 8 * nrof_subbands; + } + } + return SBC_HEADER_LEN + (result + 7) / 8; +} + + +PRIVATE OI_UINT32 internal_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame) +{ + OI_UINT blocksbands; + blocksbands = frame->nrof_subbands * frame->nrof_blocks; + + return DIVIDE(8 * internal_CalculateFramelen(frame) * frame->frequency, blocksbands); +} + + +INLINE OI_UINT16 OI_SBC_CalculateFrameAndHeaderlen(OI_CODEC_SBC_FRAME_INFO *frame, OI_UINT *headerLen_) +{ + OI_UINT headerLen = SBC_HEADER_LEN + frame->nrof_subbands * frame->nrof_channels / 2; + + if (frame->mode == SBC_JOINT_STEREO) { + headerLen++; + } + + *headerLen_ = headerLen; + return internal_CalculateFramelen(frame); +} + + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + + +/* + * Computes the bit need for each sample and as also returns a counts of bit needs that are greater + * than one. This count is used in the first phase of bit allocation. + * + * We also compute a preferred bitpool value that this is the minimum bitpool needed to guarantee + * lossless representation of the audio data. The preferred bitpool may be larger than the bits + * actually required but the only input we have are the scale factors. For example, it takes 2 bits + * to represent values in the range -1 .. +1 but the scale factor is 0. To guarantee lossless + * representation we add 2 to each scale factor and sum them to come up with the preferred bitpool. + * This is not ideal because 0 requires 0 bits but we currently have no way of knowing this. + * + * @param bitneed Array to return bitneeds for each subband + * + * @param ch Channel 0 or 1 + * + * @param preferredBitpool Returns the number of reserved bits + * + * @return The SBC bit need + * + */ +OI_UINT computeBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT8 *bitneeds, + OI_UINT ch, + OI_UINT *preferredBitpool) +{ + static const OI_INT8 offset4[4][4] = { + { -1, 0, 0, 0 }, + { -2, 0, 0, 1 }, + { -2, 0, 0, 1 }, + { -2, 0, 0, 1 } + }; + + static const OI_INT8 offset8[4][8] = { + { -2, 0, 0, 0, 0, 0, 0, 1 }, + { -3, 0, 0, 0, 0, 0, 1, 2 }, + { -4, 0, 0, 0, 0, 0, 1, 2 }, + { -4, 0, 0, 0, 0, 0, 1, 2 } + }; + + const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + OI_UINT sb; + OI_INT8 *scale_factor = &common->scale_factor[ch ? nrof_subbands : 0]; + OI_UINT bitcount = 0; + OI_UINT8 maxBits = 0; + OI_UINT8 prefBits = 0; + + if (common->frameInfo.alloc == SBC_SNR) { + for (sb = 0; sb < nrof_subbands; sb++) { + OI_INT bits = scale_factor[sb]; + if (bits > maxBits) { + maxBits = bits; + } + if ((bitneeds[sb] = bits) > 1) { + bitcount += bits; + } + prefBits += 2 + bits; + } + } else { + const OI_INT8 *offset; + if (nrof_subbands == 4) { + offset = offset4[common->frameInfo.freqIndex]; + } else { + offset = offset8[common->frameInfo.freqIndex]; + } + for (sb = 0; sb < nrof_subbands; sb++) { + OI_INT bits = scale_factor[sb]; + if (bits > maxBits) { + maxBits = bits; + } + prefBits += 2 + bits; + if (bits) { + bits -= offset[sb]; + if (bits > 0) { + bits /= 2; + } + bits += 5; + } + if ((bitneeds[sb] = bits) > 1) { + bitcount += bits; + } + } + } + common->maxBitneed = OI_MAX(maxBits, common->maxBitneed); + *preferredBitpool += prefBits; + return bitcount; +} + + +/* + * Explanation of the adjustToFitBitpool inner loop. + * + * The inner loop computes the effect of adjusting the bit allocation up or + * down. Allocations must be 0 or in the range 2..16. This is accomplished by + * the following code: + * + * for (s = bands - 1; s >= 0; --s) { + * OI_INT bits = bitadjust + bitneeds[s]; + * bits = bits < 2 ? 0 : bits; + * bits = bits > 16 ? 16 : bits; + * count += bits; + * } + * + * This loop can be optimized to perform 4 operations at a time as follows: + * + * Adjustment is computed as a 7 bit signed value and added to the bitneed. + * + * Negative allocations are zeroed by masking. (n & 0x40) >> 6 puts the + * sign bit into bit 0, adding this to 0x7F give us a mask of 0x80 + * for -ve values and 0x7F for +ve values. + * + * n &= 0x7F + (n & 0x40) >> 6) + * + * Allocations greater than 16 are truncated to 16. Adjusted allocations are in + * the range 0..31 so we know that bit 4 indicates values >= 16. We use this bit + * to create a mask that zeroes bits 0 .. 3 if bit 4 is set. + * + * n &= (15 + (n >> 4)) + * + * Allocations of 1 are disallowed. Add and shift creates a mask that + * eliminates the illegal value + * + * n &= ((n + 14) >> 4) | 0x1E + * + * These operations can be performed in 8 bits without overflowing so we can + * operate on 4 values at once. + */ + + +/* + * Encoder/Decoder + * + * Computes adjustment +/- of bitneeds to fill bitpool and returns overall + * adjustment and excess bits. + * + * @param bitpool The bitpool we have to work within + * + * @param bitneeds An array of bit needs (more acturately allocation prioritities) for each + * subband across all blocks in the SBC frame + * + * @param subbands The number of subbands over which the adkustment is calculated. For mono and + * dual mode this is 4 or 8, for stereo or joint stereo this is 8 or 16. + * + * @param bitcount A starting point for the adjustment + * + * @param excess Returns the excess bits after the adjustment + * + * @return The adjustment. + */ +OI_INT adjustToFitBitpool(const OI_UINT bitpool, + OI_UINT32 *bitneeds, + const OI_UINT subbands, + OI_UINT bitcount, + OI_UINT *excess) +{ + OI_INT maxBitadjust = 0; + OI_INT bitadjust = (bitcount > bitpool) ? -8 : 8; + OI_INT chop = 8; + + /* + * This is essentially a binary search for the optimal adjustment value. + */ + while ((bitcount != bitpool) && chop) { + OI_UINT32 total = 0; + OI_UINT count; + OI_UINT32 adjust4; + OI_INT i; + + adjust4 = bitadjust & 0x7F; + adjust4 |= (adjust4 << 8); + adjust4 |= (adjust4 << 16); + + for (i = (subbands / 4 - 1); i >= 0; --i) { + OI_UINT32 mask; + OI_UINT32 n = bitneeds[i] + adjust4; + mask = 0x7F7F7F7F + ((n & 0x40404040) >> 6); + n &= mask; + mask = 0x0F0F0F0F + ((n & 0x10101010) >> 4); + n &= mask; + mask = (((n + 0x0E0E0E0E) >> 4) | 0x1E1E1E1E); + n &= mask; + total += n; + } + + count = (total & 0xFFFF) + (total >> 16); + count = (count & 0xFF) + (count >> 8); + + chop >>= 1; + if (count > bitpool) { + bitadjust -= chop; + } else { + maxBitadjust = bitadjust; + bitcount = count; + bitadjust += chop; + } + } + + *excess = bitpool - bitcount; + + return maxBitadjust; +} + + +/* + * The bit allocator trys to avoid single bit allocations except as a last resort. So in the case + * where a bitneed of 1 was passed over during the adsjustment phase 2 bits are now allocated. + */ +INLINE OI_INT allocAdjustedBits(OI_UINT8 *dest, + OI_INT bits, + OI_INT excess) +{ + if (bits < 16) { + if (bits > 1) { + if (excess) { + ++bits; + --excess; + } + } else if ((bits == 1) && (excess > 1)) { + bits = 2; + excess -= 2; + } else { + bits = 0; + } + } else { + bits = 16; + } + *dest = (OI_UINT8)bits; + return excess; +} + + +/* + * Excess bits not allocated by allocaAdjustedBits are allocated round-robin. + */ +INLINE OI_INT allocExcessBits(OI_UINT8 *dest, + OI_INT excess) +{ + if (*dest < 16) { + *dest += 1; + return excess - 1; + } else { + return excess; + } +} + +void oneChannelBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common, + BITNEED_UNION1 *bitneeds, + OI_UINT ch, + OI_UINT bitcount) +{ + const OI_UINT8 nrof_subbands = common->frameInfo.nrof_subbands; + OI_UINT excess; + OI_UINT sb; + OI_INT bitadjust; + OI_UINT8 RESTRICT *allocBits; + + + { + OI_UINT ex; + bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds->uint32, nrof_subbands, bitcount, &ex); + /* We want the compiler to put excess into a register */ + excess = ex; + } + + /* + * Allocate adjusted bits + */ + allocBits = &common->bits.uint8[ch ? nrof_subbands : 0]; + + sb = 0; + while (sb < nrof_subbands) { + excess = allocAdjustedBits(&allocBits[sb], bitneeds->uint8[sb] + bitadjust, excess); + ++sb; + } + sb = 0; + while (excess) { + excess = allocExcessBits(&allocBits[sb], excess); + ++sb; + } +} + + +void monoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common) +{ + BITNEED_UNION1 bitneeds; + OI_UINT bitcount; + OI_UINT bitpoolPreference = 0; + + bitcount = computeBitneed(common, bitneeds.uint8, 0, &bitpoolPreference); + + oneChannelBitAllocation(common, &bitneeds, 0, bitcount); +} + +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/bitstream-decode.c b/components/ble/ble_stack/sbc/dec/bitstream-decode.c new file mode 100644 index 00000000..da20e314 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/bitstream-decode.c @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Functions for manipulating input bitstreams. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_stddefs.h" +#include "oi_bitstream.h" +#include "oi_assert.h" + +#if defined(SBC_DEC_INCLUDED) + +PRIVATE void OI_BITSTREAM_ReadInit(OI_BITSTREAM *bs, + const OI_BYTE *buffer) +{ + bs->value = ((OI_INT32)buffer[0] << 16) | ((OI_INT32)buffer[1] << 8) | (buffer[2]); + bs->ptr.r = buffer + 3; + bs->bitPtr = 8; +} + +PRIVATE OI_UINT32 OI_BITSTREAM_ReadUINT(OI_BITSTREAM *bs, OI_UINT bits) +{ + OI_UINT32 result; + + OI_BITSTREAM_READUINT(result, bits, bs->ptr.r, bs->value, bs->bitPtr); + + return result; +} + +PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT4Aligned(OI_BITSTREAM *bs) +{ + OI_UINT32 result; + + OI_ASSERT(bs->bitPtr < 16); + OI_ASSERT(bs->bitPtr % 4 == 0); + + if (bs->bitPtr == 8) { + result = bs->value << 8; + bs->bitPtr = 12; + } else { + result = bs->value << 12; + bs->value = (bs->value << 8) | *bs->ptr.r++; + bs->bitPtr = 8; + } + result >>= 28; + OI_ASSERT(result < (1u << 4)); + return (OI_UINT8)result; +} + +PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT8Aligned(OI_BITSTREAM *bs) +{ + OI_UINT32 result; + OI_ASSERT(bs->bitPtr == 8); + + result = bs->value >> 16; + bs->value = (bs->value << 8) | *bs->ptr.r++; + + return (OI_UINT8)result; +} + +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/decoder-oina.c b/components/ble/ble_stack/sbc/dec/decoder-oina.c new file mode 100644 index 00000000..3515826c --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/decoder-oina.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2006 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file +This file exposes OINA-specific interfaces to decoder functions. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include + +#if defined(SBC_DEC_INCLUDED) + +OI_STATUS OI_CODEC_SBC_DecoderConfigureRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 frequency, + OI_UINT8 mode, + OI_UINT8 subbands, + OI_UINT8 blocks, + OI_UINT8 alloc, + OI_UINT8 maxBitpool) +{ + if (frequency > SBC_FREQ_48000) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (enhanced) { +#ifdef SBC_ENHANCED + if (subbands != SBC_SUBBANDS_8) { + return OI_STATUS_INVALID_PARAMETERS; + } +#else + return OI_STATUS_INVALID_PARAMETERS; +#endif + } + + if (mode > SBC_JOINT_STEREO) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (subbands > SBC_SUBBANDS_8) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (blocks > SBC_BLOCKS_16) { + return OI_STATUS_INVALID_PARAMETERS; + } + + if (alloc > SBC_SNR) { + return OI_STATUS_INVALID_PARAMETERS; + } + +#ifdef SBC_ENHANCED + context->common.frameInfo.enhanced = enhanced; +#else + context->common.frameInfo.enhanced = FALSE; +#endif + context->common.frameInfo.freqIndex = frequency; + context->common.frameInfo.mode = mode; + context->common.frameInfo.subbands = subbands; + context->common.frameInfo.blocks = blocks; + context->common.frameInfo.alloc = alloc; + context->common.frameInfo.bitpool = maxBitpool; + + OI_SBC_ExpandFrameFields(&context->common.frameInfo); + + if (context->common.frameInfo.nrof_channels >= context->common.pcmStride) { + return OI_STATUS_INVALID_PARAMETERS; + } + + return OI_OK; +} + + + +OI_STATUS OI_CODEC_SBC_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + return internal_DecodeRaw(context, + bitpool, + frameData, + frameBytes, + pcmData, + pcmBytes); +} + +OI_STATUS OI_CODEC_SBC_DecoderLimit(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 subbands) +{ + if (enhanced) { +#ifdef SBC_ENHANCED + context->enhancedEnabled = TRUE; +#else + context->enhancedEnabled = FALSE; +#endif + } else { + context->enhancedEnabled = FALSE; + } + context->restrictSubbands = subbands; + context->limitFrameFormat = TRUE; + return OI_OK; +} + + +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/decoder-private.c b/components/ble/ble_stack/sbc/dec/decoder-private.c new file mode 100644 index 00000000..c3b99882 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/decoder-private.c @@ -0,0 +1,258 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** +@file +This file drives SBC decoding. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_codec_sbc_private.h" +#include "oi_bitstream.h" +#include + +#if defined(SBC_DEC_INCLUDED) + +OI_CHAR *const OI_Codec_Copyright = "Copyright 2002-2007 Open Interface North America, Inc. All rights reserved"; + +INLINE OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_BYTE maxChannels, + OI_BYTE pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable) +{ + OI_UINT i; + OI_STATUS status; + + for (i = 0; i < sizeof(*context); i++) { + ((char *)context)[i] = 0; + } + +#ifdef SBC_ENHANCED + context->enhancedEnabled = enhanced ? TRUE : FALSE; +#else + context->enhancedEnabled = FALSE; + if (enhanced) { + return OI_STATUS_INVALID_PARAMETERS; + } +#endif + + if (msbc_enable) { + context->sbc_mode = OI_SBC_MODE_MSBC; + } else { + context->sbc_mode = OI_SBC_MODE_STD; + } + + status = OI_CODEC_SBC_Alloc(&context->common, decoderData, decoderDataBytes, maxChannels, pcmStride); + + if (!OI_SUCCESS(status)) { + return status; + } + + context->common.codecInfo = OI_Codec_Copyright; + context->common.maxBitneed = 0; + context->limitFrameFormat = FALSE; + OI_SBC_ExpandFrameFields(&context->common.frameInfo); + + /*PLATFORM_DECODER_RESET(context);*/ + + return OI_OK; +} + +/** + * Read the SBC header up to but not including the joint stereo mask. The syncword has already been + * examined, and the enhanced mode flag set, by FindSyncword. + */ +INLINE void OI_SBC_ReadHeader(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data) +{ + OI_CODEC_SBC_FRAME_INFO *frame = &common->frameInfo; + OI_UINT8 d1; + + + OI_ASSERT(data[0] == OI_SBC_SYNCWORD || data[0] == OI_SBC_ENHANCED_SYNCWORD + || data[0] == OI_mSBC_SYNCWORD); + + /** + * For mSBC, just set those parameters + */ + if (data[0] == OI_mSBC_SYNCWORD){ + frame->freqIndex = 0; + frame->frequency = 16000; + + frame->blocks = 4; + frame->nrof_blocks = 15; + + frame->mode = 0; + frame->nrof_channels = 1; + + frame->alloc = SBC_LOUDNESS; + + frame->subbands = 1; + frame->nrof_subbands = 8; + + frame->cachedInfo = 0; + + frame->bitpool = 26; + frame->crc = data[3]; + return; + } + + /* Avoid filling out all these strucutures if we already remember the values + * from last time. Just in case we get a stream corresponding to data[1] == + * 0, DecoderReset is responsible for ensuring the lookup table entries have + * already been populated + */ + d1 = data[1]; + if (d1 != frame->cachedInfo) { + + frame->freqIndex = (d1 & (BIT7 | BIT6)) >> 6; + frame->frequency = freq_values[frame->freqIndex]; + + frame->blocks = (d1 & (BIT5 | BIT4)) >> 4; + frame->nrof_blocks = block_values[frame->blocks]; + + frame->mode = (d1 & (BIT3 | BIT2)) >> 2; + frame->nrof_channels = channel_values[frame->mode]; + + frame->alloc = (d1 & BIT1) >> 1; + + frame->subbands = (d1 & BIT0); + frame->nrof_subbands = band_values[frame->subbands]; + + frame->cachedInfo = d1; + } + /* + * For decode, the bit allocator needs to know the bitpool value + */ + frame->bitpool = data[2]; + frame->crc = data[3]; +} + + +#define LOW(x) ((x)& 0xf) +#define HIGH(x) ((x) >> 4) + +/* + * Read scalefactor values and prepare the bitstream for OI_SBC_ReadSamples + */ +PRIVATE void OI_SBC_ReadScalefactors(OI_CODEC_SBC_COMMON_CONTEXT *common, + const OI_BYTE *b, + OI_BITSTREAM *bs) +{ + OI_UINT i = common->frameInfo.nrof_subbands * common->frameInfo.nrof_channels; + OI_INT8 *scale_factor = common->scale_factor; + OI_UINT f; + + if (common->frameInfo.nrof_subbands == 8 || common->frameInfo.mode != SBC_JOINT_STEREO) { + if (common->frameInfo.mode == SBC_JOINT_STEREO) { + common->frameInfo.join = *b++; + } else { + common->frameInfo.join = 0; + } + i /= 2; + do { + *scale_factor++ = HIGH(f = *b++); + *scale_factor++ = LOW(f); + } while (--i); + /* + * In this case we know that the scale factors end on a byte boundary so all we need to do + * is initialize the bitstream. + */ + OI_BITSTREAM_ReadInit(bs, b); + } else { + OI_ASSERT(common->frameInfo.nrof_subbands == 4 && common->frameInfo.mode == SBC_JOINT_STEREO); + common->frameInfo.join = HIGH(f = *b++); + i = (i - 1) / 2; + do { + *scale_factor++ = LOW(f); + *scale_factor++ = HIGH(f = *b++); + } while (--i); + *scale_factor++ = LOW(f); + /* + * In 4-subband joint stereo mode, the joint stereo information ends on a half-byte + * boundary, so it's necessary to use the bitstream abstraction to read it, since + * OI_SBC_ReadSamples will need to pick up in mid-byte. + */ + OI_BITSTREAM_ReadInit(bs, b); + *scale_factor++ = OI_BITSTREAM_ReadUINT4Aligned(bs); + } +} + +/** Read quantized subband samples from the input bitstream and expand them. */ +PRIVATE void OI_SBC_ReadSamples(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT nrof_blocks = common->frameInfo.nrof_blocks; + OI_INT32 *RESTRICT s = common->subdata; + OI_UINT8 *ptr = global_bs->ptr.w; + OI_UINT32 value = global_bs->value; + OI_UINT bitPtr = global_bs->bitPtr; + + const OI_UINT iter_count = common->frameInfo.nrof_channels * common->frameInfo.nrof_subbands / 4; + do { + OI_UINT i; + for (i = 0; i < iter_count; ++i) { + OI_UINT32 sf_by4 = ((OI_UINT32 *)common->scale_factor)[i]; + OI_UINT32 bits_by4 = common->bits.uint32[i]; + OI_UINT n; + for (n = 0; n < 4; ++n) { + OI_INT32 dequant; + OI_UINT bits; + OI_INT sf; + + if (OI_CPU_BYTE_ORDER == OI_LITTLE_ENDIAN_BYTE_ORDER) { + bits = bits_by4 & 0xFF; + bits_by4 >>= 8; + sf = sf_by4 & 0xFF; + sf_by4 >>= 8; + } else { + bits = (bits_by4 >> 24) & 0xFF; + bits_by4 <<= 8; + sf = (sf_by4 >> 24) & 0xFF; + sf_by4 <<= 8; + } + if (bits) { + OI_UINT32 raw; + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + } else { + dequant = 0; + } + *s++ = dequant; + } + } + } while (--nrof_blocks); +} + +/** +@} +*/ +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/decoder-sbc.c b/components/ble/ble_stack/sbc/dec/decoder-sbc.c new file mode 100644 index 00000000..e923ade2 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/decoder-sbc.c @@ -0,0 +1,471 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2006 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ + ***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addtogroup codec_internal */ +/**@{*/ + +#include "oi_codec_sbc_private.h" +#include "oi_bitstream.h" + +#if defined(SBC_DEC_INCLUDED) + +#define SPECIALIZE_READ_SAMPLES_JOINT + +/** + * Scans through a buffer looking for a codec syncword. If the decoder has been + * set for enhanced operation using OI_CODEC_SBC_DecoderReset(), it will search + * for both a standard and an enhanced syncword. + */ +PRIVATE OI_STATUS FindSyncword(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes) +{ +#ifdef SBC_ENHANCED + OI_BYTE search1 = OI_SBC_SYNCWORD; + OI_BYTE search2 = OI_SBC_ENHANCED_SYNCWORD; +#endif // SBC_ENHANCED + + if (*frameBytes == 0) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + +#ifdef SBC_ENHANCED + if (context->limitFrameFormat && context->enhancedEnabled) { + /* If the context is restricted, only search for specified SYNCWORD */ + search1 = search2; + } else if (context->enhancedEnabled == FALSE) { + /* If enhanced is not enabled, only search for classic SBC SYNCWORD*/ + search2 = search1; + } + while (*frameBytes && (**frameData != search1) && (**frameData != search2)) { + (*frameBytes)--; + (*frameData)++; + } + if (*frameBytes) { + /* Syncword found, *frameData points to it, and *frameBytes correctly + * reflects the number of bytes available to read, including the + * syncword. */ + context->common.frameInfo.enhanced = (**frameData == OI_SBC_ENHANCED_SYNCWORD); + return OI_OK; + } else { + /* No syncword was found anywhere in the provided input data. + * *frameData points past the end of the original input, and + * *frameBytes is 0. */ + return OI_CODEC_SBC_NO_SYNCWORD; + } +#else // SBC_ENHANCED + while (*frameBytes + && (!(context->sbc_mode == OI_SBC_MODE_STD && **frameData == OI_SBC_SYNCWORD)) + && (!(context->sbc_mode == OI_SBC_MODE_MSBC && **frameData == OI_mSBC_SYNCWORD))) { + (*frameBytes)--; + (*frameData)++; + } + if (*frameBytes) { + /* Syncword found, *frameData points to it, and *frameBytes correctly + * reflects the number of bytes available to read, including the + * syncword. */ + context->common.frameInfo.enhanced = FALSE; + return OI_OK; + } else { + /* No syncword was found anywhere in the provided input data. + * *frameData points past the end of the original input, and + * *frameBytes is 0. */ + return OI_CODEC_SBC_NO_SYNCWORD; + } +#endif // SBC_ENHANCED +} + +static OI_STATUS DecodeBody(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE *bodyData, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes, + OI_BOOL allowPartial) +{ + OI_BITSTREAM bs; + OI_UINT frameSamples = context->common.frameInfo.nrof_blocks * context->common.frameInfo.nrof_subbands; + OI_UINT decode_block_count; + + /* + * Based on the header data, make sure that there is enough room to write the output samples. + */ + if (*pcmBytes < (sizeof(OI_INT16) * frameSamples * context->common.pcmStride) && !allowPartial) { + /* If we're not allowing partial decodes, we need room for the entire + * codec frame */ + TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; + } else if (*pcmBytes < sizeof (OI_INT16) * context->common.frameInfo.nrof_subbands * context->common.pcmStride) { + /* Even if we're allowing partials, we can still only decode on a frame + * boundary */ + return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA; + } + + if (context->bufferedBlocks == 0) { + TRACE(("Reading scalefactors")); + OI_SBC_ReadScalefactors(&context->common, bodyData, &bs); + + TRACE(("Computing bit allocation")); + OI_SBC_ComputeBitAllocation(&context->common); + + TRACE(("Reading samples")); + if (context->common.frameInfo.mode == SBC_JOINT_STEREO) { + OI_SBC_ReadSamplesJoint(context, &bs); + } else { + OI_SBC_ReadSamples(context, &bs); + } + + context->bufferedBlocks = context->common.frameInfo.nrof_blocks; + } + + if (allowPartial) { + decode_block_count = *pcmBytes / sizeof(OI_INT16) / context->common.pcmStride / context->common.frameInfo.nrof_subbands; + + if (decode_block_count > context->bufferedBlocks) { + decode_block_count = context->bufferedBlocks; + } + + } else { + decode_block_count = context->common.frameInfo.nrof_blocks; + } + + TRACE(("Synthesizing frame")); + { + OI_UINT start_block = context->common.frameInfo.nrof_blocks - context->bufferedBlocks; + OI_SBC_SynthFrame(context, pcmData, start_block, decode_block_count); + } + + OI_ASSERT(context->bufferedBlocks >= decode_block_count); + context->bufferedBlocks -= decode_block_count; + + frameSamples = decode_block_count * context->common.frameInfo.nrof_subbands; + + /* + * When decoding mono into a stride-2 array, copy pcm data to second channel + */ + if (context->common.frameInfo.nrof_channels == 1 && context->common.pcmStride == 2) { + OI_UINT i; + for (i = 0; i < frameSamples; ++i) { + pcmData[2 * i + 1] = pcmData[2 * i]; + } + } + + /* + * Return number of pcm bytes generated by the decode operation. + */ + *pcmBytes = frameSamples * sizeof(OI_INT16) * context->common.pcmStride; + if (context->bufferedBlocks > 0) { + return OI_CODEC_SBC_PARTIAL_DECODE; + } else { + return OI_OK; + } +} + +PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + OI_STATUS status; + OI_UINT bodyLen; + + TRACE(("+OI_CODEC_SBC_DecodeRaw")); + + if (context->bufferedBlocks == 0) { + /* + * The bitallocator needs to know the bitpool value. + */ + context->common.frameInfo.bitpool = bitpool; + /* + * Compute the frame length and check we have enough frame data to proceed + */ + bodyLen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo) - SBC_HEADER_LEN; + if (*frameBytes < bodyLen) { + TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + } else { + bodyLen = 0; + } + /* + * Decode the SBC data. Pass TRUE to DecodeBody to allow partial decoding of + * tones. + */ + status = DecodeBody(context, *frameData, pcmData, pcmBytes, TRUE); + if (OI_SUCCESS(status) || status == OI_CODEC_SBC_PARTIAL_DECODE) { + *frameData += bodyLen; + *frameBytes -= bodyLen; + } + TRACE(("-OI_CODEC_SBC_DecodeRaw: %d", status)); + return status; +} + +OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable) +{ + return internal_DecoderReset(context, decoderData, decoderDataBytes, maxChannels, pcmStride, enhanced, msbc_enable); +} + +OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes) +{ + OI_STATUS status; + OI_UINT framelen; + OI_UINT8 crc; + + TRACE(("+OI_CODEC_SBC_DecodeFrame")); + + TRACE(("Finding syncword")); + status = FindSyncword(context, frameData, frameBytes); + if (!OI_SUCCESS(status)) { + return status; + } + + /* Make sure enough data remains to read the header. */ + if (*frameBytes < SBC_HEADER_LEN) { + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + + TRACE(("Reading Header")); + OI_SBC_ReadHeader(&context->common, *frameData); + + /* + * Some implementations load the decoder into RAM and use overlays for 4 vs 8 subbands. We need + * to ensure that the SBC parameters for this frame are compatible with the restrictions imposed + * by the loaded overlays. + */ + if (context->limitFrameFormat && (context->common.frameInfo.subbands != context->restrictSubbands)) { + ERROR(("SBC parameters incompatible with loaded overlay")); + return OI_STATUS_INVALID_PARAMETERS; + } + + if (context->common.frameInfo.nrof_channels > context->common.maxChannels) { + ERROR(("SBC parameters incompatible with number of channels specified during reset")); + return OI_STATUS_INVALID_PARAMETERS; + } + + if (context->common.pcmStride < 1 || context->common.pcmStride > 2) { + ERROR(("PCM stride not set correctly during reset")); + return OI_STATUS_INVALID_PARAMETERS; + } + + /* + * At this point a header has been read. However, it's possible that we found a false syncword, + * so the header data might be invalid. Make sure we have enough bytes to read in the + * CRC-protected header, but don't require we have the whole frame. That way, if it turns out + * that we're acting on bogus header data, we don't stall the decoding process by waiting for + * data that we don't actually need. + */ + framelen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo); + if (*frameBytes < framelen) { + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA")); + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + + TRACE(("Calculating checksum")); + + crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); + if (crc != context->common.frameInfo.crc) { + TRACE(("CRC Mismatch: calc=%02x read=%02x\n", crc, context->common.frameInfo.crc)); + TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_CHECKSUM_MISMATCH")); + return OI_CODEC_SBC_CHECKSUM_MISMATCH; + } + +#ifdef OI_DEBUG + /* + * Make sure the bitpool values are sane. + */ + if ((context->common.frameInfo.bitpool < SBC_MIN_BITPOOL) && !context->common.frameInfo.enhanced) { + ERROR(("Bitpool too small: %d (must be >= 2)", context->common.frameInfo.bitpool)); + return OI_STATUS_INVALID_PARAMETERS; + } + if (context->common.frameInfo.bitpool > OI_SBC_MaxBitpool(&context->common.frameInfo)) { + ERROR(("Bitpool too large: %d (must be <= %ld)", context->common.frameInfo.bitpool, OI_SBC_MaxBitpool(&context->common.frameInfo))); + return OI_STATUS_INVALID_PARAMETERS; + } +#endif + + /* + * Now decode the SBC data. Partial decode is not yet implemented for an SBC + * stream, so pass FALSE to decode body to have it enforce the old rule that + * you have to decode a whole packet at a time. + */ + status = DecodeBody(context, *frameData + SBC_HEADER_LEN, pcmData, pcmBytes, FALSE); + if (OI_SUCCESS(status)) { + *frameData += framelen; + *frameBytes -= framelen; + } + TRACE(("-OI_CODEC_SBC_DecodeFrame: %d", status)); + + return status; +} + +OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes) +{ + OI_STATUS status; + OI_UINT framelen; + OI_UINT headerlen; + OI_UINT8 crc; + + status = FindSyncword(context, frameData, frameBytes); + if (!OI_SUCCESS(status)) { + return status; + } + if (*frameBytes < SBC_HEADER_LEN) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + OI_SBC_ReadHeader(&context->common, *frameData); + framelen = OI_SBC_CalculateFrameAndHeaderlen(&context->common.frameInfo, &headerlen); + if (*frameBytes < headerlen) { + return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA; + } + crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData); + if (crc != context->common.frameInfo.crc) { + return OI_CODEC_SBC_CHECKSUM_MISMATCH; + } + if (*frameBytes < framelen) { + return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA; + } + context->bufferedBlocks = 0; + *frameData += framelen; + *frameBytes -= framelen; + return OI_OK; +} + +OI_UINT8 OI_CODEC_SBC_FrameCount(OI_BYTE *frameData, + OI_UINT32 frameBytes) +{ + OI_UINT8 mode; + OI_UINT8 blocks; + OI_UINT8 subbands; + OI_UINT8 frameCount = 0; + OI_UINT frameLen; + + while (frameBytes) { + while (frameBytes && ((frameData[0] & 0xFE) != 0x9C)) { + frameData++; + frameBytes--; + } + + if (frameBytes < SBC_HEADER_LEN) { + return frameCount; + } + + /* Extract and translate required fields from Header */ + subbands = mode = blocks = frameData[1];; + mode = (mode & (BIT3 | BIT2)) >> 2; + blocks = block_values[(blocks & (BIT5 | BIT4)) >> 4]; + subbands = band_values[(subbands & BIT0)]; + + /* Inline logic to avoid corrupting context */ + frameLen = blocks * frameData[2]; + switch (mode) { + case SBC_JOINT_STEREO: + frameLen += subbands + (8 * subbands); + break; + + case SBC_DUAL_CHANNEL: + frameLen *= 2; + /* fall through */ + + default: + if (mode == SBC_MONO) { + frameLen += 4 * subbands; + } else { + frameLen += 8 * subbands; + } + } + + frameCount++; + frameLen = SBC_HEADER_LEN + (frameLen + 7) / 8; + if (frameBytes > frameLen) { + frameBytes -= frameLen; + frameData += frameLen; + } else { + frameBytes = 0; + } + } + return frameCount; +} + +/** Read quantized subband samples from the input bitstream and expand them. */ + +#ifdef SPECIALIZE_READ_SAMPLES_JOINT + +PRIVATE void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ +#define NROF_SUBBANDS 4 +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +} + +PRIVATE void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ +#define NROF_SUBBANDS 8 +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +} + +typedef void (*READ_SAMPLES)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs); + +static const READ_SAMPLES SpecializedReadSamples[] = { + OI_SBC_ReadSamplesJoint4, + OI_SBC_ReadSamplesJoint8 +}; + +#endif /* SPECIALIZE_READ_SAMPLES_JOINT */ + + +PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs) +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; +#ifdef SPECIALIZE_READ_SAMPLES_JOINT + OI_ASSERT((nrof_subbands >> 3u) <= 1u); + SpecializedReadSamples[nrof_subbands >> 3](context, global_bs); +#else + +#define NROF_SUBBANDS nrof_subbands +#include "readsamplesjoint.inc" +#undef NROF_SUBBANDS +#endif /* SPECIALIZE_READ_SAMPLES_JOINT */ +} + +/**@}*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/dequant.c b/components/ble/ble_stack/sbc/dec/dequant.c new file mode 100644 index 00000000..fc52721a --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/dequant.c @@ -0,0 +1,214 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** + @file + + Dequantizer for SBC decoder; reconstructs quantized representation of subband samples. + + @ingroup codec_internal + */ + +/** +@addtogroup codec_internal +@{ +*/ + +/** + This function is a fixed-point approximation of a modification of the following + dequantization operation defined in the spec, as inferred from section 12.6.4: + + @code + dequant = 2^(scale_factor+1) * ((raw * 2.0 + 1.0) / ((2^bits) - 1) - 1) + + 2 <= bits <= 16 + 0 <= raw < (2^bits)-1 (the -1 is because quantized values with all 1's are forbidden) + + -65535 < dequant < 65535 + @endcode + + The code below computes the dequantized value divided by a scaling constant + equal to about 1.38. This constant is chosen to ensure that the entry in the + dequant_long_scaled table for 16 bits is as accurate as possible, since it has + the least relative precision available to it due to its small magnitude. + + This routine outputs in Q16.15 format. + + The helper array dequant_long is defined as follows: + + @code + dequant_long_long[bits] = round(2^31 * 1/((2^bits - 1) / 1.38...) for 2 <= bits <= 16 + @endcode + + + Additionally, the table entries have the following property: + + @code + dequant_long_scaled[bits] <= 2^31 / ((2^bits - 1)) for 2 <= bits <= 16 + @endcode + + Therefore + + @code + d = 2 * raw + 1 1 <= d <= 2^bits - 2 + + d' = d * dequant_long[bits] + + d * dequant_long_scaled[bits] <= (2^bits - 2) * (2^31 / (2^bits - 1)) + d * dequant_long_scaled[bits] <= 2^31 * (2^bits - 2)/(2^bits - 1) < 2^31 + @endcode + + Therefore, d' doesn't overflow a signed 32-bit value. + + @code + + d' =~ 2^31 * (raw * 2.0 + 1.0) / (2^bits - 1) / 1.38... + + result = d' - 2^31/1.38... =~ 2^31 * ((raw * 2.0 + 1.0) / (2^bits - 1) - 1) / 1.38... + + result is therefore a scaled approximation to dequant. It remains only to + turn 2^31 into 2^(scale_factor+1). Since we're aiming for Q16.15 format, + this is achieved by shifting right by (15-scale_factor): + + (2^31 * x) >> (15-scale_factor) =~ 2^(31-15+scale_factor) * x = 2^15 * 2^(1+scale_factor) * x + @endcode + + */ + +#include + +#if defined(SBC_DEC_INCLUDED) + +#ifndef SBC_DEQUANT_LONG_SCALED_OFFSET +#define SBC_DEQUANT_LONG_SCALED_OFFSET 1555931970 +#endif + +#ifndef SBC_DEQUANT_LONG_UNSCALED_OFFSET +#define SBC_DEQUANT_LONG_UNSCALED_OFFSET 2147483648 +#endif + +#ifndef SBC_DEQUANT_SCALING_FACTOR +#define SBC_DEQUANT_SCALING_FACTOR 1.38019122262781f +#endif + +const OI_UINT32 dequant_long_scaled[17]; +const OI_UINT32 dequant_long_unscaled[17]; + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +#ifdef DEBUG_DEQUANTIZATION + +#include + +static INLINE float dequant_float(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + float result = (1 << (scale_factor + 1)) * ((raw * 2.0f + 1.0f) / ((1 << bits) - 1.0f) - 1.0f); + + result /= SBC_DEQUANT_SCALING_FACTOR; + + /* Unless the encoder screwed up, all correct dequantized values should + * satisfy this inequality. Non-compliant encoders which generate quantized + * values with all 1-bits set can, theoretically, trigger this assert. This + * is unlikely, however, and only an issue in debug mode. + */ + OI_ASSERT(fabs(result) < 32768 * 1.6); + + return result; +} + +#endif + + +INLINE OI_INT32 OI_SBC_Dequant(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + OI_UINT32 d; + OI_INT32 result; + + OI_ASSERT(scale_factor <= 15); + OI_ASSERT(bits <= 16); + + if (bits <= 1) { + return 0; + } + + d = (raw * 2) + 1; + d *= dequant_long_scaled[bits]; + result = d - SBC_DEQUANT_LONG_SCALED_OFFSET; + +#ifdef DEBUG_DEQUANTIZATION + { + OI_INT32 integerized_float_result; + float float_result; + + float_result = dequant_float(raw, scale_factor, bits); + integerized_float_result = (OI_INT32)floor(0.5f + float_result * (1 << 15)); + + /* This detects overflow */ + OI_ASSERT(((result >= 0) && (integerized_float_result >= 0)) || + ((result <= 0) && (integerized_float_result <= 0))); + } +#endif + return result >> (15 - scale_factor); +} + +/* This version of Dequant does not incorporate the scaling factor of 1.38. It + * is intended for use with implementations of the filterbank which are + * hard-coded into a DSP. Output is Q16.4 format, so that after joint stereo + * processing (which leaves the most significant bit equal to the sign bit if + * the encoder is conformant) the result will fit a 24 bit fixed point signed + * value.*/ + +INLINE OI_INT32 OI_SBC_Dequant_Unscaled(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits) +{ + OI_UINT32 d; + OI_INT32 result; + + OI_ASSERT(scale_factor <= 15); + OI_ASSERT(bits <= 16); + + + if (bits <= 1) { + return 0; + } + if (bits == 16) { + result = (raw << 16) + raw - 0x7fff7fff; + return SCALE(result, 24 - scale_factor); + } + + + d = (raw * 2) + 1; + d *= dequant_long_unscaled[bits]; + result = d - 0x80000000; + + return SCALE(result, 24 - scale_factor); +} + +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/framing-sbc.c b/components/ble/ble_stack/sbc/dec/framing-sbc.c new file mode 100644 index 00000000..10b8ef68 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/framing-sbc.c @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ + +#include "oi_codec_sbc_private.h" + +#if defined(SBC_DEC_INCLUDED) + +const OI_CHAR *const OI_CODEC_SBC_FreqText[] = { "SBC_FREQ_16000", "SBC_FREQ_32000", "SBC_FREQ_44100", "SBC_FREQ_48000" }; +const OI_CHAR *const OI_CODEC_SBC_ModeText[] = { "SBC_MONO", "SBC_DUAL_CHANNEL", "SBC_STEREO", "SBC_JOINT_STEREO" }; +const OI_CHAR *const OI_CODEC_SBC_SubbandsText[] = { "SBC_SUBBANDS_4", "SBC_SUBBANDS_8" }; +const OI_CHAR *const OI_CODEC_SBC_BlocksText[] = { "SBC_BLOCKS_4", "SBC_BLOCKS_8", "SBC_BLOCKS_12", "SBC_BLOCKS_16" }; +const OI_CHAR *const OI_CODEC_SBC_AllocText[] = { "SBC_LOUDNESS", "SBC_SNR" }; + +#ifdef OI_DEBUG +void OI_CODEC_SBC_DumpConfig(OI_CODEC_SBC_FRAME_INFO *frameInfo) +{ + BT_WARN("SBC configuration\n"); + BT_WARN(" enhanced: %s\n", frameInfo->enhanced ? "TRUE" : "FALSE"); + BT_WARN(" frequency: %d\n", frameInfo->frequency); + BT_WARN(" subbands: %d\n", frameInfo->nrof_subbands); + BT_WARN(" blocks: %d\n", frameInfo->nrof_blocks); + BT_WARN(" channels: %d\n", frameInfo->nrof_channels); + BT_WARN(" mode: %s\n", OI_CODEC_SBC_ModeText[frameInfo->mode]); + BT_WARN(" alloc: %s\n", OI_CODEC_SBC_AllocText[frameInfo->alloc]); + BT_WARN(" bitpool: %d\n", frameInfo->bitpool); +} +#endif /* OI_DEBUG */ + +/**@}*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/framing.c b/components/ble/ble_stack/sbc/dec/framing.c new file mode 100644 index 00000000..4c9ad103 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/framing.c @@ -0,0 +1,252 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Checksum and header-related functions. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_codec_sbc_private.h" +#include "oi_assert.h" + +#if defined(SBC_DEC_INCLUDED) + +/* asdasd */ + +#define USE_NIBBLEWISE_CRC + +/* #define PRINT_SAMPLES */ +/* #define PRINT_SCALEFACTORS */ +/* #define DEBUG_CRC */ + +/* + * CRC-8 table for X^8 + X^4 + X^3 + X^2 + 1; byte-wise lookup + */ +#ifdef USE_WIDE_CRC +/* Save space if a char is 16 bits, such as on the C54x */ +const OI_BYTE crc8_wide[128] = { + 0x001d, 0x3a27, 0x7469, 0x4e53, 0xe8f5, 0xd2cf, 0x9c81, 0xa6bb, 0xcdd0, 0xf7ea, 0xb9a4, 0x839e, 0x2538, 0x1f02, 0x514c, 0x6b76, 0x879a, 0xbda0, 0xf3ee, 0xc9d4, 0x6f72, 0x5548, 0x1b06, 0x213c, 0x4a57, 0x706d, 0x3e23, 0x0419, 0xa2bf, 0x9885, 0xd6cb, 0xecf1, 0x130e, 0x2934, 0x677a, 0x5d40, 0xfbe6, 0xc1dc, 0x8f92, 0xb5a8, 0xdec3, 0xe4f9, 0xaab7, 0x908d, 0x362b, 0x0c11, 0x425f, 0x7865, 0x9489, 0xaeb3, 0xe0fd, 0xdac7, 0x7c61, 0x465b, 0x0815, 0x322f, 0x5944, 0x637e, 0x2d30, 0x170a, 0xb1ac, 0x8b96, 0xc5d8, 0xffe2, 0x263b, 0x1c01, 0x524f, 0x6875, 0xced3, 0xf4e9, 0xbaa7, 0x809d, 0xebf6, 0xd1cc, 0x9f82, 0xa5b8, 0x031e, 0x3924, 0x776a, 0x4d50, 0xa1bc, 0x9b86, 0xd5c8, 0xeff2, 0x4954, 0x736e, 0x3d20, 0x071a, 0x6c71, 0x564b, 0x1805, 0x223f, 0x8499, 0xbea3, 0xf0ed, 0xcad7, 0x3528, 0x0f12, 0x415c, 0x7b66, 0xddc0, 0xe7fa, 0xa9b4, 0x938e, 0xf8e5, 0xc2df, 0x8c91, 0xb6ab, 0x100d, 0x2a37, 0x6479, 0x5e43, 0xb2af, 0x8895, 0xc6db, 0xfce1, 0x5a47, 0x607d, 0x2e33, 0x1409, 0x7f62, 0x4558, 0x0b16, 0x312c, 0x978a, 0xadb0, 0xe3fe, 0xd9c4, +}; +#elif defined(USE_NIBBLEWISE_CRC) +const OI_BYTE crc8_narrow[16] = { + 0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb +}; +#else +const OI_BYTE crc8_narrow[256] = { + 0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb, 0xcd, 0xd0, 0xf7, 0xea, 0xb9, 0xa4, 0x83, 0x9e, 0x25, 0x38, 0x1f, 0x02, 0x51, 0x4c, 0x6b, 0x76, 0x87, 0x9a, 0xbd, 0xa0, 0xf3, 0xee, 0xc9, 0xd4, 0x6f, 0x72, 0x55, 0x48, 0x1b, 0x06, 0x21, 0x3c, 0x4a, 0x57, 0x70, 0x6d, 0x3e, 0x23, 0x04, 0x19, 0xa2, 0xbf, 0x98, 0x85, 0xd6, 0xcb, 0xec, 0xf1, 0x13, 0x0e, 0x29, 0x34, 0x67, 0x7a, 0x5d, 0x40, 0xfb, 0xe6, 0xc1, 0xdc, 0x8f, 0x92, 0xb5, 0xa8, 0xde, 0xc3, 0xe4, 0xf9, 0xaa, 0xb7, 0x90, 0x8d, 0x36, 0x2b, 0x0c, 0x11, 0x42, 0x5f, 0x78, 0x65, 0x94, 0x89, 0xae, 0xb3, 0xe0, 0xfd, 0xda, 0xc7, 0x7c, 0x61, 0x46, 0x5b, 0x08, 0x15, 0x32, 0x2f, 0x59, 0x44, 0x63, 0x7e, 0x2d, 0x30, 0x17, 0x0a, 0xb1, 0xac, 0x8b, 0x96, 0xc5, 0xd8, 0xff, 0xe2, 0x26, 0x3b, 0x1c, 0x01, 0x52, 0x4f, 0x68, 0x75, 0xce, 0xd3, 0xf4, 0xe9, 0xba, 0xa7, 0x80, 0x9d, 0xeb, 0xf6, 0xd1, 0xcc, 0x9f, 0x82, 0xa5, 0xb8, 0x03, 0x1e, 0x39, 0x24, 0x77, 0x6a, 0x4d, 0x50, 0xa1, 0xbc, 0x9b, 0x86, 0xd5, 0xc8, 0xef, 0xf2, 0x49, 0x54, 0x73, 0x6e, 0x3d, 0x20, 0x07, 0x1a, 0x6c, 0x71, 0x56, 0x4b, 0x18, 0x05, 0x22, 0x3f, 0x84, 0x99, 0xbe, 0xa3, 0xf0, 0xed, 0xca, 0xd7, 0x35, 0x28, 0x0f, 0x12, 0x41, 0x5c, 0x7b, 0x66, 0xdd, 0xc0, 0xe7, 0xfa, 0xa9, 0xb4, 0x93, 0x8e, 0xf8, 0xe5, 0xc2, 0xdf, 0x8c, 0x91, 0xb6, 0xab, 0x10, 0x0d, 0x2a, 0x37, 0x64, 0x79, 0x5e, 0x43, 0xb2, 0xaf, 0x88, 0x95, 0xc6, 0xdb, 0xfc, 0xe1, 0x5a, 0x47, 0x60, 0x7d, 0x2e, 0x33, 0x14, 0x09, 0x7f, 0x62, 0x45, 0x58, 0x0b, 0x16, 0x31, 0x2c, 0x97, 0x8a, 0xad, 0xb0, 0xe3, 0xfe, 0xd9, 0xc4 +}; +#endif +const OI_UINT32 dequant_long_scaled[17] = { + 0, + 0, + 0x1ee9e116, /* bits=2 0.24151243 1/3 * (1/1.38019122262781) (0x00000008)*/ + 0x0d3fa99c, /* bits=3 0.10350533 1/7 * (1/1.38019122262781) (0x00000013)*/ + 0x062ec69e, /* bits=4 0.04830249 1/15 * (1/1.38019122262781) (0x00000029)*/ + 0x02fddbfa, /* bits=5 0.02337217 1/31 * (1/1.38019122262781) (0x00000055)*/ + 0x0178d9f5, /* bits=6 0.01150059 1/63 * (1/1.38019122262781) (0x000000ad)*/ + 0x00baf129, /* bits=7 0.00570502 1/127 * (1/1.38019122262781) (0x0000015e)*/ + 0x005d1abe, /* bits=8 0.00284132 1/255 * (1/1.38019122262781) (0x000002bf)*/ + 0x002e760d, /* bits=9 0.00141788 1/511 * (1/1.38019122262781) (0x00000582)*/ + 0x00173536, /* bits=10 0.00070825 1/1023 * (1/1.38019122262781) (0x00000b07)*/ + 0x000b9928, /* bits=11 0.00035395 1/2047 * (1/1.38019122262781) (0x00001612)*/ + 0x0005cc37, /* bits=12 0.00017693 1/4095 * (1/1.38019122262781) (0x00002c27)*/ + 0x0002e604, /* bits=13 0.00008846 1/8191 * (1/1.38019122262781) (0x00005852)*/ + 0x000172fc, /* bits=14 0.00004422 1/16383 * (1/1.38019122262781) (0x0000b0a7)*/ + 0x0000b97d, /* bits=15 0.00002211 1/32767 * (1/1.38019122262781) (0x00016150)*/ + 0x00005cbe, /* bits=16 0.00001106 1/65535 * (1/1.38019122262781) (0x0002c2a5)*/ +}; + + +const OI_UINT32 dequant_long_unscaled[17] = { + 0, + 0, + 0x2aaaaaab, /* bits=2 0.33333333 1/3 (0x00000005)*/ + 0x12492492, /* bits=3 0.14285714 1/7 (0x0000000e)*/ + 0x08888889, /* bits=4 0.06666667 1/15 (0x0000001d)*/ + 0x04210842, /* bits=5 0.03225806 1/31 (0x0000003e)*/ + 0x02082082, /* bits=6 0.01587302 1/63 (0x0000007e)*/ + 0x01020408, /* bits=7 0.00787402 1/127 (0x000000fe)*/ + 0x00808081, /* bits=8 0.00392157 1/255 (0x000001fd)*/ + 0x00402010, /* bits=9 0.00195695 1/511 (0x000003fe)*/ + 0x00200802, /* bits=10 0.00097752 1/1023 (0x000007fe)*/ + 0x00100200, /* bits=11 0.00048852 1/2047 (0x00000ffe)*/ + 0x00080080, /* bits=12 0.00024420 1/4095 (0x00001ffe)*/ + 0x00040020, /* bits=13 0.00012209 1/8191 (0x00003ffe)*/ + 0x00020008, /* bits=14 0.00006104 1/16383 (0x00007ffe)*/ + 0x00010002, /* bits=15 0.00003052 1/32767 (0x0000fffe)*/ + 0x00008001, /* bits=16 0.00001526 1/65535 (0x0001fffc)*/ +}; + +#if defined(OI_DEBUG) || defined(PRINT_SAMPLES) || defined(PRINT_SCALEFACTORS) +#include +#endif + +#ifdef USE_WIDE_CRC +static INLINE OI_CHAR crc_iterate(OI_UINT8 oldcrc, OI_UINT8 next) +{ + OI_UINT crc; + OI_UINT idx; + idx = oldcrc ^ next; + crc = crc8_wide[idx >> 1]; + if (idx % 2) { + crc &= 0xff; + } else { + crc >>= 8; + } + + return crc; +} + +static INLINE OI_CHAR crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next) +{ + OI_UINT crc; + OI_UINT idx; + idx = (oldcrc ^ next) >> 4; + crc = crc8_wide[idx >> 1]; + if (idx % 2) { + crc &= 0xff; + } else { + crc >>= 8; + } + + return (oldcrc << 4) ^ crc; +} + +#else // USE_WIDE_CRC + +static INLINE OI_UINT8 crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next) +{ + return (oldcrc << 4) ^ crc8_narrow[(oldcrc ^ next) >> 4]; +} + +#ifdef USE_NIBBLEWISE_CRC +static INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next) +{ + crc = (crc << 4) ^ crc8_narrow[(crc ^ next) >> 4]; + crc = (crc << 4) ^ crc8_narrow[((crc >> 4)^next) & 0xf]; + + return crc; +} + +#else // USE_NIBBLEWISE_CRC +static INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next) +{ + return crc8_narrow[crc ^ next]; +} + +#endif // USE_NIBBLEWISE_CRC + +#endif // USE_WIDE_CRC + + +PRIVATE OI_UINT8 OI_SBC_CalculateChecksum(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data) +{ + OI_UINT i; + OI_UINT8 crc = 0x0f; + /* Count is the number of whole bytes subject to CRC. Actually, it's one + * more than this number, because data[3] is the CRC field itself, which is + * explicitly skipped. Since crc_iterate (should be) inlined, it's cheaper + * spacewise to include the check in the loop. This shouldn't be much of a + * bottleneck routine in the first place. */ + OI_UINT count = (frame->nrof_subbands * frame->nrof_channels / 2u) + 4; + + if (frame->mode == SBC_JOINT_STEREO && frame->nrof_subbands == 8) { + count++; + } + + for (i = 1; i < count; i++) { + if (i != 3) { + crc = crc_iterate(crc, data[i]); + } + } + + if (frame->mode == SBC_JOINT_STEREO && frame->nrof_subbands == 4) { + crc = crc_iterate_top4(crc, data[i]); + } + + return crc; +} + +void OI_SBC_ExpandFrameFields(OI_CODEC_SBC_FRAME_INFO *frame) +{ + frame->nrof_blocks = block_values[frame->blocks]; + frame->nrof_subbands = band_values[frame->subbands]; + + frame->frequency = freq_values[frame->freqIndex]; + frame->nrof_channels = channel_values[frame->mode]; +} + +/** + * Unrolled macro to copy 4 32-bit aligned 32-bit values backward in memory + */ +#define COPY4WORDS_BACK(_dest, _src) \ + do { \ + OI_INT32 _a, _b, _c, _d; \ + _a = *--_src; \ + _b = *--_src; \ + _c = *--_src; \ + _d = *--_src; \ + *--_dest = _a; \ + *--_dest = _b; \ + *--_dest = _c; \ + *--_dest = _d; \ + } while (0) + + +#if defined(USE_PLATFORM_MEMMOVE) || defined(USE_PLATFORM_MEMCPY) +#include +#endif +PRIVATE void shift_buffer(SBC_BUFFER_T *dest, SBC_BUFFER_T *src, OI_UINT wordCount) +{ +#ifdef USE_PLATFORM_MEMMOVE + memmove(dest, src, wordCount * sizeof(SBC_BUFFER_T)); +#elif defined(USE_PLATFORM_MEMCPY) + OI_ASSERT(((OI_CHAR *)(dest) - (OI_CHAR *)(src)) >= wordCount * sizeof(*dest)); + memcpy(dest, src, wordCount * sizeof(SBC_BUFFER_T)); +#else + OI_UINT n; + OI_INT32 *d; + OI_INT32 *s; + n = wordCount / 4 / (sizeof(OI_INT32) / sizeof(*dest)); + OI_ASSERT((n * 4 * (sizeof(OI_INT32) / sizeof(*dest))) == wordCount); + + d = (void *)(dest + wordCount); + s = (void *)(src + wordCount); + + do { + COPY4WORDS_BACK(d, s); + } while (--n); +#endif +} +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/oi_assert.h b/components/ble/ble_stack/sbc/dec/oi_assert.h new file mode 100644 index 00000000..2f20f3a0 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_assert.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_ASSERT_H +#define _OI_ASSERT_H +/** @file + This file provides macros and functions for compile-time and run-time assertions. + + When the OI_DEBUG preprocessor value is defined, the macro OI_ASSERT is compiled into + the program, providing for a runtime assertion failure check. + C_ASSERT is a macro that can be used to perform compile time checks. +*/ +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** \addtogroup Debugging Debugging APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef OI_DEBUG + +/** The macro OI_ASSERT takes a condition argument. If the asserted condition + does not evaluate to true, the OI_ASSERT macro calls the host-dependent function, + OI_AssertFail(), which reports the failure and generates a runtime error. +*/ +void OI_AssertFail(char *file, int line, char *reason); + + +#define OI_ASSERT(condition) \ + { if (!(condition)) OI_AssertFail(__FILE__, __LINE__, #condition); } + +#define OI_ASSERT_FAIL(msg) \ + { OI_AssertFail(__FILE__, __LINE__, msg); } + +#else + + +#define OI_ASSERT(condition) +#define OI_ASSERT_FAIL(msg) + +#endif + + +/** + C_ASSERT() can be used to perform many compile-time assertions: type sizes, field offsets, etc. + An assertion failure results in compile time error C2118: negative subscript. + Unfortunately, this elegant macro doesn't work with GCC, so it's all commented out + for now. Perhaps later..... +*/ + +#ifndef C_ASSERT +// #define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +// #define C_ASSERT(e) +#endif + + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_ASSERT_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_bitstream.h b/components/ble/ble_stack/sbc/dec/oi_bitstream.h new file mode 100644 index 00000000..c6ce59b4 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_bitstream.h @@ -0,0 +1,123 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_BITSTREAM_H +#define _OI_BITSTREAM_H + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** +@file +Function prototypes and macro definitions for manipulating input and output +bitstreams. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#include "oi_codec_sbc_private.h" +#include "oi_stddefs.h" + +INLINE void OI_BITSTREAM_ReadInit(OI_BITSTREAM *bs, const OI_BYTE *buffer); + +INLINE void OI_BITSTREAM_WriteInit(OI_BITSTREAM *bs, OI_BYTE *buffer); + +INLINE OI_UINT32 OI_BITSTREAM_ReadUINT(OI_BITSTREAM *bs, OI_UINT bits); + +INLINE OI_UINT8 OI_BITSTREAM_ReadUINT4Aligned(OI_BITSTREAM *bs); + +INLINE OI_UINT8 OI_BITSTREAM_ReadUINT8Aligned(OI_BITSTREAM *bs); + +INLINE void OI_BITSTREAM_WriteUINT(OI_BITSTREAM *bs, + OI_UINT16 value, + OI_UINT bits); + +/* + * Use knowledge that the bitstream is aligned to optimize the write of a byte + */ +PRIVATE void OI_BITSTREAM_WriteUINT8Aligned(OI_BITSTREAM *bs, + OI_UINT8 datum); + +/* + * Use knowledge that the bitstream is aligned to optimize the write pair of nibbles + */ +PRIVATE void OI_BITSTREAM_Write2xUINT4Aligned(OI_BITSTREAM *bs, + OI_UINT8 datum1, + OI_UINT8 datum2); + +/** Internally the bitstream looks ahead in the stream. When + * OI_SBC_ReadScalefactors() goes to temporarily break the abstraction, it will + * need to know where the "logical" pointer is in the stream. + */ +#define OI_BITSTREAM_GetWritePtr(bs) ((bs)->ptr.w - 3) +#define OI_BITSTREAM_GetReadPtr(bs) ((bs)->ptr.r - 3) + +/** This is declared here as a macro because decoder.c breaks the bitsream + * encapsulation for efficiency reasons. + */ +#define OI_BITSTREAM_READUINT(result, bits, ptr, value, bitPtr) \ +do { \ + OI_ASSERT((bits) <= 16); \ + OI_ASSERT((bitPtr) < 16); \ + OI_ASSERT((bitPtr) >= 8); \ + \ + result = (value) << (bitPtr); \ + result >>= 32 - (bits); \ + \ + bitPtr += (bits); \ + while (bitPtr >= 16) { \ + value = ((value) << 8) | *ptr++; \ + bitPtr -= 8; \ + } \ + OI_ASSERT((bits == 0) || (result < (1u << (bits)))); \ +} while (0) + + +#define OI_BITSTREAM_WRITEUINT(ptr, value, bitPtr, datum, bits) \ +do {\ + bitPtr -= bits;\ + value |= datum << bitPtr;\ + \ + while (bitPtr <= 16) {\ + bitPtr += 8;\ + *ptr++ = (OI_UINT8)(value >> 24);\ + value <<= 8;\ + }\ +} while (0) + +#define OI_BITSTREAM_WRITEFLUSH(ptr, value, bitPtr) \ +do {\ + while (bitPtr < 32) {\ + bitPtr += 8;\ + *ptr++ = (OI_UINT8)(value >> 24);\ + value <<= 8;\ + }\ +} while (0) + +/** +@} +*/ + +#endif /* _OI_BITSTREAM_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_bt_spec.h b/components/ble/ble_stack/sbc/dec/oi_bt_spec.h new file mode 100644 index 00000000..b98a5821 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_bt_spec.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_BT_SPEC_H +#define _OI_BT_SPEC_H +/** + * @file + * + * This file contains common definitions from the Bluetooth specification. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** The maximum number of active slaves in a piconet. */ +#define OI_BT_MAX_ACTIVE_SLAVES 7 + +/** the number of bytes in a Bluetooth device address (BD_ADDR) */ +#define OI_BD_ADDR_BYTE_SIZE 6 + +/** + * 48-bit Bluetooth device address + * + * Because 48-bit integers may not be supported on all platforms, the + * address is defined as an array of bytes. This array is big-endian, + * meaning that + * - array[0] contains bits 47-40, + * - array[1] contains bits 39-32, + * - array[2] contains bits 31-24, + * - array[3] contains bits 23-16, + * - array[4] contains bits 15-8, and + * - array[5] contains bits 7-0. + */ +typedef struct { + OI_UINT8 addr[OI_BD_ADDR_BYTE_SIZE] ; /**< Bluetooth device address represented as an array of 8-bit values */ +} OI_BD_ADDR ; + +/** + * @name Data types for working with UUIDs + * UUIDs are 16 bytes (128 bits). + * + * To avoid having to pass around 128-bit values all the time, 32-bit and 16-bit + * UUIDs are defined, along with a mapping from the shorter versions to the full + * version. + * + * @{ + */ + +/** + * 16-bit representation of a 128-bit UUID + */ +typedef OI_UINT16 OI_UUID16; + +/** + * 32-bit representation of a 128-bit UUID + */ +typedef OI_UINT32 OI_UUID32; + +/** + * number of bytes in a 128 bit UUID + */ +#define OI_BT_UUID128_SIZE 16 + +/** + * number of bytes in IPv6 style addresses + */ +#define OI_BT_IPV6ADDR_SIZE 16 + +/** + * type definition for a 128-bit UUID + * + * To simplify conversion between 128-bit UUIDs and 16-bit and 32-bit UUIDs, + * the most significant 32 bits are stored with the same endian-ness as is + * native on the target (local) device. The remainder of the 128-bit UUID is + * stored as bytes in big-endian order. + */ +typedef struct { + OI_UINT32 ms32bits; /**< most significant 32 bits of 128-bit UUID */ + OI_UINT8 base[OI_BT_UUID128_SIZE - sizeof(OI_UINT32)]; /**< remainder of 128-bit UUID, array of 8-bit values */ +} OI_UUID128; + +/** @} */ + +/** number of bytes in a link key */ +#define OI_BT_LINK_KEY_SIZE 16 + +/** + * type definition for a baseband link key + * + * Because 128-bit integers may not be supported on all platforms, we define + * link keys as an array of bytes. Unlike the Bluetooth device address, + * the link key is stored in little-endian order, meaning that + * - array[0] contains bits 0 - 7, + * - array[1] contains bits 8 - 15, + * - array[2] contains bits 16 - 23, + * - array[3] contains bits 24 - 31, + * - array[4] contains bits 32 - 39, + * - array[5] contains bits 40 - 47, + * - array[6] contains bits 48 - 55, + * - array[7] contains bits 56 - 63, + * - array[8] contains bits 64 - 71, + * - array[9] contains bits 72 - 79, + * - array[10] contains bits 80 - 87, + * - array[11] contains bits 88 - 95, + * - array[12] contains bits 96 - 103, + * - array[13] contains bits 104- 111, + * - array[14] contains bits 112- 119, and + * - array[15] contains bits 120- 127. + */ +typedef struct { + OI_UINT8 key[OI_BT_LINK_KEY_SIZE] ; /**< link key represented as an array of 8-bit values */ +} OI_LINK_KEY ; + + +/** Out-of-band data size - C and R values are 16-bytes each */ +#define OI_BT_OOB_NUM_BYTES 16 + +typedef struct { + OI_UINT8 value[OI_BT_OOB_NUM_BYTES] ; /**< same struct used for C and R values */ +} OI_OOB_DATA ; + + +/** + * link key types + */ +typedef enum { + OI_LINK_KEY_TYPE_COMBO = 0, /**< combination key */ + OI_LINK_KEY_TYPE_LOCAL_UNIT = 1, /**< local unit key */ + OI_LINK_KEY_TYPE_REMOTE_UNIT = 2, /**< remote unit key */ + OI_LINK_KEY_TYPE_DEBUG_COMBO = 3, /**< debug combination key */ + OI_LINK_KEY_TYPE_UNAUTHENTICATED = 4, /**< Unauthenticated */ + OI_LINK_KEY_TYPE_AUTHENTICATED = 5, /**< Authenticated */ + OI_LINK_KEY_TYPE_CHANGED_COMBO = 6 /**< Changed */ + +} OI_BT_LINK_KEY_TYPE ; + + +/** amount of space allocated for a PIN (personal indentification number) in bytes */ +#define OI_BT_PIN_CODE_SIZE 16 + +/** data type for a PIN (PINs are treated as strings, so endianness does not apply.) */ +typedef struct { + OI_UINT8 pin[OI_BT_PIN_CODE_SIZE] ; /**< PIN represented as an array of 8-bit values */ +} OI_PIN_CODE ; + +/** maximum number of SCO connections per device, which is 3 as of version 2.0+EDR + of the Bluetooth specification (see sec 4.3 of vol 2 part B) */ +#define OI_BT_MAX_SCO_CONNECTIONS 3 + +/** data type for clock offset */ +typedef OI_UINT16 OI_BT_CLOCK_OFFSET ; + +/** data type for a LM handle */ +typedef OI_UINT16 OI_HCI_LM_HANDLE; + +/** opaque data type for a SCO or ACL connection handle */ +typedef struct _OI_HCI_CONNECTION *OI_HCI_CONNECTION_HANDLE; + +/** data type for HCI Error Code, as defined in oi_hcispec.h */ +typedef OI_UINT8 OI_HCI_ERROR_CODE ; + +/** + * The Bluetooth device type is indicated by a 24-bit bitfield, represented as a + * 32-bit number in the stack. The bit layout and values for device class are specified + * in the file oi_bt_assigned_nos.h and in the Bluetooth "Assigned Numbers" specification + * at http://www.bluetooth.org/assigned-numbers/. + */ +typedef OI_UINT32 OI_BT_DEVICE_CLASS ; + +#define OI_BT_DEV_CLASS_FORMAT_MASK 0x000003 /**< Bits 0-1 contain format type. */ +#define OI_BT_DEV_CLASS_MINOR_DEVICE_MASK 0x0000FC /**< Bits 2-7 contain minor device class value. */ +#define OI_BT_DEV_CLASS_MAJOR_DEVICE_MASK 0x001F00 /**< Bits 8-12 contain major device class value. */ +#define OI_BT_DEV_CLASS_MAJOR_SERVICE_MASK 0xFFE000 /**< Bits 13-23 contain major service class value. */ + +/** There is currently only one device class format defined, type 00. */ +#define OI_BT_DEV_CLASS_FORMAT_TYPE 00 + +/** Bit 13 in device class indicates limited discoverability mode (GAP v2.0+EDR, section 4.1.2.2) */ +#define OI_BT_DEV_CLASS_LIMITED_DISCO_BIT BIT13 + +/** macro to test validity of the Device Class Format */ +#define OI_BT_VALID_DEVICE_CLASS_FORMAT(class) (OI_BT_DEV_CLASS_FORMAT_TYPE == ((class) & OI_BT_DEV_CLASS_FORMAT_MASK)) + +/** the time between baseband clock ticks, currently 625 microseconds (one slot) */ +#define OI_BT_TICK 625 +/** some macros to convert to/from baseband clock ticks - use no floating point! */ +#define OI_SECONDS_TO_BT_TICKS(secs) ((secs)*1600) +#define OI_BT_TICKS_TO_SECONDS(ticks) ((ticks)/1600) +#define OI_MSECS_TO_BT_TICKS(msecs) (((msecs)*8)/5) +#define OI_BT_TICKS_TO_MSECS(ticks) (((ticks)*5)/8) + +/** EIR byte order */ +#define OI_EIR_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/*****************************************************************************/ +#endif /* _OI_BT_SPEC_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h b/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h new file mode 100644 index 00000000..495fbf6b --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h @@ -0,0 +1,488 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#ifndef _OI_CODEC_SBC_CORE_H +#define _OI_CODEC_SBC_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** +@file +Declarations of codec functions, data types, and macros. + +@ingroup codec_lib +*/ + +/** +@addtogroup codec_lib +@{ +*/ + +/* Non-BM3 users of of the codec must include oi_codec_sbc_bm3defs.h prior to + * including this file, or else these includes will fail because the BM3 SDK is + * not in the include path */ +#ifndef _OI_CODEC_SBC_BM3DEFS_H +#include "oi_stddefs.h" +#include "oi_status.h" +#endif + +#include + +#define SBC_MAX_CHANNELS 2 +#define SBC_MAX_BANDS 8 +#define SBC_MAX_BLOCKS 16 +#define SBC_MIN_BITPOOL 2 /**< Minimum size of the bit allocation pool used to encode the stream */ +#define SBC_MAX_BITPOOL 250 /**< Maximum size of the bit allocation pool used to encode the stream */ +#define SBC_MAX_ONE_CHANNEL_BPS 320000 +#define SBC_MAX_TWO_CHANNEL_BPS 512000 + + +#define SBC_WBS_BITRATE 62000 +#define SBC_WBS_BITPOOL 27 +#define SBC_WBS_NROF_BLOCKS 16 +#define SBC_WBS_FRAME_LEN 62 +#define SBC_WBS_SAMPLES_PER_FRAME 128 + + +#define SBC_HEADER_LEN 4 +#define SBC_MAX_FRAME_LEN (SBC_HEADER_LEN + \ + ((SBC_MAX_BANDS * SBC_MAX_CHANNELS / 2) + \ + (SBC_MAX_BANDS + SBC_MAX_BLOCKS * SBC_MAX_BITPOOL + 7)/8)) +#define SBC_MAX_SAMPLES_PER_FRAME (SBC_MAX_BANDS * SBC_MAX_BLOCKS) + +#define SBC_MAX_SCALEFACTOR_BYTES ((4*(SBC_MAX_CHANNELS * SBC_MAX_BANDS) + 7)/8) + +#define OI_SBC_SYNCWORD 0x9c +#define OI_SBC_ENHANCED_SYNCWORD 0x9d +#define OI_mSBC_SYNCWORD 0xad + +#define OI_SBC_MODE_STD 0 +#define OI_SBC_MODE_MSBC 1 + +/**@name Sampling frequencies */ +/**@{*/ +#define SBC_FREQ_16000 0 /**< The sampling frequency is 16 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_32000 1 /**< The sampling frequency is 32 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_44100 2 /**< The sampling frequency is 44.1 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_FREQ_48000 3 /**< The sampling frequency is 48 kHz. One possible value for the @a frequency parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Channel modes */ +/**@{*/ +#define SBC_MONO 0 /**< The mode of the encoded channel is mono. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_DUAL_CHANNEL 1 /**< The mode of the encoded channel is dual-channel. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_STEREO 2 /**< The mode of the encoded channel is stereo. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_JOINT_STEREO 3 /**< The mode of the encoded channel is joint stereo. One possible value for the @a mode parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Subbands */ +/**@{*/ +#define SBC_SUBBANDS_4 0 /**< The encoded stream has 4 subbands. One possible value for the @a subbands parameter of OI_CODEC_SBC_EncoderConfigure()*/ +#define SBC_SUBBANDS_8 1 /**< The encoded stream has 8 subbands. One possible value for the @a subbands parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Block lengths */ +/**@{*/ +#define SBC_BLOCKS_4 0 /**< A block size of 4 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_8 1 /**< A block size of 8 blocks was used to encode the stream is. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_12 2 /**< A block size of 12 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_BLOCKS_16 3 /**< A block size of 16 blocks was used to encode the stream. One possible value for the @a blocks parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/**@name Bit allocation methods */ +/**@{*/ +#define SBC_LOUDNESS 0 /**< The bit allocation method. One possible value for the @a loudness parameter of OI_CODEC_SBC_EncoderConfigure() */ +#define SBC_SNR 1 /**< The bit allocation method. One possible value for the @a loudness parameter of OI_CODEC_SBC_EncoderConfigure() */ +/**@}*/ + +/** +@} + +@addtogroup codec_internal +@{ +*/ + +typedef OI_INT16 SBC_BUFFER_T; + + +/** Used internally. */ +typedef struct { + OI_UINT16 frequency; /**< The sampling frequency. Input parameter. */ + OI_UINT8 freqIndex; + + OI_UINT8 nrof_blocks; /**< The block size used to encode the stream. Input parameter. */ + OI_UINT8 blocks; + + + OI_UINT8 nrof_subbands; /**< The number of subbands of the encoded stream. Input parameter. */ + OI_UINT8 subbands; + + OI_UINT8 mode; /**< The mode of the encoded channel. Input parameter. */ + OI_UINT8 nrof_channels; /**< The number of channels of the encoded stream. */ + + OI_UINT8 alloc; /**< The bit allocation method. Input parameter. */ + OI_UINT8 bitpool; /**< Size of the bit allocation pool used to encode the stream. Input parameter. */ + OI_UINT8 crc; /**< Parity check byte used for error detection. */ + OI_UINT8 join; /**< Whether joint stereo has been used. */ + OI_UINT8 enhanced; + OI_UINT8 min_bitpool; /**< This value is only used when encoding. SBC_MAX_BITPOOL if variable + bitpools are disallowed, otherwise the minimum bitpool size that will + be used by the bit allocator. */ + + OI_UINT8 cachedInfo; /**< Information about the previous frame */ +} OI_CODEC_SBC_FRAME_INFO; + +/** Used internally. */ +typedef struct { + const OI_CHAR *codecInfo; + OI_CODEC_SBC_FRAME_INFO frameInfo; + OI_INT8 scale_factor[SBC_MAX_CHANNELS * SBC_MAX_BANDS]; + OI_UINT32 frameCount; + OI_INT32 *subdata; + + SBC_BUFFER_T *filterBuffer[SBC_MAX_CHANNELS]; + OI_INT32 filterBufferLen; + OI_UINT filterBufferOffset; + + union { + OI_UINT8 uint8[SBC_MAX_CHANNELS * SBC_MAX_BANDS]; + OI_UINT32 uint32[SBC_MAX_CHANNELS * SBC_MAX_BANDS / 4]; + } bits; + OI_UINT8 maxBitneed; /**< Running maximum bitneed */ + OI_BYTE formatByte; + OI_UINT8 pcmStride; + OI_UINT8 maxChannels; +} OI_CODEC_SBC_COMMON_CONTEXT; + + +/* + * A smaller value reduces RAM usage at the expense of increased CPU usage. Values in the range + * 27..50 are recommended, beyond 50 there is a diminishing return on reduced CPU usage. + */ +#define SBC_CODEC_MIN_FILTER_BUFFERS 16 +#define SBC_CODEC_FAST_FILTER_BUFFERS 27 + +/* Expands to the number of OI_UINT32s needed to ensure enough memory to encode + * or decode streams of numChannels channels, using numBuffers buffers. + * Example: + * OI_UINT32 decoderData[CODEC_DATA_WORDS(SBC_MAX_CHANNELS, SBC_DECODER_FAST_SYNTHESIS_BUFFERS)]; + * */ +#define CODEC_DATA_WORDS(numChannels, numBuffers) \ + ((\ + (sizeof(OI_INT32) * SBC_MAX_BLOCKS * numChannels * SBC_MAX_BANDS) \ + + (sizeof(SBC_BUFFER_T) * SBC_MAX_CHANNELS * SBC_MAX_BANDS * numBuffers) \ + + (sizeof (OI_UINT32) - 1) \ + ) / sizeof(OI_UINT32)) + +/** Opaque parameter to decoding functions; maintains decoder context. */ +typedef struct { + OI_CODEC_SBC_COMMON_CONTEXT common; + OI_UINT8 limitFrameFormat; /* Boolean, set by OI_CODEC_SBC_DecoderLimit() */ + OI_UINT8 restrictSubbands; + OI_UINT8 enhancedEnabled; + OI_UINT8 bufferedBlocks; + OI_UINT8 sbc_mode; /* OI_SBC_MODE_STD or OI_SBC_MODE_MSBC */ +} OI_CODEC_SBC_DECODER_CONTEXT; + +typedef struct { + OI_UINT32 data[CODEC_DATA_WORDS(1, SBC_CODEC_FAST_FILTER_BUFFERS)]; +} OI_CODEC_SBC_CODEC_DATA_MONO; + +typedef struct { + OI_UINT32 data[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)]; +} OI_CODEC_SBC_CODEC_DATA_STEREO; + +/** +@} + +@addtogroup codec_lib +@{ +*/ + +/** + * This function resets the decoder. The context must be reset when + * changing streams, or if the following stream parameters change: + * number of subbands, stereo mode, or frequency. + * + * @param context Pointer to the decoder context structure to be reset. + * + * @param enhanced If true, enhanced SBC operation is enabled. If enabled, + * the codec will recognize the alternative syncword for + * decoding an enhanced SBC stream. Enhancements should not + * be enabled unless the stream is known to be generated + * by an enhanced encoder, or there is a small possibility + * for decoding glitches if synchronization were to be lost. + */ +OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable); + +/** + * This function restricts the kind of SBC frames that the Decoder will + * process. Its use is optional. If used, it must be called after + * calling OI_CODEC_SBC_DecoderReset(). After it is called, any calls + * to OI_CODEC_SBC_DecodeFrame() with SBC frames that do not conform + * to the Subband and Enhanced SBC setting will be rejected with an + * OI_STATUS_INVALID_PARAMETERS return. + * + * @param context Pointer to the decoder context structure to be limited. + * + * @param enhanced If true, all frames passed to the decoder must be + * Enhanced SBC frames. If false, all frames must be + * standard SBC frames. + * + * @param subbands May be set to SBC_SUBBANDS_4 or SBC_SUBBANDS_8. All + * frames passed to the decoder must be encoded with + * the requested number of subbands. + * + */ +OI_STATUS OI_CODEC_SBC_DecoderLimit(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 subbands); + +/** + * This function sets the decoder parameters for a raw decode where the decoder parameters are not + * available in the sbc data stream. OI_CODEC_SBC_DecoderReset must be called + * prior to calling this function. + * + * @param context Decoder context structure. This must be the context must be + * used each time a frame is decoded. + * + * @param enhanced Set to TRUE to enable Qualcomm proprietary + * quality enhancements. + * + * @param frequency One of SBC_FREQ_16000, SBC_FREQ_32000, SBC_FREQ_44100, + * SBC_FREQ_48000 + * + * @param mode One of SBC_MONO, SBC_DUAL_CHANNEL, SBC_STEREO, + * SBC_JOINT_STEREO + * + * @param subbands One of SBC_SUBBANDS_4, SBC_SUBBANDS_8 + * + * @param blocks One of SBC_BLOCKS_4, SBC_BLOCKS_8, SBC_BLOCKS_12, + * SBC_BLOCKS_16 + * + * @param alloc One of SBC_LOUDNESS, SBC_SNR + * + * @param maxBitpool The maximum bitpool size for this context + */ +OI_STATUS OI_CODEC_SBC_DecoderConfigureRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_BOOL enhanced, + OI_UINT8 frequency, + OI_UINT8 mode, + OI_UINT8 subbands, + OI_UINT8 blocks, + OI_UINT8 alloc, + OI_UINT8 maxBitpool); + +/** + * Decode one SBC frame. The frame has no header bytes. The context must have been previously + * initialized by calling OI_CODEC_SBC_DecoderConfigureRaw(). + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param bitpool The actual bitpool size for this frame. Must be <= the maxbitpool specified + * in the call to OI_CODEC_SBC_DecoderConfigureRaw(), + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + * @param pcmData Address of an array of OI_INT16 pairs, which will be + * populated with the decoded audio data. This address + * is not updated. + * + * @param pcmBytes Pointer to a UINT32 in/out parameter. On input, it + * should contain the number of bytes available for pcm + * data. On output, it will contain the number of bytes + * written. Note that this differs from the semantics of + * frameBytes. + */ +OI_STATUS OI_CODEC_SBC_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +/** + * Decode one SBC frame. + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + * @param pcmData Address of an array of OI_INT16 pairs, which will be + * populated with the decoded audio data. This address + * is not updated. + * + * @param pcmBytes Pointer to a UINT32 in/out parameter. On input, it + * should contain the number of bytes available for pcm + * data. On output, it will contain the number of bytes + * written. Note that this differs from the semantics of + * frameBytes. + */ +OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +/** + * Calculate the number of SBC frames but don't decode. CRC's are not checked, + * but the Sync word is found prior to count calculation. + * + * @param frameData Pointer to the SBC data. + * + * @param frameBytes Number of bytes avaiable in the frameData buffer + * + */ +OI_UINT8 OI_CODEC_SBC_FrameCount(OI_BYTE *frameData, + OI_UINT32 frameBytes); + +/** + * Analyze an SBC frame but don't do the decode. + * + * @param context Pointer to a decoder context structure. The same context + * must be used each time when decoding from the same stream. + * + * @param frameData Address of a pointer to the SBC data to decode. This + * value will be updated to point to the next frame after + * successful decoding. + * + * @param frameBytes Pointer to a UINT32 containing the number of available + * bytes of frame data. This value will be updated to reflect + * the number of bytes remaining after a decoding operation. + * + */ +OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes); + +/* Common functions */ + +/** + Calculate the frame length. + + @param frame The frame whose length to calculate + + @return the length of an individual encoded frame in + bytes + */ +OI_UINT16 OI_CODEC_SBC_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame); + + +/** + * Calculate the maximum bitpool size that fits within a given frame length. + * + * @param frame The frame to calculate the bitpool size for + * @param frameLen The frame length to fit the bitpool to + * + * @return the maximum bitpool that will fit in the specified frame length + */ +OI_UINT16 OI_CODEC_SBC_CalculateBitpool(OI_CODEC_SBC_FRAME_INFO *frame, + OI_UINT16 frameLen); + +/** + Calculate the bit rate. + + @param frame The frame whose bit rate to calculate + + @return the approximate bit rate in bits per second, + assuming that stream parameters are constant + */ +OI_UINT32 OI_CODEC_SBC_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame); + +/** + Calculate decoded audio data length for one frame. + + @param frame The frame whose audio data length to calculate + + @return length of decoded audio data for a + single frame, in bytes + */ +OI_UINT16 OI_CODEC_SBC_CalculatePcmBytes(OI_CODEC_SBC_COMMON_CONTEXT *common); + +/** + * Get the codec version text. + * + * @return pointer to text string containing codec version text + * + */ +OI_CHAR *OI_CODEC_Version(void); + + +/** +@} + +@addtogroup codec_internal +@{ +*/ + +extern const OI_CHAR *const OI_CODEC_SBC_FreqText[]; +extern const OI_CHAR *const OI_CODEC_SBC_ModeText[]; +extern const OI_CHAR *const OI_CODEC_SBC_SubbandsText[]; +extern const OI_CHAR *const OI_CODEC_SBC_BlocksText[]; +extern const OI_CHAR *const OI_CODEC_SBC_AllocText[]; + +/** +@} + +@addtogroup codec_lib +@{ +*/ + +#ifdef OI_DEBUG +void OI_CODEC_SBC_DumpConfig(OI_CODEC_SBC_FRAME_INFO *frameInfo); +#else +#define OI_CODEC_SBC_DumpConfig(f) +#endif + +/** +@} +*/ + +#ifdef __cplusplus +} +#endif + + +#endif /* _OI_CODEC_SBC_CORE_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h b/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h new file mode 100644 index 00000000..148f3cf0 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_CODEC_SBC_PRIVATE_H +#define _OI_CODEC_SBC_PRIVATE_H + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** +@file +Function prototypes and macro definitions used internally by the codec. + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +#ifdef USE_RESTRICT_KEYWORD +#define RESTRICT restrict +#else +#define RESTRICT +#endif + +#ifdef CODEC_DEBUG +#include +#define ERROR(x) do {BT_WARN x; BT_WARN("\n"); } while (0) +#else +#define ERROR(x) +#endif + +#ifdef TRACE_EXECUTION +#define TRACE(x) do { BT_WARN x; BT_WARN("\n"); } while (0) +#else +#define TRACE(x) +#endif + +#ifndef PRIVATE +#define PRIVATE +#endif + +#ifndef INLINE +#define INLINE +#endif + +#include "oi_assert.h" +#include "oi_codec_sbc.h" + +#ifndef OI_SBC_SYNCWORD +#define OI_SBC_SYNCWORD 0x9c +#endif + +#ifndef DIVIDE +#define DIVIDE(a, b) ((a) / (b)) +#endif + +typedef union { + OI_UINT8 uint8[SBC_MAX_BANDS]; + OI_UINT32 uint32[SBC_MAX_BANDS / 4]; +} BITNEED_UNION1; + +typedef union { + OI_UINT8 uint8[2 * SBC_MAX_BANDS]; + OI_UINT32 uint32[2 * SBC_MAX_BANDS / 4]; +} BITNEED_UNION2; + +static const OI_UINT16 freq_values[] = { 16000, 32000, 44100, 48000 }; +static const OI_UINT8 block_values[] = { 4, 8, 12, 16 }; +static const OI_UINT8 channel_values[] = { 1, 2, 2, 2 }; +static const OI_UINT8 band_values[] = { 4, 8 }; + + +#define TEST_MODE_SENTINEL "OINA" +#define TEST_MODE_SENTINEL_LENGTH 4 + +/** Used internally. */ +typedef struct { + union { + const OI_UINT8 *r; + OI_UINT8 *w; + } ptr; + OI_UINT32 value; + OI_UINT bitPtr; +} OI_BITSTREAM; + + +#define VALID_INT16(x) (((x) >= OI_INT16_MIN) && ((x) <= OI_INT16_MAX)) +#define VALID_INT32(x) (((x) >= OI_INT32_MIN) && ((x) <= OI_INT32_MAX)) + +#define DCTII_8_SHIFT_IN 0 +#define DCTII_8_SHIFT_OUT 16-DCTII_8_SHIFT_IN + +#define DCTII_8_SHIFT_0 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_1 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_2 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_3 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_4 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_5 (DCTII_8_SHIFT_OUT) +#define DCTII_8_SHIFT_6 (DCTII_8_SHIFT_OUT-1) +#define DCTII_8_SHIFT_7 (DCTII_8_SHIFT_OUT-2) + +#define DCT_SHIFT 15 + +#define DCTIII_4_SHIFT_IN 2 +#define DCTIII_4_SHIFT_OUT 15 + +#define DCTIII_8_SHIFT_IN 3 +#define DCTIII_8_SHIFT_OUT 14 + +OI_UINT computeBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT8 *bitneeds, + OI_UINT ch, + OI_UINT *preferredBitpool); + +void oneChannelBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common, + BITNEED_UNION1 *bitneeds, + OI_UINT ch, + OI_UINT bitcount); + + +OI_INT adjustToFitBitpool(const OI_UINT bitpool, + OI_UINT32 *bitneeds, + const OI_UINT subbands, + OI_UINT bitcount, + OI_UINT *excess); + +OI_INT allocAdjustedBits(OI_UINT8 *dest, + OI_INT bits, + OI_INT excess); + +OI_INT allocExcessBits(OI_UINT8 *dest, + OI_INT excess); + +PRIVATE OI_UINT32 internal_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame); + +PRIVATE OI_UINT16 internal_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame); + +void monoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common); + +typedef void (*BIT_ALLOC)(OI_CODEC_SBC_COMMON_CONTEXT *common); + +PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT8 bitpool, + const OI_BYTE **frameData, + OI_UINT32 *frameBytes, + OI_INT16 *pcmData, + OI_UINT32 *pcmBytes); + +OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context, + OI_UINT32 *decoderData, + OI_UINT32 decoderDataBytes, + OI_BYTE maxChannels, + OI_BYTE pcmStride, + OI_BOOL enhanced, + OI_BOOL msbc_enable); + +OI_UINT16 OI_SBC_CalculateFrameAndHeaderlen(OI_CODEC_SBC_FRAME_INFO *frame, OI_UINT *headerLen_); + +PRIVATE OI_UINT32 OI_SBC_MaxBitpool(OI_CODEC_SBC_FRAME_INFO *frame); + +PRIVATE void OI_SBC_ComputeBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *frame); +PRIVATE OI_UINT8 OI_SBC_CalculateChecksum(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data); + +/* Transform functions */ +PRIVATE void shift_buffer(SBC_BUFFER_T *dest, SBC_BUFFER_T *src, OI_UINT wordCount); +PRIVATE void cosineModulateSynth4(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT in); +PRIVATE void SynthWindow40_int32_int32_symmetry_with_sum(OI_INT16 *pcm, SBC_BUFFER_T buffer[80], OI_UINT strideShift); + +void dct3_4(OI_INT32 *RESTRICT out, OI_INT32 const *RESTRICT in); +PRIVATE void analyze4_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 40], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[4]); + +void dct3_8(OI_INT32 *RESTRICT out, OI_INT32 const *RESTRICT in); + +PRIVATE void analyze8_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 80], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[8]); + +#ifdef SBC_ENHANCED +PRIVATE void analyze8_enhanced_generated(SBC_BUFFER_T analysisBuffer[RESTRICT 112], + OI_INT16 *pcm, + OI_UINT strideShift, + OI_INT32 subband[8]); +#endif + +/* Decoder functions */ + +void OI_SBC_ReadHeader(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data); +PRIVATE void OI_SBC_ReadScalefactors(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *b, OI_BITSTREAM *bs); +PRIVATE void OI_SBC_ReadSamples(OI_CODEC_SBC_DECODER_CONTEXT *common, OI_BITSTREAM *ob); +PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT *common, OI_BITSTREAM *global_bs); +PRIVATE void OI_SBC_SynthFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT start_block, OI_UINT nrof_blocks); +OI_INT32 OI_SBC_Dequant(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits); +PRIVATE OI_BOOL OI_SBC_ExamineCommandPacket(OI_CODEC_SBC_DECODER_CONTEXT *context, const OI_BYTE *data, OI_UINT32 len); +PRIVATE void OI_SBC_GenerateTestSignal(OI_INT16 pcmData[][2], OI_UINT32 sampleCount); + +PRIVATE void OI_SBC_ExpandFrameFields(OI_CODEC_SBC_FRAME_INFO *frame); +PRIVATE OI_STATUS OI_CODEC_SBC_Alloc(OI_CODEC_SBC_COMMON_CONTEXT *common, + OI_UINT32 *codecDataAligned, + OI_UINT32 codecDataBytes, + OI_UINT8 maxChannels, + OI_UINT8 pcmStride); +/** +@} +*/ + +#endif /* _OI_CODEC_SBC_PRIVATE_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_codec_version.c b/components/ble/ble_stack/sbc/dec/oi_codec_version.c new file mode 100644 index 00000000..5d107b3a --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_codec_version.c @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/** +@file +This file contains a single function, which returns a string indicating the +version number of the eSBC codec + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ +#include "oi_stddefs.h" +#include "oi_codec_sbc_private.h" + +#if defined(SBC_DEC_INCLUDED) +/** Version string for the BLUEmagic 3.0 protocol stack and profiles */ +PRIVATE OI_CHAR *const codecVersion = "v1.5" +#ifdef OI_SBC_EVAL + " (Evaluation version)" +#endif + ; + +/** This function returns the version string for the BLUEmagic 3.0 protocol stack + and profiles */ +OI_CHAR *OI_CODEC_Version(void) +{ + return codecVersion; +} + +/**********************************************************************************/ + +/** +@} +*/ + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/oi_common.h b/components/ble/ble_stack/sbc/dec/oi_common.h new file mode 100644 index 00000000..c4169f93 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_common.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_COMMON_H +#define _OI_COMMON_H +/** + * @file + * + * This file is used to group commonly used BLUEmagic 3.0 software + * header files. + * + * This file should be included in application source code along with the header + * files for the specific modules of the protocol stack being used. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_bt_spec.h" +#include "oi_stddefs.h" +#include "oi_status.h" +#include "oi_time.h" +#include "oi_osinterface.h" + + +/*****************************************************************************/ +#endif /* _OI_COMMON_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h b/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h new file mode 100644 index 00000000..dfa52c16 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_CPU_DEP_H +#define _OI_CPU_DEP_H +/** + * @file + * This file contains definitions for characteristics of the target CPU and + * compiler, including primitive data types and endianness. + * + * This file defines the byte order and primitive data types for various + * CPU families. The preprocessor symbol 'CPU' must be defined to be an + * appropriate value or this header will generate a compile-time error. + * + * @note The documentation for this header file uses the x86 family of processors + * as an illustrative example for CPU/compiler-dependent data type definitions. + * Go to the source code of this header file to see the details of primitive type + * definitions for each platform. + * + * Additional information is available in the @ref data_types_docpage section. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +/** @name Definitions indicating family of target OI_CPU_TYPE + * @{ + */ + +#define OI_CPU_X86 1 /**< x86 processor family */ +#define OI_CPU_ARM 2 /**< ARM processor family. + @deprecated Use #OI_CPU_ARM7_LEND or + #OI_CPU_ARM7_BEND. */ +#define OI_CPU_ARC 3 /**< ARC processor family. + @deprecated Use #OI_CPU_ARC_LEND or + #OI_CPU_ARC_BEND. */ +#define OI_CPU_SH3 4 /**< Hitachi SH-3 processor family */ +#define OI_CPU_H8 5 /**< Hitachi H8 processor family */ +#define OI_CPU_MIPS 6 /**< MIPS processor family */ +#define OI_CPU_SPARC 7 /**< SPARC processor family */ +#define OI_CPU_M68000 8 /**< Motorola M68000 processor family */ +#define OI_CPU_PPC 9 /**< PowerPC (PPC) processor family */ +#define OI_CPU_SH4_7750 10 /**< Hitachi SH7750 series in SH-4 processor family */ +#define OI_CPU_SH2 11 /**< Hitachi SH-2 processor family */ +#define OI_CPU_ARM7_LEND 12 /**< ARM7, little-endian */ +#define OI_CPU_ARM7_BEND 13 /**< ARM7, big-endian */ +#define OI_CPU_GDM1202 14 /**< GCT GDM1202 */ +#define OI_CPU_ARC_LEND 15 /**< ARC processor family, little-endian */ +#define OI_CPU_ARC_BEND 16 /**< ARC processor family, big-endian */ +#define OI_CPU_M30833F 17 /**< Mitsubishi M308 processor family */ +#define OI_CPU_CR16C 18 /**< National Semiconductor 16 bit processor family */ +#define OI_CPU_M64111 19 /**< Renesas M64111 processor (M32R family) */ +#define OI_CPU_ARMV5_LEND 20 //*< ARM5, little-endian */ + +#define OI_CPU_TYPE 12 + +#ifndef OI_CPU_TYPE +#error "OI_CPU_TYPE type not defined" +#endif + +/**@}*/ + + +/** @name Definitions indicating byte-wise endianness of target CPU + * @{ + */ + +#define OI_BIG_ENDIAN_BYTE_ORDER 0 /**< Multiple-byte values are stored in memory beginning with the most significant byte at the lowest address. */ +#define OI_LITTLE_ENDIAN_BYTE_ORDER 1 /**< Multiple-byte values are stored in memory beginning with the least significant byte at the lowest address. */ + +/**@}*/ + + +/** @name CPU/compiler-independent primitive data type definitions + * @{ + */ + +typedef int OI_BOOL; /**< Boolean values use native integer data type for target CPU. */ +typedef int OI_INT; /**< Integer values use native integer data type for target CPU. */ +typedef unsigned int OI_UINT; /**< Unsigned integer values use native unsigned integer data type for target CPU. */ +typedef unsigned char OI_BYTE; /**< Raw bytes type uses native character data type for target CPU. */ + +/**@}*/ + + + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_X86 + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER /**< x86 platform byte ordering is little-endian */ + +/** @name CPU/compiler-dependent primitive data type definitions for x86 processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for x86 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for x86 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for x86 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for x86 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for x86 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for x86 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM +/* This CPU type is deprecated (removed from use). Instead, use OI_CPU_ARM7_LEND or OI_CPU_ARM7_BEND for + little-endian or big-endian configurations of the ARM7, respectively. */ +#error OI_CPU_ARM is deprecated +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC +/* This CPU type is deprecated (removed from use). Instead, use OI_CPU_ARC_LEND or OI_CPU_ARC_BEND for + little-endian or big-endian configurations of the ARC, respectively. */ +#error OI_CPU_ARC is deprecated +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH3 +/* The Hitachi SH C compiler defines _LIT or _BIG, depending on the endianness + specified to the compiler on the command line. */ +#if defined(_LIT) +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER /**< If _LIT is defined, SH-3 platform byte ordering is little-endian. */ +#elif defined(_BIG) +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< If _BIG is defined, SH-3 platform byte ordering is big-endian. */ +#else +#error SH compiler endianness undefined +#endif + +/** @name CPU/compiler-dependent primitive data type definitions for SH-3 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH-3 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH-3 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH-3 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH-3 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH-3 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH-3 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH2 + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< SH-2 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for SH-2 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH-2 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH-2 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH-2 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH-2 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH-2 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH-2 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_H8 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER +#error basic types not defined +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_MIPS +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER +/** @name CPU/compiler-dependent primitive data type definitions for MIPS processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SPARC +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER +#error basic types not defined +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M68000 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< M68000 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for M68000 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M68000 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M68000 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M68000 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M68000 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M68000 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M68000 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_PPC +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + + +/** @name CPU/compiler-dependent primitive data type definitions for PPC 8XX processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for PPC8XX processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for PPC8XX processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for PPC8XX processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for PPC8XX processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for PPC8XX processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for PPC8XX processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_SH4_7750 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER /**< SH7750 platform byte ordering is big-endian. */ + +/** @name CPU/compiler-dependent primitive data type definitions for SH7750 processor series of the SH-4 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for SH7750 SH-4 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for SH7750 SH-4 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for SH7750 SH-4 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for SH7750 SH-4 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for SH7750 SH-4 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for SH7750 SH-4 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM7_LEND +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name little-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef void *OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARM7_BEND +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER +/** @name big-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef void *OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_GDM1202 +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +typedef signed char OI_INT8; /**< 8-bit signed integer. */ +typedef signed short OI_INT16; /**< 16-bit signed integer. */ +typedef signed long OI_INT32; /**< 32-bit signed integer. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC_LEND + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for ARC processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARC processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARC processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARC processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARC processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARC processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARC processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARC_BEND + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for ARC processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARC processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARC processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARC processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARC processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARC processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARC processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M30833F + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for Mitsubishi M308 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M308 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M308 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M308 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M308 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M308 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M308 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_CR16C + +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for National Semicnductor processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for CR16C processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for CR16C processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for CR16C processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for CR16C processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for CR16C processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for CR16C processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_M64111 + +#define OI_CPU_BYTE_ORDER OI_BIG_ENDIAN_BYTE_ORDER + +/** @name CPU/compiler-dependent primitive data type definitions for Renesas M32R processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for M64111 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for M64111 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for M64111 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for M64111 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for M64111 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for M64111 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ +#endif + +/*********************************************************************************/ + +#if OI_CPU_TYPE==OI_CPU_ARMV5_LEND +#define OI_CPU_BYTE_ORDER OI_LITTLE_ENDIAN_BYTE_ORDER + +/** @name little-endian CPU/compiler-dependent primitive data type definitions for the ARM7 processor family + * @{ + */ + +typedef signed char OI_INT8; /**< 8-bit signed integer values use native signed character data type for ARM7 processor. */ +typedef signed short OI_INT16; /**< 16-bit signed integer values use native signed short integer data type for ARM7 processor. */ +typedef signed long OI_INT32; /**< 32-bit signed integer values use native signed long integer data type for ARM7 processor. */ +typedef unsigned char OI_UINT8; /**< 8-bit unsigned integer values use native unsigned character data type for ARM7 processor. */ +typedef unsigned short OI_UINT16; /**< 16-bit unsigned integer values use native unsigned short integer data type for ARM7 processor. */ +typedef unsigned long OI_UINT32; /**< 32-bit unsigned integer values use native unsigned long integer data type for ARM7 processor. */ + +typedef OI_UINT32 OI_ELEMENT_UNION; /**< Type for first element of a union to support all data types up to pointer width. */ + +/**@}*/ + +#endif + +/*********************************************************************************/ + + +#ifndef OI_CPU_BYTE_ORDER +#error "Byte order (endian-ness) not defined" +#endif + + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +/*********************************************************************************/ +#endif /* _OI_CPU_DEP_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_modules.h b/components/ble/ble_stack/sbc/dec/oi_modules.h new file mode 100644 index 00000000..1b7c24bf --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_modules.h @@ -0,0 +1,170 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_MODULES_H +#define _OI_MODULES_H +/** + * @file + * + * Enumeration type defining the inidivual stack components. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * This enumeration lists constants for referencing the components of + * the BLUEmagic 3.0 protocol stack, profiles, and other functionalities. + * + * In order to distinguish types of modules, items are grouped with markers to + * delineate start and end of the groups + * + * The module type is used for various purposes: + * identification in debug print statements + * access to initialization flags + * access to the configuration table + */ + +typedef enum { + /* profiles and protocols --> Updates to oi_debug.c and oi_config_table.c */ + + /* XX --> Keep Enum values up-to-date! */ + OI_MODULE_AT, /**< 00 AT command processing */ + OI_MODULE_A2DP, /**< 01 Advanced Audio Distribution Profile */ + OI_MODULE_AVCTP, /**< 02 Audio-Visual Control Transport Profile */ + OI_MODULE_AVDTP, /**< 03 Audio-Visual Distribution Protocol */ + OI_MODULE_AVRCP, /**< 04 Audio-Visual Remote Control Profile */ + OI_MODULE_BIP_CLI, /**< 05 Basic Imaging Profile protocol client */ + OI_MODULE_BIP_SRV, /**< 06 Basic Imaging Profile protocol server */ + OI_MODULE_BNEP, /**< 07 Bluetooth Network Encapsulation Protocol */ + OI_MODULE_BPP_SENDER, /**< 08 Basic Printing Profile */ + OI_MODULE_BPP_PRINTER, /**< 09 Basic Printing Profile */ + OI_MODULE_CTP, /**< 10 Cordless Telephony Profile */ + OI_MODULE_DUN, /**< 11 Dial-Up Networking Profile */ + OI_MODULE_FAX, /**< 12 Fax Profile */ + OI_MODULE_FTP_CLI, /**< 13 File Transfer Profile protocol client */ + OI_MODULE_FTP_SRV, /**< 14 File Transfer Profile protocol server */ + OI_MODULE_HANDSFREE, /**< 15 Hands-Free Profile */ + OI_MODULE_HANDSFREE_AG, /**< 16 Hands-Free Profile */ + OI_MODULE_HCRP_CLI, /**< 17 Hardcopy Cable Replacement Profile */ + OI_MODULE_HCRP_SRV, /**< 18 Hardcopy Cable Replacement Profile */ + OI_MODULE_HEADSET, /**< 19 Headset Profile */ + OI_MODULE_HEADSET_AG, /**< 20 Headset Profile */ + OI_MODULE_HID, /**< 21 Human Interface Device profile */ + OI_MODULE_INTERCOM, /**< 22 Intercom Profile */ + OI_MODULE_OBEX_CLI, /**< 23 OBEX protocol client, Generic Object Exchange Profile */ + OI_MODULE_OBEX_SRV, /**< 24 OBEX protocol server, Generic Object Exchange Profile */ + OI_MODULE_OPP_CLI, /**< 25 Object Push Profile protocol client */ + OI_MODULE_OPP_SRV, /**< 26 Object Push Profile protocol server */ + OI_MODULE_PAN, /**< 27 PAN profile */ + OI_MODULE_PBAP_CLI, /**< 28 Phonebook Access Profile client */ + OI_MODULE_PBAP_SRV, /**< 29 Phonebook Access Profile server */ + OI_MODULE_SAP_CLI, /**< 30 SIM Access Profile */ + OI_MODULE_SAP_SRV, /**< 31 SIM Access Profile */ + OI_MODULE_SPP, /**< 32 Serial Port Profile */ + OI_MODULE_SYNC_CLI, /**< 33 Synchronization Profile */ + OI_MODULE_SYNC_SRV, /**< 34 Synchronization Profile */ + OI_MODULE_SYNC_CMD_CLI, /**< 35 Synchronization Profile */ + OI_MODULE_SYNC_CMD_SRV, /**< 36 Synchronization Profile */ + OI_MODULE_SYNCML, /**< 37 SyncML Profile */ + OI_MODULE_TCS, /**< 38 TCS Binary */ + OI_MODULE_VDP, /**< 39 Video Distribution Profile */ + + /* corestack components --> Updates to oi_debug.c and oi_config_table.c */ + + OI_MODULE_COMMON_CONFIG, /**< 40 Common configuration, module has no meaning other than for config struct */ + OI_MODULE_CMDCHAIN, /**< 41 Command chaining utility */ + OI_MODULE_DISPATCH, /**< 42 Dispatcher */ + OI_MODULE_DATAELEM, /**< 43 Data Elements, marshaller */ + OI_MODULE_DEVMGR, /**< 44 Device Manager */ + OI_MODULE_DEVMGR_MODES, /**< 45 Device Manager connectability/discoverability modes */ + OI_MODULE_HCI, /**< 46 Host Controller Interface command layer */ + OI_MODULE_L2CAP, /**< 47 L2CAP */ + OI_MODULE_MEMMGR, /**< 48 modules that do memory management */ + OI_MODULE_POLICYMGR, /**< 49 Policy Manager */ + OI_MODULE_RFCOMM, /**< 50 RFCOMM */ + OI_MODULE_RFCOMM_SD, /**< 51 RFCOMM Service discovery */ + OI_MODULE_SDP_CLI, /**< 52 Service Discovery Protocol client */ + OI_MODULE_SDP_SRV, /**< 53 Service Discovery Protocol server */ + OI_MODULE_SDPDB, /**< 54 Service Discovery Protocol database */ + OI_MODULE_SECMGR, /**< 55 Security Manager */ + OI_MODULE_SNIFFLOG, /**< 56 sniff log */ + OI_MODULE_SUPPORT, /**< 57 support functions, including CThru Dispatcher, time functions, and stack initialization */ + OI_MODULE_TRANSPORT, /**< 58 transport layer between HCI command layer and driver */ + OI_MODULE_TEST, /**< 59 used to debug output from internal test programs */ + OI_MODULE_XML, /**< 60 XML/CSS parser */ + + OI_MODULE_DI, /**< 61 Device Identification Profile */ + + // bhapi components --> Updates to oi_debug.c + + OI_MODULE_BHAPI, /**< 62 BLUEmagic Host API generic */ + OI_MODULE_BHCLI, /**< 63 BLUEmagic Host API client side */ + OI_MODULE_BHSRV, /**< 64 BLUEmagic Host API server side */ + OI_MODULE_MSGQ, /**< 65 module that handles message queuing */ + OI_MODULE_BHAPI_TRANSPORT, /**< 66 module that handles message queuing */ + OI_MODULE_BLST_SRV, /**< 67 module that provides server side BHAPI Lightweight Serial Transport */ + OI_MODULE_BLST_CLI, /**< 68 module that provides client side BHAPI Lightweight Serial Transport */ + + // OEM files --> Updates to oi_debug.c + OI_MODULE_OEM, /**< 69 Application Memory allocation */ + + // Application glue --> Updates to oi_debug.c + OI_MODULE_APP, /**< 70 Application Memory allocation */ + + /* various pieces of code depend on these last 2 elements occuring in a specific order: + OI_MODULE_ALL must be the 2nd to last element + OI_MODULE_UNKNOWN must be the last element + */ + OI_MODULE_ALL, /**< 71 special value identifying all modules - used for control of debug print statements */ + OI_MODULE_UNKNOWN /**< 72 special value - used for debug print statements */ +} OI_MODULE; + +/** + * This constant is the number of actual modules in the list. ALL and UNKNOWN are + * special values that are not actually modules. + * Used for debug print and memmgr profiling + */ +#define OI_NUM_MODULES OI_MODULE_ALL + + +/** + * This constant is the number of profile and core components. It is used to size + * the initialization and configuration tables. + */ +#define OI_NUM_STACK_MODULES OI_MODULE_BHAPI + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_MODULES_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_osinterface.h b/components/ble/ble_stack/sbc/dec/oi_osinterface.h new file mode 100644 index 00000000..f486c8bb --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_osinterface.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_OSINTERFACE_H +#define _OI_OSINTERFACE_H +/** + @file + * This file provides the platform-independent interface for functions for which + * implementation is platform-specific. + * + * The functions in this header file define the operating system or hardware + * services needed by the BLUEmagic 3.0 protocol stack. The + * actual implementation of these services is platform-dependent. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" +#include "oi_time.h" +#include "oi_status.h" +#include "oi_modules.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Terminates execution. + * + * @param reason Reason for termination + */ +void OI_FatalError(OI_STATUS reason); + +/** + * This function logs an error. + * + * When built for release mode, BLUEmagic 3 errors are logged to + * this function. (in debug mode, errors are logged via + * OI_Print()). + * + * @param module Module in which the error was detected (see + * oi_modules.h) + * @param lineno Line number of the C file OI_SLOG_ERROR called + * @param status Status code associated with the error event + */ +void OI_LogError(OI_MODULE module, OI_INT lineno, OI_STATUS status); + +/** + * This function initializes the debug code handling. + * + * When built for debug mode, this function performs platform + * dependent initialization to handle message codes passed in + * via OI_SetMsgCode(). + */ +void OI_InitDebugCodeHandler(void); + + +/** + * This function reads the time from the real time clock. + * + * All timing in BM3 is relative, typically a granularity + * of 5 or 10 msecs is adequate. + * + * @param[out] now Pointer to the buffer to which the current + * time will be returned + */ +void OI_Time_Now(OI_TIME *now); + +/** + * This function causes the current thread to sleep for the + * specified amount of time. This function must be called + * without the stack access token. + * + * @note BM3 corestack and profiles never suspend and never call + * OI_Sleep. The use of OI_Sleep is limited to applications and + * platform-specific code. + * + * If your port and applications never use OI_Sleep, this function can be left unimplemented. + * + * @param milliseconds Number of milliseconds to sleep + */ +void OI_Sleep(OI_UINT32 milliseconds); + + +/** + * Defines for message type codes. + */ +#define OI_MSG_CODE_APPLICATION 0 /**< Application output */ +#define OI_MSG_CODE_ERROR 1 /**< Error message output */ +#define OI_MSG_CODE_WARNING 2 /**< Warning message output */ +#define OI_MSG_CODE_TRACE 3 /**< User API function trace output */ +#define OI_MSG_CODE_PRINT1 4 /**< Catagory 1 debug print output */ +#define OI_MSG_CODE_PRINT2 5 /**< Catagory 2 debug print output */ +#define OI_MSG_CODE_HEADER 6 /**< Error/Debug output header */ + +/** + * This function is used to indicate the type of text being output with + * OI_Print(). For the Linux and Win32 platforms, it will set + * the color of the text. Other possible uses could be to insert + * HTML style tags, add some other message type indication, or + * be completely ignored altogether. + * + * @param code OI_MSG_CODE_* indicating setting the message type. + */ +void OI_SetMsgCode(OI_UINT8 code); + +/** + * All output from OI_Printf() and all debug output is sent to OI_Print. + * Typically, if the platform has a console, OI_Print() is sent to stdout. + * Embedded platforms typically send OI_Print() output to a serial port. + * + * @param str String to print + */ +void OI_Print(OI_CHAR const *str); + +/** + * In cases where OI_Print() is sending output to a logfile in addition to console, + * it is desirable to also put console input into the logfile. + * This function can be called by the console input process. + * + * @note This is an optional API which is strictly + * between the platform-specific stack_console and osinterface + * modules. This API need only be implemented on those + * platforms where is serves a useful purpose, e.g., win32. + * + * @param str Console input string + */ + +void OI_Print_ConsoleInput(OI_CHAR const *str); + +/** + * This function computes the CRC16 of the program image. + */ +OI_UINT16 OI_ProgramImageCRC16(void); + +/** + * Writes an integer to stdout in hex. This macro is intended + * for selective use when debugging in small memory + * configurations or other times when it is not possible to use + * OI_DBGPRINT. + * + * @param n the integer to print + */ + +#define OI_Print_Int(n) \ +{ \ + static const OI_CHAR _digits[] = "0123456789ABCDEF"; \ + OI_CHAR _buf[9]; \ + OI_CHAR *_str = &_buf[8]; \ + OI_UINT32 _i = n; \ + *_str = 0; \ + do { *(--_str) = _digits[(_i & 0xF)]; _i >>= 4; } while (_i); \ + OI_Print(_str); \ +} + +/** + * Application Dynamic Memory allocation. + * + * These APIs are provided for application use on those + * platforms which have no dynamic memory support. Memory is + * allocated from the pool-based heap managed by the stack's + * internal memory manager. + */ +void *OI_APP_Malloc(OI_INT32 size); +void OI_APP_Free(void *ptr); + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_OSINTERFACE_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_status.h b/components/ble/ble_stack/sbc/dec/oi_status.h new file mode 100644 index 00000000..b527ebb1 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_status.h @@ -0,0 +1,578 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_STATUS_H +#define _OI_STATUS_H +/** + * @file + * This file contains status codes for BLUEmagic 3.0 software. + */ + +#include "oi_stddefs.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** test it **/ + +/** + * OI_STATUS must fit in 16 bits, so status codes can range from 0 to 66535, inclusive. + */ + +typedef enum { + OI_STATUS_SUCCESS = 0, /**< function call succeeded alias for #OI_OK */ + OI_OK = 0, /**< function call succeeded alias for #OI_STATUS_SUCCESS */ + OI_STATUS_INVALID_PARAMETERS = 101, /**< invalid function input parameters */ + OI_STATUS_NOT_IMPLEMENTED = 102, /**< attempt to use an unimplemented function */ + OI_STATUS_NOT_INITIALIZED = 103, /**< data not initialized */ + OI_STATUS_NO_RESOURCES = 104, /**< generic resource allocation failure status */ + OI_STATUS_INTERNAL_ERROR = 105, /**< internal inconsistency */ + OI_STATUS_OUT_OF_MEMORY = 106, /**< generally, OI_Malloc failed */ + OI_ILLEGAL_REENTRANT_CALL = 107, /**< violation of non-reentrant module policy */ + OI_STATUS_INITIALIZATION_FAILED = 108, /**< module initialization failed */ + OI_STATUS_INITIALIZATION_PENDING = 109, /**< inititialization not yet complete */ + OI_STATUS_NO_SCO_SUPPORT = 110, /**< SCO operation rejected; system not configured for SCO */ + OI_STATUS_OUT_OF_STATIC_MEMORY = 111, /**< static malloc failed */ + OI_TIMEOUT = 112, /**< generic timeout */ + OI_OS_ERROR = 113, /**< some operating system error */ + OI_FAIL = 114, /**< generic failure */ + OI_STRING_FORMAT_ERROR = 115, /**< error in VarString formatting string */ + OI_STATUS_PENDING = 116, /**< The operation is pending. */ + OI_STATUS_INVALID_COMMAND = 117, /**< The command was invalid. */ + OI_BUSY_FAIL = 118, /**< command rejected due to busy */ + OI_STATUS_ALREADY_REGISTERED = 119, /**< The registration has already been performed. */ + OI_STATUS_NOT_FOUND = 120, /**< The referenced resource was not found. */ + OI_STATUS_NOT_REGISTERED = 121, /**< not registered */ + OI_STATUS_NOT_CONNECTED = 122, /**< not connected */ + OI_CALLBACK_FUNCTION_REQUIRED = 123, /**< A callback function parameter was required. */ + OI_STATUS_MBUF_OVERFLOW = 124, /**< There is no room to add another buffer to an mbuf. */ + OI_STATUS_MBUF_UNDERFLOW = 125, /**< There was an attempt to pull too many bytes from an mbuf. */ + OI_STATUS_CONNECTION_EXISTS = 126, /**< connection exists */ + OI_STATUS_NOT_CONFIGURED = 127, /**< module not configured */ + OI_LOWER_STACK_ERROR = 128, /**< An error was reported by lower stack API. This is used for embedded platforms. */ + OI_STATUS_RESET_IN_PROGRESS = 129, /**< Request failed/rejected because we're busy resetting. */ + OI_STATUS_ACCESS_DENIED = 130, /**< Generic access denied error. */ + OI_STATUS_DATA_ERROR = 131, /**< Generic data error. */ + OI_STATUS_INVALID_ROLE = 132, /**< The requested role was invalid. */ + OI_STATUS_ALREADY_CONNECTED = 133, /**< The requested connection is already established. */ + OI_STATUS_PARSE_ERROR = 134, /**< Parse error */ + OI_STATUS_END_OF_FILE = 135, /**< End of file */ + OI_STATUS_READ_ERROR = 136, /**< Generic read error */ + OI_STATUS_WRITE_ERROR = 137, /**< Generic write error */ + OI_STATUS_NEGOTIATION_FAILURE = 138, /**< Error in negotiation */ + OI_STATUS_READ_IN_PROGRESS = 139, /**< A read is already in progress */ + OI_STATUS_ALREADY_INITIALIZED = 140, /**< Initialization has already been done */ + OI_STATUS_STILL_CONNECTED = 141, /**< The service cannot be shutdown because there are still active connections. */ + OI_STATUS_MTU_EXCEEDED = 142, /**< The packet is too big */ + OI_STATUS_LINK_TERMINATED = 143, /**< The link was terminated */ + OI_STATUS_PIN_CODE_TOO_LONG = 144, /**< Application gave us a pin code that is too long */ + OI_STATUS_STILL_REGISTERED = 145, /**< The service cannot be shutdown because there are still active registrations. */ + OI_STATUS_SPEC_VIOLATION = 146, /**< Some application behavior contrary to BT specifications */ + + + OI_STATUS_PSM_ALREADY_REGISTERED = 402, /**< L2CAP: The specified PSM has already been registered. */ + OI_STATUS_INVALID_CID = 403, /**< L2CAP: CID is invalid or no longer valid (connection terminated) */ + OI_STATUS_CID_NOT_FOUND = 404, /**< L2CAP: CID does not represent a current connection */ + OI_STATUS_CHANNEL_NOT_FOUND = 406, /**< L2CAP: CID does not represent a current connection */ + OI_STATUS_PSM_NOT_FOUND = 407, /**< L2CAP: PSM not found */ + OI_STATUS_INVALID_STATE = 408, /**< L2CAP: invalid state */ + OI_STATUS_WRITE_IN_PROGRESS = 410, /**< L2CAP: write in progress */ + OI_STATUS_INVALID_PACKET = 411, /**< L2CAP: invalid packet */ + OI_STATUS_SEND_COMPLETE = 412, /**< L2CAP: send is complete */ + OI_STATUS_INVALID_HANDLE = 414, /**< L2CAP: handle is invalid */ + OI_STATUS_GROUP_FULL = 418, /**< L2CAP: No more members can be added to the specified group. */ + OI_STATUS_DEVICE_ALREADY_IN_GROUP = 423, /**< L2CAP: The device already exists in the group. */ + OI_STATUS_DUPLICATE_GROUP = 425, /**< L2CAP: attempt to add more than one group */ + OI_STATUS_EMPTY_GROUP = 426, /**< L2CAP: group is empty */ + OI_STATUS_PACKET_NOT_FOUND = 427, /**< L2CAP: packet not found */ + OI_STATUS_BUFFER_TOO_SMALL = 428, /**< L2CAP: The buffer size is too small. */ + OI_STATUS_IDENTIFIER_NOT_FOUND = 429, /**< L2CAP: identifier not found */ + + OI_L2CAP_DISCONNECT_LOWER_LAYER = 430, /**< L2CAP: The lower level forced a disconnect. */ + OI_L2CAP_DISCONNECT_REMOTE_REQUEST = 431, /**< L2CAP: The remote device requested a disconnect. */ + OI_L2CAP_GROUP_ADD_CONNECT_FAIL = 433, /**< L2CAP: Group add connect faiL */ + OI_L2CAP_GROUP_REMOVE_FAILURE = 434, /**< L2CAP: Group remove failure */ + OI_L2CAP_DATA_WRITE_ERROR_LINK_TERM = 435, /**< L2CAP: Data write error LINK_TERM */ + OI_L2CAP_DISCONNECT_LOCAL_REQUEST = 436, /**< L2CAP: Disconnect local request */ + + OI_L2CAP_CONNECT_TIMEOUT = 437, /**< L2CAP: Connect timeout */ + OI_L2CAP_DISCONNECT_TIMEOUT = 439, /**< L2CAP: Disconnect timeout */ + OI_L2CAP_PING_TIMEOUT = 440, /**< L2CAP: Ping timeout */ + OI_L2CAP_GET_INFO_TIMEOUT = 441, /**< L2CAP: Get info timeout */ + OI_L2CAP_INVALID_ADDRESS = 444, /**< L2CAP: Invalid address */ + OI_L2CAP_CMD_REJECT_RCVD = 445, /**< L2CAP: remote sent us 'command reject' response */ + + OI_L2CAP_CONNECT_BASE = 450, /**< L2CAP: Connect base */ + OI_L2CAP_CONNECT_PENDING = 451, /**< L2CAP: Connect pending */ + OI_L2CAP_CONNECT_REFUSED_INVALID_PSM = 452, /**< L2CAP: Connect refused invalid PSM */ + OI_L2CAP_CONNECT_REFUSED_SECURITY = 453, /**< L2CAP: Connect refused security */ + OI_L2CAP_CONNECT_REFUSED_NO_RESOURCES = 454, /**< L2CAP: Connect refused no resources */ + + OI_L2CAP_CONFIG_BASE = 460, /**< L2CAP: Config base */ + OI_L2CAP_CONFIG_FAIL_INVALID_PARAMETERS = 461, /**< L2CAP: Config fail invalid parameters */ + OI_L2CAP_CONFIG_FAIL_NO_REASON = 462, /**< L2CAP: Config fail no reason */ + OI_L2CAP_CONFIG_FAIL_UNKNOWN_OPTIONS = 463, /**< L2CAP: Config fail unknown options */ + + OI_L2CAP_GET_INFO_BASE = 470, /**< L2CAP: Get info base */ + OI_L2CAP_GET_INFO_NOT_SUPPORTED = 471, /**< L2CAP: Get info not supported */ + OI_L2CAP_MTU_EXCEEDED = 472, /**< L2CAP: The MTU of the channel was exceeded */ + OI_L2CAP_INVALID_PSM = 482, /**< L2CAP: Invalid PSM */ + OI_L2CAP_INVALID_MTU = 483, /**< L2CAP: Invalid MTU */ + OI_L2CAP_INVALID_FLUSHTO = 484, /**< L2CAP: Invalid flush timeout */ + + OI_HCI_NO_SUCH_CONNECTION = 601, /**< HCI: caller specified a non-existent connection handle */ + OI_HCI_CB_LIST_FULL = 603, /**< HCI: callback list is full, cannot attempt to send command */ + OI_HCI_EVENT_UNDERRUN = 605, /**< HCI: parsing event packet, premature end-of-parameters */ + OI_HCI_UNKNOWN_EVENT_CODE = 607, /**< HCI: event received - event code is unknown */ + OI_HCI_BAD_EVENT_PARM_LEN = 608, /**< HCI: event - parameter length is incorrect */ + OI_HCI_CMD_QUEUE_FULL = 611, /**< HCI: command queue is full */ + OI_HCI_SHORT_EVENT = 612, /**< HCI: event received, missing event code and/or parm len */ + OI_HCI_TRANSMIT_NOT_READY = 613, /**< HCI: ACL/SCO transmit request failed - busy or no buffers available */ + OI_HCI_ORPHAN_SENT_EVENT = 614, /**< HCI: got spurious 'sent' event from transport layer */ + OI_HCI_CMD_TABLE_ERROR = 615, /**< HCI: inconsistency in the internal command table */ + OI_HCI_UNKNOWN_CMD_ID = 616, /**< HCI: HciApi Command - unknown command id */ + OI_HCI_UNEXPECTED_EVENT = 619, /**< HCI: event received which only occurs in response to our cmd */ + OI_HCI_EVENT_TABLE_ERROR = 620, /**< HCI: inconsistency in the internal event table */ + OI_HCI_EXPECTED_EVENT_TIMOUT = 621, /**< HCI: timed out waiting for an expected event */ + OI_HCI_NO_CMD_DESC_FOR_OPCODE = 622, /**< HCI: event opcode is not known */ + OI_HCI_INVALID_OPCODE_ERROR = 623, /**< HCI: command opcode is invalid */ + OI_HCI_FLOW_CONTROL_DISABLED = 624, /**< HCI: can not use host flow control APIs if disabled in configuration */ + OI_HCI_TX_COMPLETE = 625, /**< HCI: packet delivery to Host Controler complete */ + OI_HCI_TX_ERROR = 626, /**< HCI: failed to deliver packet to Host Controler */ + OI_HCI_DEVICE_NOT_INITIALIZED = 627, /**< HCI: commands from upper layers disallowed until device is up and running */ + OI_HCI_UNSUPPORTED_COMMAND = 628, /**< HCI: command requested is not supported by local device */ + OI_HCI_PASSTHROUGH_ERROR = 629, /**< HCI: Error processing passthrough command */ + OI_HCI_PASSTHROUGH_ALREADY_SET = 630, /**< HCI: Passthrough mode already enabled */ + OI_HCI_RESET_FAILURE = 631, /**< HCI: failed to reset the device/baseband */ + OI_HCI_TRANSPORT_RESET = 632, /**< HCI: some operation failed because of a reset in the transport */ + OI_HCIERR_HCIIFC_INIT_FAILURE = 633, /**< HCI: failed to initialize transport layer interface */ + + OI_HCIERR_FIRST_ERROR_VALUE = 701, /**< marker for first HCI protocol error */ + OI_HCIERR_UNKNOWN_HCI_COMMAND = 701, /**< HCI: protocol error 0x01 */ + OI_HCIERR_NO_CONNECTION = 702, /**< HCI: protocol error 0x02 */ + OI_HCIERR_HARDWARE_FAILURE = 703, /**< HCI: protocol error 0x03 */ + OI_HCIERR_PAGE_TIMEOUT = 704, /**< HCI: protocol error 0x04 */ + OI_HCIERR_AUTHENTICATION_FAILURE = 705, /**< HCI: protocol error 0x05 */ + OI_HCIERR_KEY_MISSING = 706, /**< HCI: protocol error 0x06 */ + OI_HCIERR_MEMORY_FULL = 707, /**< HCI: protocol error 0x07 */ + OI_HCIERR_CONNECTION_TIMEOUT = 708, /**< HCI: protocol error 0x08 */ + OI_HCIERR_MAX_NUM_OF_CONNECTIONS = 709, /**< HCI: protocol error 0x09 */ + OI_HCIERR_MAX_NUM_OF_SCO_CONNECTIONS = 710, /**< HCI: protocol error 0x0A */ + OI_HCIERR_ACL_CONNECTION_ALREADY_EXISTS = 711, /**< HCI: protocol error 0x0B */ + OI_HCIERR_COMMAND_DISALLOWED = 712, /**< HCI: protocol error 0x0C */ + OI_HCIERR_HOST_REJECTED_RESOURCES = 713, /**< HCI: protocol error 0x0D */ + OI_HCIERR_HOST_REJECTED_SECURITY = 714, /**< HCI: protocol error 0x0E */ + OI_HCIERR_HOST_REJECTED_PERSONAL_DEVICE = 715, /**< HCI: protocol error 0x0F */ + OI_HCIERR_HOST_TIMEOUT = 716, /**< HCI: protocol error 0x10 */ + OI_HCIERR_UNSUPPORTED = 717, /**< HCI: protocol error 0x11 */ + OI_HCIERR_INVALID_PARAMETERS = 718, /**< HCI: protocol error 0x12 */ + OI_HCIERR_OTHER_END_USER_DISCONNECT = 719, /**< HCI: protocol error 0x13 */ + OI_HCIERR_OTHER_END_LOW_RESOURCES = 720, /**< HCI: protocol error 0x14 */ + OI_HCIERR_OTHER_END_POWERING_OFF = 721, /**< HCI: protocol error 0x15 */ + OI_HCIERR_CONNECTION_TERMINATED_LOCALLY = 722, /**< HCI: protocol error 0x16 */ + OI_HCIERR_REPEATED_ATTEMPTS = 723, /**< HCI: protocol error 0x17 */ + OI_HCIERR_PAIRING_NOT_ALLOWED = 724, /**< HCI: protocol error 0x18 */ + OI_HCIERR_UNKNOWN_LMP_PDU = 725, /**< HCI: protocol error 0x19 */ + OI_HCIERR_UNSUPPORTED_REMOTE_FEATURE = 726, /**< HCI: protocol error 0x1A */ + OI_HCIERR_SCO_OFFSET_REJECTED = 727, /**< HCI: protocol error 0x1B */ + OI_HCIERR_SCO_INTERVAL_REJECTED = 728, /**< HCI: protocol error 0x1C */ + OI_HCIERR_SCO_AIR_MODE_REJECTED = 729, /**< HCI: protocol error 0x1D */ + OI_HCIERR_INVALID_LMP_PARMS = 730, /**< HCI: protocol error 0x1E */ + OI_HCIERR_UNSPECIFIED_ERROR = 731, /**< HCI: protocol error 0x1F */ + OI_HCIERR_UNSUPPORTED_LMP_PARAMETERS = 732, /**< HCI: protocol error 0x20 */ + OI_HCIERR_ROLE_CHANGE_NOT_ALLOWED = 733, /**< HCI: protocol error 0x21 */ + OI_HCIERR_LMP_RESPONSE_TIMEOUT = 734, /**< HCI: protocol error 0x22 */ + OI_HCIERR_LMP_ERROR_TRANS_COLLISION = 735, /**< HCI: protocol error 0x23 */ + OI_HCIERR_LMP_PDU_NOT_ALLOWED = 736, /**< HCI: protocol error 0x24 */ + OI_HCIERR_ENCRYPTION_MODE_NOT_ACCEPTABLE = 737, /**< HCI: protocol error 0x25 */ + OI_HCIERR_UNIT_KEY_USED = 738, /**< HCI: protocol error 0x26 */ + OI_HCIERR_QOS_NOT_SUPPORTED = 739, /**< HCI: protocol error 0x27 */ + OI_HCIERR_INSTANT_PASSED = 740, /**< HCI: protocol error 0x28 */ + OI_HCIERR_UNIT_KEY_PAIRING_UNSUPPORTED = 741, /**< HCI: protocol error 0x29 */ + OI_HCIERR_DIFFERENT_TRANS_COLLISION = 742, /**< HCI: protocol error 0x2A */ + OI_HCIERR_RESERVED_2B = 743, /**< HCI: protocol error 0x2B */ + OI_HCIERR_QOS_UNACCEPTABLE_PARAMETER = 744, /**< HCI: protocol error 0x2C */ + OI_HCIERR_QOS_REJECTED = 745, /**< HCI: protocol error 0x2D */ + OI_HCIERR_CHANNEL_CLASSIFICATION_NS = 746, /**< HCI: protocol error 0x2E */ + OI_HCIERR_INSUFFICIENT_SECURITY = 747, /**< HCI: protocol error 0x2F */ + OI_HCIERR_PARM_OUT_OF_MANDATORY_RANGE = 748, /**< HCI: protocol error 0x30 */ + OI_HCIERR_RESERVED_31 = 749, /**< HCI: protocol error 0x31 */ + OI_HCIERR_ROLE_SWITCH_PENDING = 750, /**< HCI: protocol error 0x32 */ + OI_HCIERR_RESERVED_33 = 751, /**< HCI: protocol error 0x33 */ + OI_HCIERR_RESERVED_SLOT_VIOLATION = 752, /**< HCI: protocol error 0x34 */ + OI_HCIERR_ROLE_SWITCH_FAILED = 753, /**< HCI: protocol error 0x35 */ + OI_HCIERR_EIR_TOO_LARGE = 754, /**< HCI: protocol error 0x36 */ + OI_HCIERR_SSP_NOT_SUPPORTED_BY_HOST = 755, /**< HCI: protocol error 0x37 */ + OI_HCIERR_HOST_BUSY_PAIRING = 756, /**< HCI: protocol error 0x38 */ + + OI_HCIERR_UNKNOWN_ERROR = 757, /**< HCI: unknown error code */ + OI_HCIERR_LAST_ERROR_VALUE = 757, /**< marker for last HCI protocol error */ + + OI_SDP_SPEC_ERROR = 800, /**< SDP: Base error status for mapping OI_STATUS codes to SDP errors */ + OI_SDP_INVALID_SERVICE_RECORD_HANDLE = (OI_SDP_SPEC_ERROR + 2), /**< SDP: protocol error Invalid Service Record Handle */ + OI_SDP_INVALID_REQUEST_SYNTAX = (OI_SDP_SPEC_ERROR + 3), /**< SDP: protocol error Invalid Request Syntax */ + OI_SDP_INVALID_PDU_SIZE = (OI_SDP_SPEC_ERROR + 4), /**< SDP: protocol error Invalid PDU Size */ + OI_SDP_INVALID_CONTINUATION_STATE = (OI_SDP_SPEC_ERROR + 5), /**< SDP: protocol error Invalid Continuation State */ + OI_SDP_INSUFFICIENT_RESOURCES = (OI_SDP_SPEC_ERROR + 6), /**< SDP: protocol error Insufficient Resources */ + OI_SDP_ERROR = 807, /**< SDP: server returned an error code */ + OI_SDP_CORRUPT_DATA_ELEMENT = 808, /**< SDP: Invalid or corrupt data element representation */ + OI_SDP_SERVER_NOT_CONNECTED = 810, /**< SDP: Attempt to disconnect from an unconnected server */ + OI_SDP_ACCESS_DENIED = 811, /**< SDP: Server denied access to server */ + OI_SDP_ATTRIBUTES_OUT_OF_ORDER = 812, /**< SDP: Attributes in attribute list not in ascending order */ + OI_SDP_DEVICE_DOES_NOT_SUPPORT_SDP = 813, /**< SDP: Tried to connect to a device that does not support SDP */ + OI_SDP_NO_MORE_DATA = 815, /**< SDP: Server does not have more continuation data */ + OI_SDP_REQUEST_PARAMS_TOO_LONG = 816, /**< SDP: Parameters for a request exceed the L2CAP buffer size */ + OI_SDP_REQUEST_PENDING = 817, /**< SDP: Cannot make a request when another request is being processed */ + OI_SDP_SERVER_CONNECT_FAILED = 819, /**< SDP: Failed attempt to connect to an SDP server */ + OI_SDP_SERVER_TOO_MANY_CONNECTIONS = 821, /**< SDP: Exceeded maximum number of simultaneous server connections */ + OI_SDP_NO_MATCHING_SERVICE_RECORD = 823, /**< SDP: No service record matched the UUID list */ + OI_SDP_PARTIAL_RESPONSE = 824, /**< SDP: Internal use only */ + OI_SDP_ILLEGAL_ARGUMENT = 825, /**< SDP: Illegal argument passed to an SDP function */ + OI_SDP_ATTRIBUTE_NOT_FOUND = 826, /**< SDP: A requested attribute was not found in a service record */ + OI_SDP_DATABASE_OUT_OF_RESOURCES = 827, /**< SDP: server database is out of memory */ + OI_SDP_SHORT_PDU = 829, /**< SDP: Not enough bytes in the packet */ + OI_SDP_TRANSACTION_ID_MISMATCH = 830, /**< SDP: Transaction Id was not as expected */ + OI_SDP_UNEXPECTED_RESPONSE_PDU_ID = 831, /**< SDP: Did not expect this response PDU */ + OI_SDP_REQUEST_TIMEOUT = 832, /**< SDP: Did not get a response within the timeout period */ + OI_SDP_INVALID_RESPONSE_SYNTAX = 833, /**< SDP: Response is not correctly formatted */ + OI_SDP_CONNECTION_TIMEOUT = 834, /**< SDP: Connection attempt timed out at a lower layer */ + OI_SDP_RESPONSE_DATA_ERROR = 835, /**< SDP: Response to a service request appears to be corrupt */ + OI_SDP_TOO_MANY_ATTRIBUTE_BYTES = 836, /**< SDP: Response contained more bytes than requested. */ + OI_SDP_TOO_MANY_SERVICE_RECORDS = 837, /**< SDP: Response contained more service records than requested. */ + OI_SDP_INVALID_CONNECTION_ID = 838, /**< SDP: Invalid connection ID in an SDP request */ + OI_SDP_CANNOT_SET_ATTRIBUTE = 839, /**< SDP: Attempt to set a dynamic attribute value failed */ + OI_SDP_BADLY_FORMED_ATTRIBUTE_VALUE = 840, /**< SDP: An attribute value has the wrong type or structure */ + OI_SDP_NO_ATTRIBUTE_LIST_TO_REMOVE = 841, /**< SDP: Attempt to remove a non-existent attribute list from a service record */ + OI_SDP_ATTRIBUTE_LIST_ALREADY_ADDED = 842, /**< SDP: An attribute list has already been added to the service record */ + OI_SDP_DATA_ELEMENT_TRUNCATED = 843, /**< SDP: Data element truncated (too few bytes) */ + + OI_RFCOMM_WRITE_IN_PROGRESS = 901, /**< RFCOMM: Write in progress */ + OI_RFCOMM_INVALID_BAUDRATE = 903, /**< RFCOMM: Invalid baudrate */ + OI_RFCOMM_INVALID_DATABIT = 904, /**< RFCOMM: Invalid databit */ + OI_RFCOMM_INVALID_STOPBIT = 905, /**< RFCOMM: Invalid stopbit */ + OI_RFCOMM_INVALID_PARITY = 906, /**< RFCOMM: Invalid parity */ + OI_RFCOMM_INVALID_PARITYTYPE = 907, /**< RFCOMM: Invalid paritytype */ + OI_RFCOMM_INVALID_FLOWCONTROL = 908, /**< RFCOMM: Invalid flowcontrol */ + OI_RFCOMM_SESSION_EXISTS = 909, /**< RFCOMM: Session exists */ + OI_RFCOMM_INVALID_CHANNEL = 910, /**< RFCOMM: Invalid channel */ + OI_RFCOMM_DLCI_EXISTS = 911, /**< RFCOMM: DLCI exists */ + OI_RFCOMM_LINK_NOT_FOUND = 912, /**< RFCOMM: Link not found */ + OI_RFCOMM_REMOTE_REJECT = 913, /**< RFCOMM: Remote reject */ + OI_RFCOMM_TEST_IN_PROGRESS = 915, /**< RFCOMM: Test in progress */ + OI_RFCOMM_SESSION_NOT_FOUND = 916, /**< RFCOMM: Session not found */ + OI_RFCOMM_INVALID_PACKET = 917, /**< RFCOMM: Invalid packet */ + OI_RFCOMM_FRAMESIZE_EXCEEDED = 918, /**< RFCOMM: Framesize exceeded */ + OI_RFCOMM_INVALID_DLCI = 920, /**< RFCOMM: Invalid dlci */ + OI_RFCOMM_SERVER_NOT_REGISTERED = 921, /**< RFCOMM: Server not registered */ + OI_RFCOMM_CREDIT_ERROR = 922, /**< RFCOMM: Credit error */ + OI_RFCOMM_NO_CHANNEL_NUMBER = 923, /**< RFCOMM: No channel number */ + OI_RFCOMM_QUERY_IN_PROGRESS = 924, /**< RFCOMM: Query in progress */ + OI_RFCOMM_SESSION_SHUTDOWN = 925, /**< RFCOMM: Session shutdown */ + OI_RFCOMM_LOCAL_DEVICE_DISCONNECTED = 926, /**< RFCOMM: Local device disconnected */ + OI_RFCOMM_REMOTE_DEVICE_DISCONNECTED = 927, /**< RFCOMM: Remote device disconnected */ + OI_RFCOMM_OUT_OF_SERVER_CHANNELS = 928, /**< RFCOMM: Out of server channels */ + + OI_DISPATCH_INVALID_CB_HANDLE = 1001, /**< Dispatcher was handed an invalid callback handle */ + OI_DISPATCH_TABLE_OVERFLOW = 1002, /**< Dispatcher table is full */ + + OI_TEST_UNKNOWN_TEST = 1101, /**< TEST: Unknown test */ + OI_TEST_FAIL = 1102, /**< TEST: Fail */ + + OI_HCITRANS_CANNOT_CONNECT_TO_DEVICE = 1201, /**< TRANSPORT: Cannot connect to device */ + OI_HCITRANS_BUFFER_TOO_SMALL = 1203, /**< TRANSPORT: Buffer too small */ + OI_HCITRANS_NULL_DEVICE_HANDLE = 1204, /**< TRANSPORT: Null device handle */ + OI_HCITRANS_IO_ERROR = 1205, /**< TRANSPORT: IO error */ + OI_HCITRANS_DEVICE_NOT_READY = 1206, /**< TRANSPORT: Device not ready */ + OI_HCITRANS_FUNCTION_NOT_SUPPORTED = 1207, /**< TRANSPORT: Function not supporteD */ + OI_HCITRANS_ACCESS_DENIED = 1209, /**< TRANSPORT: win32 */ + OI_HCITRANS_ACL_DATA_ERROR = 1210, /**< TRANSPORT: ACL data error */ + OI_HCITRANS_SCO_DATA_ERROR = 1211, /**< TRANSPORT: SCO data error */ + OI_HCITRANS_EVENT_DATA_ERROR = 1212, /**< TRANSPORT: HCI event data error */ + OI_HCITRANS_INTERNAL_ERROR = 1214, /**< TRANSPORT: Internal error in the transport */ + OI_HCITRANS_LINK_NOT_ACTIVE = 1215, /**< TRANSPORT: Link to the device is not currently active */ + OI_HCITRANS_INITIALIZING = 1216, /**< TRANSPORT: Transport is initializing */ + + OI_DEVMGR_NO_CONNECTION = 1301, /**< DEVMGR: No connection */ + OI_DEVMGR_HARDWARE_ERROR = 1305, /**< DEVMGR: error reported by HCI */ + OI_DEVMGR_PENDING_CONNECT_LIST_FULL = 1307, /**< DEVMGR: Pending connect list full */ + OI_DEVMGR_CONNECTION_LIST_FULL = 1309, /**< DEVMGR: Connection list full */ + OI_DEVMGR_NO_SUCH_CONNECTION = 1310, /**< DEVMGR: No such connection */ + OI_DEVMGR_INQUIRY_IN_PROGRESS = 1311, /**< DEVMGR: Inquiry in progress */ + OI_DEVMGR_PERIODIC_INQUIRY_ACTIVE = 1312, /**< DEVMGR: Periodic inquiry active */ + OI_DEVMGR_NO_INQUIRIES_ACTIVE = 1313, /**< DEVMGR: can not cancel/exit if not active */ + OI_DEVMGR_DUPLICATE_CONNECTION = 1314, /**< DEVMGR: internal error */ + OI_DEVMGR_DUPLICATE_EVENT_CALLBACK = 1316, /**< DEVMGR: attempt to register same callback twice */ + OI_DEVMGR_EVENT_CALLBACK_LIST_FULL = 1317, /**< DEVMGR: can not register event callback, list is full */ + OI_DEVMGR_EVENT_CALLBACK_NOT_FOUND = 1318, /**< DEVMGR: attempt to unregister callback failed */ + OI_DEVMGR_BUSY = 1319, /**< DEVMGR: some operations can only execute one at a time */ + OI_DEVMGR_ENUM_UNEXPECTED_INQ_COMPLETE = 1320, /**< DEVMGR: inquiry complete event in inappropriate enumeration state */ + OI_DEVMGR_ENUM_UNEXPECTED_INQ_RESULT = 1321, /**< DEVMGR: inquiry result event in inappropriate enumeration state */ + OI_DEVMGR_ENUM_DATABASE_FULL = 1322, /**< DEVMGR: device enumeration, database is full, couldn't add a new device */ + OI_DEVMGR_ENUM_INQUIRIES_OVERLAP = 1323, /**< DEVMGR: device enumeration, periodic inquiries occurring too close together */ + OI_DEVMGR_UNKNOWN_LINK_TYPE = 1324, /**< DEVMGR: HCI connect request with unkown link type */ + OI_DEVMGR_PARAM_IO_ACTIVE = 1325, /**< DEVMGR: request for parameter read/write while param read/write active */ + OI_DEVMGR_UNKNOWN_IAC_LAP = 1326, /**< DEVMGR: unrecognized IAC LAP */ + OI_DEVMGR_SCO_ALREADY_REGISTERED = 1327, /**< DEVMGR: only one application can use SCO */ + OI_DEVMGR_SCO_NOT_REGISTERED = 1328, /**< DEVMGR: SCO applications must register before using the API */ + OI_DEVMGR_SCO_WITHOUT_ACL = 1329, /**< DEVMGR: Got SCO connection but there is no underlying ACL connection */ + OI_DEVMGR_NO_SUPPORT = 1330, /**< DEVMGR: Request is not supported by the device */ + OI_DEVMGR_WRITE_POLICY_FAILED = 1331, /**< DEVMGR: connection attempt failed - unable to write link policy */ + OI_DEVMGR_NOT_IN_MASTER_MODE = 1332, /**< DEVMGR: OI_DEVMGR EndMasterMode without prior OI_DEVMGR_BeginMasterMode */ + OI_DEVMGR_POLICY_VIOLATION = 1333, /**< DEVMGR: low-power request is rejected - link policy does not allow it */ + OI_DEVMGR_BUSY_TIMEOUT = 1334, /**< DEVMGR: queued operation timed out while in the queue; \n + timeout configurable via @ref OI_CONFIG_DEVMGR::connectQueueTimeoutSecs "connectQueueTimeoutSecs" */ + OI_DEVMGR_REENCRYPT_FAILED = 1335, /**< DEVMGR: failed to re-encrypt link after role switch */ + OI_DEVMGR_ROLE_POLICY_CONFLICT = 1336, /**< DEVMGR: requested role conflicts with current policy */ + OI_DEVMGR_BAD_INTERVAL = 1337, /**< DEVMGR: current linkTO outside range of requested min/max interval */ + OI_DEVMGR_INVALID_SCO_HANDLE = 1338, /**< DEVMGR: HCI SCO event, invalid handle */ + OI_DEVMGR_CONNECTION_OVERLAP = 1339, /**< DEVMGR: Connection failed due to race condition with remote side */ + OI_DEVMGR_ORPHAN_SUBRATE_COMPLETE = 1340, /**< DEVMGR: sniff subrate complete, but no callback */ + OI_DEVMGR_EIR_RESPONSE_2_LARGE = 1341, /**< DEVMGR: eir builder, response length would exceed spec max */ + + OI_SECMGR_NO_POLICY = 1401, /**< SECMGR: no security policy has been established */ + OI_SECMGR_INTERNAL_ERROR = 1402, /**< SECMGR: internal inconsistency */ + OI_SECMGR_ORPHANED_CALLBACK = 1403, /**< SECMGR: we've been called back, but CB context is gone */ + OI_SECMGR_BUSY = 1404, /**< SECMGR: configure and access request cannot be concurrent */ + OI_SECMGR_DEVICE_NOT_TRUSTED = 1405, /**< SECMGR: l2cap access denied - device is not trusted */ + OI_SECMGR_DEVICE_ENCRYPT_FAIL = 1407, /**< SECMGR: l2cap access denied - failed to start encryption */ + OI_SECMGR_DISCONNECTED_FAIL = 1408, /**< SECMGR: l2cap access denied - disconnected */ + OI_SECMGR_ACCESS_PENDING = 1409, /**< SECMGR: l2cap access request is still pending */ + OI_SECMGR_PIN_CODE_TOO_SHORT = 1410, /**< SECMGR: Higher-layer process gave us a pin code that is too short */ + OI_SECMGR_UNKNOWN_ENCRYPT_VALUE = 1411, /**< SECMGR: got EncryptionChange event, unknown encryption enable value */ + OI_SECMGR_INVALID_POLICY = 1412, /**< SECMGR: the specified security policy is not valid for security mode */ + OI_SECMGR_AUTHORIZATION_FAILED = 1413, /**< SECMGR: device authorization failed */ + OI_SECMGR_ENCRYPTION_FAILED = 1414, /**< SECMGR: device encryption failed */ + OI_SECMGR_UNIT_KEY_UNSUPPORTED = 1415, /**< SECMGR: authentication failed due to non-support of unit keys */ + OI_SECMGR_NOT_REGISTERED = 1416, /**< SECMGR: required registrations have not yet occurred */ + OI_SECMGR_ILLEGAL_WRITE_SSP_MODE = 1417, /**< SECMGR: 2.1 HCI spec does not allow SSP mode to be disabled */ + OI_SECMGR_INVALID_SEC_LEVEL = 1418, /**< SECMGR: security level for a service is not a valid value */ + OI_SECMGR_INSUFFICIENT_LINK_KEY = 1419, /**< SECMGR: link key type is not sufficient to meet service requirements */ + OI_SECMGR_INVALID_KEY_TYPE = 1420, /**< SECMGR: link key type is not a valid value */ + OI_SECMGR_SSP_NOT_ENCRYPTED = 1421, /**< SECMGR: ssp required encryption on incoming link */ + OI_SECMGR_ORPHAN_EVENT = 1422, /**< SECMGR: some HCI security event unrelated to current processes */ + OI_SECMGR_NOT_BONDABLE = 1423, /**< SECMGR: not in bondable mode */ + + OI_TCS_INVALID_ELEMENT_TYPE = 1602, /**< TCS: element type is invalid */ + OI_TCS_INVALID_PACKET = 1603, /**< TCS: packet is invalide */ + OI_TCS_CALL_IN_PROGRESS = 1604, /**< TCS: call is in progress */ + OI_TCS_NO_CALL_IN_PROGRESS = 1605, /**< TCS: no call in progress */ + + OI_OBEX_CONTINUE = 1701, /**< OBEX: Continue processing OBEX request */ + OI_OBEX_COMMAND_ERROR = 1702, /**< OBEX: An unrecognized OBEX command opcode */ + OI_OBEX_CONNECTION_TIMEOUT = 1703, /**< OBEX: Timeout waiting for a response to a request */ + OI_OBEX_CONNECT_FAILED = 1704, /**< OBEX: An OBEX connection request did not succeed */ + OI_OBEX_DISCONNECT_FAILED = 1705, /**< OBEX: A disconnect failed probably because the connection did not exist */ + OI_OBEX_ERROR = 1706, /**< OBEX: Unspecified OBEX error */ + OI_OBEX_INCOMPLETE_PACKET = 1707, /**< OBEX: Packet too short or corrupt */ + OI_OBEX_LENGTH_REQUIRED = 1708, /**< OBEX: Length header required in OBEX command */ + OI_OBEX_NOT_CONNECTED = 1709, /**< OBEX: No connection to OBEX server */ + OI_OBEX_NO_MORE_CONNECTIONS = 1710, /**< OBEX: Reached max connections limit */ + OI_OBEX_OPERATION_IN_PROGRESS = 1711, /**< OBEX: Another operation is still in progress on a connection */ + OI_OBEX_PUT_RESPONSE_ERROR = 1712, /**< OBEX: An error in the response to a PUT command */ + OI_OBEX_GET_RESPONSE_ERROR = 1713, /**< OBEX: An error in the response to a GET command */ + OI_OBEX_REQUIRED_HEADER_NOT_FOUND = 1714, /**< OBEX: packet was missing a required header */ + OI_OBEX_SERVICE_UNAVAILABLE = 1715, /**< OBEX: Unown OBEX target or required service */ + OI_OBEX_TOO_MANY_HEADER_BYTES = 1716, /**< OBEX: Headers will not fit in single OBEX packet */ + OI_OBEX_UNKNOWN_COMMAND = 1717, /**< OBEX: Unrecognized OBEX command */ + OI_OBEX_UNSUPPORTED_VERSION = 1718, /**< OBEX: Version mismatch */ + OI_OBEX_CLIENT_ABORTED_COMMAND = 1719, /**< OBEX: server received abort command */ + OI_OBEX_BAD_PACKET = 1720, /**< OBEX: Any malformed OBEX packet */ + OI_OBEX_BAD_REQUEST = 1721, /**< OBEX: Maps to OBEX response of the same name */ + OI_OBEX_OBJECT_OVERFLOW = 1723, /**< OBEX: Too many bytes received. */ + OI_OBEX_NOT_FOUND = 1724, /**< OBEX: Maps to obex response of same name */ + OI_OBEX_ACCESS_DENIED = 1735, /**< OBEX: Object could not be read or written. */ + OI_OBEX_VALUE_NOT_ACCEPTABLE = 1736, /**< OBEX: Value in a command was not in the acceptable range. */ + OI_OBEX_PACKET_OVERFLOW = 1737, /**< OBEX: Buffer will not fit in a single OBEX packet. */ + OI_OBEX_NO_SUCH_FOLDER = 1738, /**< OBEX: Error returned by a setpath operation. */ + OI_OBEX_NAME_REQUIRED = 1739, /**< OBEX: Name must be non-null and non-empty. */ + OI_OBEX_PASSWORD_TOO_LONG = 1740, /**< OBEX: Password exceeds implementation imposed length limit. */ + OI_OBEX_PRECONDITION_FAILED = 1741, /**< OBEX: response Precondition Failed */ + OI_OBEX_UNAUTHORIZED = 1742, /**< OBEX: authentication was not successful. */ + OI_OBEX_NOT_IMPLEMENTED = 1743, /**< OBEX: Unimplemented feature. */ + OI_OBEX_INVALID_AUTH_DIGEST = 1744, /**< OBEX: An authentication digest was bad. */ + OI_OBEX_INVALID_OPERATION = 1745, /**< OBEX: Operation not allowed at this time. */ + OI_OBEX_DATABASE_FULL = 1746, /**< OBEX: Sync database full. */ + OI_OBEX_DATABASE_LOCKED = 1747, /**< OBEX: Sync database locked. */ + OI_OBEX_INTERNAL_SERVER_ERROR = 1748, /**< OBEX: response Internal Server Error */ + OI_OBEX_UNSUPPORTED_MEDIA_TYPE = 1749, /**< OBEX: response Unsupported Media Type */ + OI_OBEX_PARTIAL_CONTENT = 1750, /**< OBEX: response Partial Content */ + OI_OBEX_METHOD_NOT_ALLOWED = 1751, /**< OBEX: response Method Not Allowed */ + OI_OBEXSRV_INCOMPLETE_GET = 1752, /**< OBEX: Indicates to a GET handler that the request phase is still in progress */ + OI_OBEX_FOLDER_BROWSING_NOT_ALLOWED = 1753, /**< OBEX: Indicates that an FTP server does not allow folder browsing */ + OI_OBEX_SERVER_FORCED_DISCONNECT = 1754, /**< OBEX: connection was forcibly terminated by the server */ + OI_OBEX_OFS_ERROR = 1755, /**< OBEX: OPP object file system error occurred */ + OI_OBEX_FILEOP_ERROR = 1756, /**< OBEX: FTP/PBAP file operation system error occurred */ + OI_OBEX_USERID_TOO_LONG = 1757, /**< OBEX: User Id exceeds spec limited length limit. */ + + OI_HANDSFREE_EVENT_REPORTING_DISABLED = 1801, /**< HANDSFREE: Event reporting disabled */ + OI_HANDSFREE_NOT_CONNECTED = 1802, /**< HANDSFREE: Not connected */ + OI_HANDSFREE_SERVICE_NOT_STARTED = 1803, /**< HANDSFREE: Cannot connect to handsfree AG if handsfree service not started */ + OI_HANDSFREE_AG_SERVICE_NOT_STARTED = 1804, /**< HANDSFREE: Cannot connect to handsfree device if handsfree AG service not started */ + OI_HANDSFREE_COMMAND_IN_PROGRESS = 1805, /**< HANDSFREE: Cannot accept a command at this time */ + OI_HANDSFREE_AUDIO_ALREADY_CONNECTED = 1806, /**< HANDSFREE: Audio is already connected */ + OI_HANDSFREE_AUDIO_NOT_CONNECTED = 1807, /**< HANDSFREE: Audio is not connected */ + OI_HANDSFREE_FEATURE_NOT_SUPPORTED = 1808, /**< HANDSFREE: Local or remote feature not supported for requested command */ + + OI_HEADSET_SERVICE_NOT_STARTED = 1901, /**< HEADSET: Cannot connect to headset AG if headset service not started */ + OI_HEADSET_AG_SERVICE_NOT_STARTED = 1902, /**< HEADSET: Cannot connect to headset device if headset AG service not started */ + OI_HEADSET_COMMAND_IN_PROGRESS = 1903, /**< HEADSET: Cannot accept a command at this time */ + + OI_BNEP_INVALID_MTU = 2001, /**< BNEP: The remote device cannot support the minimum BNEP MTU */ + OI_BNEP_SETUP_TIMEOUT = 2002, /**< BNEP: The setup request timed out. */ + OI_BNEP_SERVICE_NOT_REGISTERED = 2003, /**< BNEP: The requested service was not found. */ + OI_BNEP_INVALID_HANDLE = 2004, /**< BNEP: The specified connection handle is not valid. */ + OI_BNEP_RESPONSE_TIMEOUT = 2005, /**< BNEP: The timer for receiving a response has expired. */ + OI_BNEP_INVALID_CONNECTION = 2006, /**< BNEP: Invalid connection */ + OI_BNEP_INVALID_FILTER = 2007, /**< BNEP: The supplied filter was invalid. */ + OI_BNEP_CONNECTION_EXISTS = 2008, /**< BNEP: An attempt was made to create a duplicate connection. */ + OI_BNEP_NOT_INITIALIZED = 2009, /**< BNEP: Init has not been called */ + OI_BNEP_CONNECT_BASE = 2010, /**< BNEP: connection response codes */ + OI_BNEP_CONNECT_FAILED_INVALID_DEST_UUID = 2011, /**< BNEP: connect response code Invalid Dest UUID */ + OI_BNEP_CONNECT_FAILED_INVALID_SOURCE_UUID = 2012, /**< BNEP: connect response code Invalid Source UUID */ + OI_BNEP_CONNECT_FAILED_INVALID_UUID_SIZE = 2013, /**< BNEP: connect response code Invalid UUID Size */ + OI_BNEP_CONNECT_FAILED_NOT_ALLOWED = 2014, /**< BNEP: connect response code Not Allowed */ + OI_BNEP_FILTER_NET_BASE = 2020, /**< BNEP: filter response codes */ + OI_BNEP_FILTER_NET_UNSUPPORTED_REQUEST = 2021, /**< BNEP: filter response code Unsupported Request */ + OI_BNEP_FILTER_NET_FAILED_INVALID_PROTOCOL_TYPE = 2022, /**< BNEP: filter response code Invalid Protocol Type */ + OI_BNEP_FILTER_NET_FAILED_MAX_LIMIT_REACHED = 2023, /**< BNEP: filter response code Max Limit Reached */ + OI_BNEP_FILTER_NET_FAILED_SECURITY = 2024, /**< BNEP: filter response code Security */ + OI_BNEP_FILTER_MULTI_BASE = 2030, /**< BNEP: multicast response codes */ + OI_BNEP_FILTER_MULTI_UNSUPPORTED_REQUEST = 2031, /**< BNEP: multicast response code Unsupported Request */ + OI_BNEP_FILTER_MULTI_FAILED_INVALID_ADDRESS = 2032, /**< BNEP: multicast response code Invalid Address */ + OI_BNEP_FILTER_MULTI_FAILED_MAX_LIMIT_REACHED = 2033, /**< BNEP: multicast response code Max Limit Reached */ + OI_BNEP_FILTER_MULTI_FAILED_SECURITY = 2034, /**< BNEP: multicast response code Security */ + OI_BNEP_LOCAL_DEVICE_MUST_BE_MASTER = 2040, /**< BNEP: Device must be master of the piconet for this function */ + OI_BNEP_PACKET_FILTERED_OUT = 2041, /**< BNEP: Packet did not pass current filters */ + + OI_NETIFC_UP_FAILED = 2101, /**< NETIFC: Could not bring up network interface */ + OI_NETIFC_COULD_NOT_CREATE_THREAD = 2102, /**< NETIFC: Network interface could not create a read thread */ + OI_NETIFC_INITIALIZATION_FAILED = 2103, /**< NETIFC: Error in network interface initialization */ + OI_NETIFC_INTERFACE_ALREADY_UP = 2104, /**< NETIFC: Network interface is already up */ + OI_NETIFC_INTERFACE_NOT_UP = 2105, /**< NETIFC: Network interface is not up */ + OI_NETIFC_PACKET_TOO_BIG = 2106, /**< NETIFC: The packet is too big */ + + OI_PAN_ROLE_ALREADY_REGISTERED = 2201, /**< PAN: This PAN role was already registered */ + OI_PAN_ROLE_NOT_ALLOWED = 2202, /**< PAN: The PAN role is not currently allowed */ + OI_PAN_INCOMPATIBLE_ROLES = 2203, /**< PAN: Only certain local and remote role combinations are permitted */ + OI_PAN_INVALID_ROLE = 2204, /**< PAN: Role specified is not one the defined PAN roles */ + OI_PAN_CONNECTION_IN_PROGRESS = 2205, /**< PAN: A PAN connection is currently being established */ + OI_PAN_USER_ALREADY_CONNECTED = 2206, /**< PAN: PAN user role only allows a single connection */ + OI_PAN_DEVICE_CONNECTED = 2207, /**< PAN: A PAN connection already exists to specified device */ + + OI_CODEC_SBC_NO_SYNCWORD = 2301, /**< CODEC: Couldn't find an SBC SYNCWORD */ + OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA = 2302, /**< CODEC: Not enough data provided to decode an SBC header */ + OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA = 2303, /**< CODEC: Decoded the header, but not enough data to contain the rest of the frame */ + OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA = 2304, /**< CODEC: Not enough audio data for this frame */ + OI_CODEC_SBC_CHECKSUM_MISMATCH = 2305, /**< CODEC: The frame header didn't match the checksum */ + OI_CODEC_SBC_PARTIAL_DECODE = 2306, /**< CODEC: Decoding was successful, but frame data still remains. Next call will provide audio without consuming input data. */ + + OI_FIFOQ_QUEUE_NOT_ALIGNED = 2401, /**< FIFOQ: queue must be 32-bit aligned */ + OI_FIFOQ_INVALID_Q = 2402, /**< FIFOQ: queue parameter is not a valid queue */ + OI_FIFOQ_BUF_TOO_LARGE = 2403, /**< FIFOQ: attempt to queue a buffer which is too large */ + OI_FIFOQ_FULL = 2404, /**< FIFOQ: enqueue() failed, queue is full */ + OI_FIFOQ_NOT_ALLOCATED = 2405, /**< FIFOQ: Enqueue QBuf() failed, buffer not allocated */ + OI_FIFOQ_INVALID_DATA_PTR = 2406, /**< FIFOQ: Enqueue QBuf() failed, data pointer does not match */ + + OI_HID_HOST_SERVICE_NOT_STARTED = 2601, /**< HID: Cannot connect to a HID device unless HID host is started */ + OI_HID_DEVICE_SERVICE_NOT_STARTED = 2602, /**< HID: Cannot connect to a HID host unless HID device is started */ + + OI_AT_ERROR = 2701, /**< AT: ERROR response */ + OI_AT_NO_CARRIER = 2702, /**< AT: NO CARRIER response */ + OI_AT_BUSY = 2703, /**< AT: BUSY response */ + OI_AT_NO_ANSWER = 2704, /**< AT: NO ANSWER response */ + OI_AT_DELAYED = 2705, /**< AT: DELAYED response */ + OI_AT_BLACKLISTED = 2706, /**< AT: BLACKLISTED response */ + OI_AT_CME_ERROR = 2707, /**< AT: +CME ERROR response */ + OI_AT_CMS_ERROR = 2708, /**< AT: +CMS ERROR response */ + + OI_BLST_CHARACTER_TIMEOUT = 2801, /**< BLST: Timeout expired while waiting for a character from the client. */ + OI_BLST_ACKNOWLDGE_TIMEOUT = 2802, /**< BLST: Timeout expired while waiting for event acknowledgment from the client */ + OI_BLST_TX_NOT_READY = 2803, /**< BLST: BLST is not ready to send a BHAPI message to the client. */ + OI_BLST_TX_BUSY = 2804, /**< BLST: BLST transmit buffer is in use. */ + + OI_AVDTP_CONNECTION_SEQ_ERROR = 2901, /**< AVDTP: sequencing of signalling/media channel connections broken. */ + OI_AVDTP_OUT_OF_RESOURCES = 2902, /**< AVDTP: Tried to allocate too many endpoints or signalling channels. */ + + OI_PBAP_REPOSITORY_NOT_SET = 3001, /**< PBAP: Phonebook repository must be set for operation to complete. */ + OI_PBAP_PHONEBOOK_NOT_SET = 3002, /**< PBAP: Phonebook be set for operation to complete. */ + + OI_AADP_BAD_ENDPOINT = 3101, /**< AADP: Invalid local endpoint specified */ + OI_AADP_BAD_STATE = 3102, /**< AADP: AADP State is not correct for this operation. */ + + OI_UNICODE_INVALID_SOURCE = 3200, /**< Unicode Conversion: Source string has invalid character encoding. */ + OI_UNICODE_SOURCE_EXHAUSTED = 3201, /**< Unicode Conversion: Incomplete Unicode character at end of source buffer. */ + OI_UNICODE_DESTINATION_EXHAUSTED = 3202, /**< Unicode Conversion: Destination buffer not large enough to hold resulting Unicode string. */ + + OI_AVRCP_TOO_MANY_CONNECTIONS = 3300, /**< AVRCP: Exceeded maximum number of simultaneous AVCTP connections. */ + OI_AVRCP_NOT_IMPLEMENTED = 3301, /**< AVRCP: The target does not implement the command specified by the opcode and operand. */ + OI_AVRCP_REJECTED = 3302, /**< AVRCP: The target cannot respond because of invalid operands in command packet. */ + OI_AVRCP_INVALID_RESPONSE = 3303, /**< AVRCP: The controller received the response with invalid parameters */ + OI_AVRCP_RESPONSE_PACKET_OVERFLOW = 3304, /**< AVRCP: The response message does not fir in one AVRCP packet (512 bytes), has to be fragmented. */ + OI_AVRCP_RESPONSE_INVALID_PDU = 3305, /**< AVRCP: Command rejected: target received a PDU that it did not understand. */ + OI_AVRCP_RESPONSE_INVALID_PARAMETER = 3306, /**< AVRCP: Command rejected: target received a PDU with a parameter ID that it did not understand. */ + OI_AVRCP_RESPONSE_PARAMETER_NOT_FOUND = 3307, /**< AVRCP: Command rejected: specified parameter not found, sent if the parameter ID is understood, but content is wrong or corrupted.*/ + OI_AVRCP_RESPONSE_INTERNAL_ERROR = 3308, /**< AVRCP: Command rejected: target detected other error conditions. */ + OI_MAX_BM3_STATUS_VAL, /* Maximum BM3 status code */ + + /* Status code values reserved for BM3 SDK platform-specific implementations */ + OI_STATUS_RESERVED_FOR_BCOT = 9000, + + /* Status code values reserved for BHAPI products */ + OI_STATUS_RESERVED_FOR_BHAPI = 9200, + + /* Status code values reserved for Soundabout products */ + OI_STATUS_RESERVED_FOR_SOUNDABOUT = 9400, + + /* + * Status code values greater than or equal to this value are reserved for use by applications. + * However, because of differences between compilers, and differences between 16-bit and 32-bit + * platforms custom status codes should be in the 16-bit range, so status codes can range from 0 + * to 65534, inclusive (65535 is reserved) + */ + OI_STATUS_RESERVED_FOR_APPS = 10000, + + + + OI_STATUS_NONE = 0xffff /**< Special status code to indicate that there is no status. (Only to be used for special cases involving OI_SLOG_ERROR() and OI_SLOG_WARNING().) */ + +} OI_STATUS; + + +/* Remeber to update the #define below when new reserved blocks are added to + * the list above. */ +#define OI_NUM_RESERVED_STATUS_BLOCKS 4 /**< Number of status code blocks reserved, including user apps */ + + +/** + * Test for success + */ +#define OI_SUCCESS(x) ((x) == OI_OK) + +/*****************************************************************************/ +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_STATUS_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_stddefs.h b/components/ble/ble_stack/sbc/dec/oi_stddefs.h new file mode 100644 index 00000000..9ab424db --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_stddefs.h @@ -0,0 +1,232 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 OI_STDDEFS_H +#define OI_STDDEFS_H +/** + * @file + * This file contains BM3 standard type definitions. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_cpu_dep.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FALSE +#define FALSE 0 /**< This define statement sets FALSE as a preprocessor alias for 0. */ +#endif + +#ifndef TRUE +#define TRUE (!FALSE) /**< This define statement sets TRUE as a preprocessor alias for !FALSE. */ +#endif + +#ifdef HEW_TOOLCHAIN +#ifdef NULL +#undef NULL /**< Override HEW toolchain NULL definition */ +#endif +#define NULL 0 /**< HEW toolchain does not allow us to compare (void*) type to function pointer */ +#else +#ifndef NULL +#define NULL ((void*)0) /**< This define statement sets NULL as a preprocessor alias for (void*)0 */ +#endif +#endif + +/** + * @name Maximum and minimum values for basic types + * @{ + */ +#define OI_INT8_MIN ((OI_INT8)0x80) /**< decimal value: -128 */ +#define OI_INT8_MAX ((OI_INT8)0x7F) /**< decimal value: 127 */ +#define OI_INT16_MIN ((OI_INT16)0x8000) /**< decimal value: -32768 */ +#define OI_INT16_MAX ((OI_INT16)0x7FFF) /**< decimal value: 32767 */ +#define OI_INT32_MIN ((OI_INT32)0x80000000) /**< decimal value: -2,147,483,648 */ +#define OI_INT32_MAX ((OI_INT32)0x7FFFFFFF) /**< decimal value: 2,147,483,647 */ +#define OI_UINT8_MIN ((OI_UINT8)0) /**< decimal value: 0 */ +#define OI_UINT8_MAX ((OI_UINT8)0xFF) /**< decimal value: 255 */ +#define OI_UINT16_MIN ((OI_UINT16)0) /**< decimal value: 0 */ +#define OI_UINT16_MAX ((OI_UINT16)0xFFFF) /**< decimal value: 65535 */ +#define OI_UINT32_MIN ((OI_UINT32)0) /**< decimal value: 0 */ +#define OI_UINT32_MAX ((OI_UINT32)0xFFFFFFFF) /**< decimal value: 4,294,967,295 */ + +/** + * @} + */ + +/** + * @name Integer types required by the Service Discovery Protocol + * @{ + */ + +/** unsigned 64-bit integer as a structure of two unsigned 32-bit integers */ +typedef struct { + OI_UINT32 I1; /**< most significant 32 bits */ + OI_UINT32 I2; /**< least significant 32 bits */ +} OI_UINT64; + +#define OI_UINT64_MIN { (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_UINT64_MAX { (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** signed 64-bit integer as a structure of one unsigned 32-bit integer and one signed 32-bit integer */ +typedef struct { + OI_INT32 I1; /**< most significant 32 bits as a signed integer */ + OI_UINT32 I2; /**< least significant 32 bits as an unsigned integer */ +} OI_INT64; + +#define OI_INT64_MIN { (OI_INT32)0x80000000, (OI_UINT32)0x00000000 } +#define OI_INT64_MAX { (OI_INT32)0X7FFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** unsigned 128-bit integer as a structure of four unsigned 32-bit integers */ +typedef struct { + OI_UINT32 I1; /**< most significant 32 bits */ + OI_UINT32 I2; /**< second-most significant 32 bits */ + OI_UINT32 I3; /**< third-most significant 32 bits */ + OI_UINT32 I4; /**< least significant 32 bits */ +} OI_UINT128; + +#define OI_UINT128_MIN { (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_UINT128_MAX { (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** signed 128-bit integer as a structure of three unsigned 32-bit integers and one signed 32-bit integer */ +typedef struct { + OI_INT32 I1; /**< most significant 32 bits as a signed integer */ + OI_UINT32 I2; /**< second-most significant 32 bits as an unsigned integer */ + OI_UINT32 I3; /**< third-most significant 32 bits as an unsigned integer */ + OI_UINT32 I4; /**< least significant 32 bits as an unsigned integer */ +} OI_INT128; + +#define OI_INT128_MIN { (OI_UINT32)0x80000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000, (OI_UINT32)0x00000000 } +#define OI_INT128_MAX { (OI_UINT32)0X7FFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF, (OI_UINT32)0XFFFFFFFF } + +/** + * @} + */ + + +/** + * type for ASCII character data items + */ +typedef char OI_CHAR; + +/** + * type for double-byte character data items + */ +typedef OI_UINT16 OI_CHAR16; + +/** + * types for UTF encoded strings. + */ +typedef OI_UINT8 OI_UTF8; +typedef OI_UINT16 OI_UTF16; +typedef OI_UINT32 OI_UTF32; + + +/** + * @name Single-bit operation macros + * @{ + * In these macros, x is the data item for which a bit is to be tested or set and y specifies which bit + * is to be tested or set. + */ + +/** This macro's value is TRUE if the bit specified by y is set in data item x. */ +#define OI_BIT_TEST(x,y) ((x) & (y)) + +/** This macro's value is TRUE if the bit specified by y is not set in data item x. */ +#define OI_BIT_CLEAR_TEST(x,y) (((x) & (y)) == 0) + +/** This macro sets the bit specified by y in data item x. */ +#define OI_BIT_SET(x,y) ((x) |= (y)) + +/** This macro clears the bit specified by y in data item x. */ +#define OI_BIT_CLEAR(x,y) ((x) &= ~(y)) + +/** @} */ + +/** + * The OI_ARRAYSIZE macro is set to the number of elements in an array + * (instead of the number of bytes, which is returned by sizeof()). + */ + +#ifndef OI_ARRAYSIZE +#define OI_ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/** + * @name Preprocessor aliases for individual bit positions + * Bits are defined here only if they are not already defined. + * @{ + */ + +#ifndef BIT0 + +#define BIT0 0x00000001 /**< preprocessor alias for 32-bit value with bit 0 set, used to specify this single bit */ +#define BIT1 0x00000002 /**< preprocessor alias for 32-bit value with bit 1 set, used to specify this single bit */ +#define BIT2 0x00000004 /**< preprocessor alias for 32-bit value with bit 2 set, used to specify this single bit */ +#define BIT3 0x00000008 /**< preprocessor alias for 32-bit value with bit 3 set, used to specify this single bit */ +#define BIT4 0x00000010 /**< preprocessor alias for 32-bit value with bit 4 set, used to specify this single bit */ +#define BIT5 0x00000020 /**< preprocessor alias for 32-bit value with bit 5 set, used to specify this single bit */ +#define BIT6 0x00000040 /**< preprocessor alias for 32-bit value with bit 6 set, used to specify this single bit */ +#define BIT7 0x00000080 /**< preprocessor alias for 32-bit value with bit 7 set, used to specify this single bit */ +#define BIT8 0x00000100 /**< preprocessor alias for 32-bit value with bit 8 set, used to specify this single bit */ +#define BIT9 0x00000200 /**< preprocessor alias for 32-bit value with bit 9 set, used to specify this single bit */ +#define BIT10 0x00000400 /**< preprocessor alias for 32-bit value with bit 10 set, used to specify this single bit */ +#define BIT11 0x00000800 /**< preprocessor alias for 32-bit value with bit 11 set, used to specify this single bit */ +#define BIT12 0x00001000 /**< preprocessor alias for 32-bit value with bit 12 set, used to specify this single bit */ +#define BIT13 0x00002000 /**< preprocessor alias for 32-bit value with bit 13 set, used to specify this single bit */ +#define BIT14 0x00004000 /**< preprocessor alias for 32-bit value with bit 14 set, used to specify this single bit */ +#define BIT15 0x00008000 /**< preprocessor alias for 32-bit value with bit 15 set, used to specify this single bit */ +#define BIT16 0x00010000 /**< preprocessor alias for 32-bit value with bit 16 set, used to specify this single bit */ +#define BIT17 0x00020000 /**< preprocessor alias for 32-bit value with bit 17 set, used to specify this single bit */ +#define BIT18 0x00040000 /**< preprocessor alias for 32-bit value with bit 18 set, used to specify this single bit */ +#define BIT19 0x00080000 /**< preprocessor alias for 32-bit value with bit 19 set, used to specify this single bit */ +#define BIT20 0x00100000 /**< preprocessor alias for 32-bit value with bit 20 set, used to specify this single bit */ +#define BIT21 0x00200000 /**< preprocessor alias for 32-bit value with bit 21 set, used to specify this single bit */ +#define BIT22 0x00400000 /**< preprocessor alias for 32-bit value with bit 22 set, used to specify this single bit */ +#define BIT23 0x00800000 /**< preprocessor alias for 32-bit value with bit 23 set, used to specify this single bit */ +#define BIT24 0x01000000 /**< preprocessor alias for 32-bit value with bit 24 set, used to specify this single bit */ +#define BIT25 0x02000000 /**< preprocessor alias for 32-bit value with bit 25 set, used to specify this single bit */ +#define BIT26 0x04000000 /**< preprocessor alias for 32-bit value with bit 26 set, used to specify this single bit */ +#define BIT27 0x08000000 /**< preprocessor alias for 32-bit value with bit 27 set, used to specify this single bit */ +#define BIT28 0x10000000 /**< preprocessor alias for 32-bit value with bit 28 set, used to specify this single bit */ +#define BIT29 0x20000000 /**< preprocessor alias for 32-bit value with bit 29 set, used to specify this single bit */ +#define BIT30 0x40000000 /**< preprocessor alias for 32-bit value with bit 30 set, used to specify this single bit */ +#define BIT31 0x80000000 /**< preprocessor alias for 32-bit value with bit 31 set, used to specify this single bit */ + +#endif /* BIT0 et al */ + + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/*****************************************************************************/ +#endif /* OI_STDDEFS_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_string.h b/components/ble/ble_stack/sbc/dec/oi_string.h new file mode 100644 index 00000000..06768bd1 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_string.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 OI_STRING_H +#define OI_STRING_H +/** + * @file + * This file contains BM3 supplied portable string.h functions + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_cpu_dep.h" +#include "oi_stddefs.h" + +#if defined(USE_NATIVE_MEMCPY) || defined(USE_NATIVE_MALLOC) +#include +#endif + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * If we are using Native malloc(), we must also use + * native Ansi string.h functions for memory manipulation. + */ +#ifdef USE_NATIVE_MALLOC +#ifndef USE_NATIVE_MEMCPY +#define USE_NATIVE_MEMCPY +#endif +#endif + +#ifdef USE_NATIVE_MEMCPY + +#define OI_MemCopy(to, from, size) memcpy((to), (from), (size)) +#define OI_MemSet(block, val, size) memset((block), (val), (size)) +#define OI_MemZero(block, size) memset((block), 0, (size)) +#define OI_MemCmp(s1, s2, n) memcmp((s1), (s2), (n)) +#define OI_Strcpy(dest, src) strcpy((dest),(src)) +#define OI_Strcat(dest, src) strcat((dest),(src)) +#define OI_StrLen(str) strlen((str)) +#define OI_Strcmp(s1, s2) strcmp((s1), (s2)) +#define OI_Strncmp(s1, s2, n) strncmp((s1), (s2), (n)) + +#else + +/* + * OI_MemCopy + * + * Copy an arbitrary number of bytes from one memory address to another. + * The underlying implementation is the ANSI memmove() or equivalant, so + * overlapping memory copies will work correctly. + */ +void OI_MemCopy(void *To, void const *From, OI_UINT32 Size); + + +/* + * OI_MemSet + * + * Sets all bytes in a block of memory to the same value + */ +void OI_MemSet(void *Block, OI_UINT8 Val, OI_UINT32 Size); + + +/* + * OI_MemZero + * + * Sets all bytes in a block of memory to zero + */ +void OI_MemZero(void *Block, OI_UINT32 Size); + + +/* + * OI_MemCmp + * + * Compare two blocks of memory + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_MemCmp(void const *s1, void const *s2, OI_UINT32 n); + +/* + * OI_Strcpy + * + * Copies the Null terminated string from pStr to pDest, and + * returns pDest. + */ + +OI_CHAR *OI_Strcpy(OI_CHAR *pDest, + OI_CHAR const *pStr); + +/* + * OI_Strcat + * + * Concatonates the pStr string to the end of pDest, and + * returns pDest. + */ + +OI_CHAR *OI_Strcat(OI_CHAR *pDest, + OI_CHAR const *pStr) ; + +/* + * OI_StrLen + * + * Calculates the number of OI_CHARs in pStr (not including + * the Null terminator) and returns the value. + */ +OI_UINT OI_StrLen(OI_CHAR const *pStr) ; + +/* + * OI_Strcmp + * + * Compares two Null terminated strings + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_Strcmp(OI_CHAR const *s1, + OI_CHAR const *s2); + +/* + * OI_Strncmp + * + * Compares the first "len" OI_CHARs of strings s1 and s2. + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_Strncmp(OI_CHAR const *s1, + OI_CHAR const *s2, + OI_UINT32 len); + + +#endif /* USE_NATIVE_MEMCPY */ + +/* + * OI_StrcmpInsensitive + * + * Compares two Null terminated strings, treating + * the Upper and Lower case of 'A' through 'Z' as + * equivilent. + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_StrcmpInsensitive(OI_CHAR const *s1, + OI_CHAR const *s2); + +/* + * OI_StrncmpInsensitive + * + * Compares the first "len" OI_CHARs of strings s1 and s2, + * treating the Upper and Lower case of 'A' through 'Z' as + * equivilent. + * + * + * Returns: + * 0, if s1 == s2 + * < 0, if s1 < s2 + * > 0, if s2 > s2 + */ +OI_INT OI_StrncmpInsensitive(OI_CHAR const *s1, + OI_CHAR const *s2, + OI_UINT len); + + + +#ifdef __cplusplus +} +#endif + +/** @} */ + +/*****************************************************************************/ +#endif /* OI_STRING_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_time.h b/components/ble/ble_stack/sbc/dec/oi_time.h new file mode 100644 index 00000000..376e6842 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_time.h @@ -0,0 +1,199 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_TIME_H +#define _OI_TIME_H +/** @file + * + * This file provides time type definitions and interfaces to time-related functions. + * + * The stack maintains a 64-bit real-time millisecond clock. The choice of + * milliseconds is for convenience, not accuracy. + * + * Timeouts are specified as tenths of seconds in a 32-bit value. Timeout values + * specified by the Bluetooth specification are usually muliple seconds, so + * accuracy to a tenth of a second is more than adequate. + * + * This file also contains macros to convert between seconds and the Link + * Manager's 1.28-second units. + * + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include "oi_stddefs.h" + + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * Within the core stack timeouts are specified in intervals of tenths of seconds + */ + +typedef OI_UINT16 OI_INTERVAL; +#define OI_INTERVALS_PER_SECOND 10 +#define MSECS_PER_OI_INTERVAL (1000 / OI_INTERVALS_PER_SECOND) + +/** maximum interval (54 min 36.7 sec) */ +#define OI_MAX_INTERVAL 0x7fff + + +/** + * Macro to convert seconds to OI_INTERVAL time units + */ + +#define OI_SECONDS(n) ((OI_INTERVAL) ((n) * OI_INTERVALS_PER_SECOND)) + +/** + * Macro to convert milliseconds to OI_INTERVAL time units (Rounded Up) + */ + +#define OI_MSECONDS(n) ((OI_INTERVAL) ((n + MSECS_PER_OI_INTERVAL - 1) / MSECS_PER_OI_INTERVAL)) + +/** + * Macro to convert minutes to OI_INTERVAL time units + */ + +#define OI_MINUTES(n) ((OI_INTERVAL) ((n) * OI_SECONDS(60))) + +/** Convert an OI_INTERVAL to milliseconds. */ +#define OI_INTERVAL_TO_MILLISECONDS(i) ((i) * MSECS_PER_OI_INTERVAL) + +/** + * The stack depends on relative not absolute time. Any mapping between the + * stack's real-time clock and absolute time and date is implementation-dependent. + */ + +typedef struct { + OI_INT32 seconds; + OI_INT16 mseconds; +} OI_TIME; + +/** + * Convert an OI_TIME to milliseconds. + * + * @param t the time to convert + * + * @return the time in milliseconds + */ +OI_UINT32 OI_Time_ToMS(OI_TIME *t); + + +/** + * This function compares two time values. + * + * @param T1 first time to compare. + * + * @param T2 second time to compare. + * + * @return + @verbatim + -1 if t1 < t2 + 0 if t1 = t2 + +1 if t1 > t2 + @endverbatim + */ + +OI_INT16 OI_Time_Compare(OI_TIME *T1, + OI_TIME *T2); + + +/** + * This function returns the interval between two times to a granularity of 0.1 seconds. + * + * @param Sooner a time value more recent that Later + * + * @param Later a time value later than Sooner + * + * @note The result is an OI_INTERVAL value so this function only works for time intervals + * that are less than about 71 minutes. + * + * @return the time interval between the two times = (Later - Sooner) + */ + +OI_INTERVAL OI_Time_Interval(OI_TIME *Sooner, + OI_TIME *Later); + + + +/** + * This function returns the interval between two times to a granularity of milliseconds. + * + * @param Sooner a time value more recent that Later + * + * @param Later a time value later than Sooner + * + * @note The result is an OI_UINT32 value so this function only works for time intervals + * that are less than about 50 days. + * + * @return the time interval between the two times = (Later - Sooner) + */ + +OI_UINT32 OI_Time_IntervalMsecs(OI_TIME *Sooner, + OI_TIME *Later); + + + +/** + * This function answers the question, Have we reached or gone past the target time? + * + * @param pTargetTime target time + * + * @return TRUE means time now is at or past target time + * FALSE means target time is still some time in the future + */ + +OI_BOOL OI_Time_NowReachedTime(OI_TIME *pTargetTime); + +/** + * Convert seconds to the Link Manager 1.28-second units + * Approximate by using 1.25 conversion factor. + */ + +#define OI_SECONDS_TO_LM_TIME_UNITS(lmUnits) ((lmUnits)<4?(lmUnits):(lmUnits)-((lmUnits)>>2)) + + +/** + * Convert Link Manager 1.28-second units to seconds. + * Approximate by using 1.25 conversion factor. + */ + +#define OI_LM_TIME_UNITS_TO_SECONDS(lmUnits) ((lmUnits) + ((lmUnits)>>2)) + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +/* Include for OI_Time_Now() prototype + * Must be included at end to obtain OI_TIME typedef + */ +#include "oi_osinterface.h" + +/*****************************************************************************/ +#endif /* _OI_TIME_H */ diff --git a/components/ble/ble_stack/sbc/dec/oi_utils.h b/components/ble/ble_stack/sbc/dec/oi_utils.h new file mode 100644 index 00000000..d7e753b0 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/oi_utils.h @@ -0,0 +1,376 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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 _OI_UTILS_H +#define _OI_UTILS_H +/** + * @file + * + * This file provides the interface for utility functions. + * Among the utilities are strlen (string length), strcmp (string compare), and + * other string manipulation functions. These are provided for those plaforms + * where this functionality is not available in stdlib. + */ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +#include +#include "oi_common.h" +#include "oi_string.h" +#include "oi_bt_spec.h" + +/** \addtogroup Misc Miscellaneous APIs */ +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opaque type for a callback function handle. See OI_ScheduleCallbackFunction() + */ +typedef OI_UINT32 OI_CALLBACK_HANDLE; + + +/** + * Function prototype for a timed procedure callback. + * + * @param arg Value that was passed into the OI_ScheduleCallback() function + * + */ +typedef void (*OI_SCHEDULED_CALLBACK)(void *arg); + + +/** + * Registers a function to be called when a timeout expires. This API uses BLUEmagic's internal + * function dispatch mechanism, so applications that make extensive use of this facility may need to + * increase the value of DispatchTableSize in the configuration block for the dispatcher (see + * oi_bt_stack_config.h). + * + * @param callbackFunction The function that will be called when the timeout expires + * + * @param arg Value that will be returned as the parameter to the callback function. + * + * @param timeout A timeout expressed in OI_INTERVALs (tenths of seconds). This can be + * zero in which case the callback function will be called as soon as + * possible. + * + * @param handle NULL or a pointer receive the callback handle. + * + * @return OI_OK if the function was reqistered, or an error status. + */ +OI_STATUS OI_ScheduleCallbackFunction(OI_SCHEDULED_CALLBACK callbackFunction, + void *arg, + OI_INTERVAL timeout, + OI_CALLBACK_HANDLE *handle); + + +/** + * Cancels a function registered with OI_ScheduleCallbackFunction() before its timer expires. + * + * @param handle handle returned by OI_ScheduleCallbackFunction(). + * + * @return OI_OK if the function was cancelled, or an error status. + */ +OI_STATUS OI_CancelCallbackFunction(OI_CALLBACK_HANDLE handle); + + +/** + * Registers a function to be called when a timeout expires. This version does not return a handle + * so can only be canceled by calling OI_CancelCallback(). + * + * @param callbackFunction The function that will be called when the timeout expires + * + * @param arg Value that will be returned as the parameter to the callback function. + * + * @param timeout A timeout expressed in OI_INTERVALs (tenths of seconds). This can be + * zero in which case the callback function will be called as soon as + * possible. + * + * @return OI_OK if the function was reqistered, or an error status. + */ +#define OI_ScheduleCallback(f, a, t) OI_ScheduleCallbackFunction(f, a, t, NULL); + + +/** + * Cancels a function registered with OI_ScheduleCallback() before its timer expires. This + * function will cancel the first entry matches the indicated callback function pointer. + * + * @param callbackFunction The function that was originally registered + * + * @return OI_OK if the function was cancelled, or an error status. + */ +OI_STATUS OI_CancelCallback(OI_SCHEDULED_CALLBACK callbackFunction); + + +/** + * Parse a Bluetooth device address from the specified string. + * + * @param str the string to parse + * @param addr the parsed address, if successful + * + * @return TRUE if an address was successfully parsed, FALSE otherwise + */ + +OI_BOOL OI_ParseBdAddr(const OI_CHAR *str, + OI_BD_ADDR *addr) ; + +/** + * Printf function for platforms which have no stdio or printf available. + * OI_Printf supports the basic formatting types, with the exception of + * floating point types. Additionally, OI_Printf supports several formats + * specific to BLUEmagic 3.0 software: + * + * \%! prints the string for an #OI_STATUS value. + * @code OI_Printf("There was an error %!", status); @endcode + * + * \%@ prints a hex dump of a buffer. + * Requires a pointer to the buffer and a signed integer length + * (0 for default length). If the buffer is large, only an excerpt will + * be printed. + * @code OI_Printf("Contents of buffer %@", buffer, sizeof(buffer)); @endcode + * + * \%: prints a Bluetooth address in the form "HH:HH:HH:HH:HH:HH". + * Requires a pointer to an #OI_BD_ADDR. + * @code OI_Printf("Bluetooth address %:", &bdaddr); @endcode + * + * \%^ decodes and prints a data element as formatted XML. + * Requires a pointer to an #OI_DATAELEM. + * @code OI_Printf("Service attribute list is:\n%^", &attributes); @endcode + * + * \%/ prints the base file name of a path, that is, the final substring + * following a '/' or '\\' character. Requires a pointer to a null + * terminated string. + * @code OI_Printf("File %/", "c:\\dir1\\dir2\\file.txt"); @endcode + * + * \%~ prints a string, escaping characters as needed to display it in + * ASCII. Requires a pointer to an #OI_PSTR and an #OI_UNICODE_ENCODING + * parameter. + * @code OI_Printf("Identifier %~", &id, OI_UNICODE_UTF16_BE); @endcode + * + * \%[ inserts an ANSI color escape sequence. Requires a single character + * identifying the color to select. Colors are red (r/R), green (g/G), + * blue (b/B), yellow (y/Y), cyan (c/C), magenta (m/M), white (W), + * light-gray (l/L), dark-gray (d/D), and black (0). The lower case is + * dim, the upper case is bright (except in the case of light-gray and + * dark-gray, where bright and dim are identical). Any other value will + * select the default color. + * @code OI_Printf("%[red text %[black %[normal\n", 'r', '0', 0); @endcode + * + * \%a same as \%s, except '\\r' and '\\n' are output as "" and "". + * \%?a is valid, but \%la is not. + * + * \%b prints an integer in base 2. + * @code OI_Printf("Bits are %b", I); @endcode + * + * \%lb prints a long integer in base 2. + * + * \%?b prints the least significant N bits of an integer (or long integer) + * in base 2. Requires the integer and a length N. + * @code OI_Printf("Bottom 4 bits are: %?b", I, 4); @endcode + * + * \%B prints an integer as boolean text, "TRUE" or "FALSE". + * @code OI_Printf("The value 0 is %B, the value 1 is %B", 0, 1); @endcode + * + * \%?s prints a substring up to a specified maximum length. + * Requires a pointer to a string and a length parameter. + * @code OI_Printf("String prefix is %?s", str, 3); @endcode + * + * \%ls same as \%S. + * + * \%S prints a UTF16 string as UTF8 (plain ASCII, plus 8-bit char sequences + * where needed). Requires a pointer to #OI_CHAR16. \%?S is valid. The + * length parameter is in OI_CHAR16 characters. + * + * \%T prints time, formatted as "secs.msecs". + * Requires pointer to #OI_TIME struct, NULL pointer prints current time. + * @code OI_Printf("The time now is %T", NULL); @endcode + * + * @param format The format string + * + */ +void OI_Printf(const OI_CHAR *format, ...); + + +/** + * Var-args version OI_Printf + * + * @param format Same as for OI_Printf. + * + * @param argp Var-args list. + */ +void OI_VPrintf(const OI_CHAR *format, va_list argp); + + +/** + * Writes a formatted string to a buffer. This function supports the same format specifiers as + * OI_Printf(). + * + * @param buffer Destination buffer for the formatted string. + * + * @param bufLen The length of the destination buffer. + * + * @param format The format string + * + * @return Number of characters written or -1 in the case of an error. + */ +OI_INT32 OI_SNPrintf(OI_CHAR *buffer, + OI_UINT16 bufLen, + const OI_CHAR *format, ...); + + +/** + * Var-args version OI_SNPrintf + * + * @param buffer Destination buffer for the formatted string. + * + * @param bufLen The length of the destination buffer. + * + * @param format The format string + * + * @param argp Var-args list. + * + * @return Number of characters written or -1 in the case of an error. + */ +OI_INT32 OI_VSNPrintf(OI_CHAR *buffer, + OI_UINT16 bufLen, + const OI_CHAR *format, va_list argp); + + +/** + * Convert a string to an integer. + * + * @param str the string to parse + * + * @return the integer value of the string or 0 if the string could not be parsed + */ +OI_INT OI_atoi(const OI_CHAR *str); + + +/** + * Parse a signed integer in a string. + * + * Skips leading whitespace (space and tabs only) and parses a decimal or hex string. Hex string + * must be prefixed by "0x". Returns pointer to first character following the integer. Returns the + * pointer passed in if the string does not describe an integer. + * + * @param str String to parse. + * + * @param val Pointer to receive the parsed integer value. + * + * @return A pointer to the first character following the integer or the pointer passed in. + */ +const OI_CHAR *OI_ScanInt(const OI_CHAR *str, + OI_INT32 *val); + + +/** + * Parse an unsigned integer in a string. + * + * Skips leading whitespace (space and tabs only) and parses a decimal or hex string. Hex string + * must be prefixed by "0x". Returns pointer to first character following the integer. Returns the + * pointer passed in if the string does not describe an integer. + * + * @param str String to parse. + * + * @param val Pointer to receive the parsed unsigned integer value. + * + * @return A pointer to the first character following the unsigned integer or the pointer passed in. + */ +const OI_CHAR *OI_ScanUInt(const OI_CHAR *str, + OI_UINT32 *val); + +/** + * Parse a whitespace delimited substring out of a string. + * + * @param str Input string to parse. + * @param outStr Buffer to return the substring + * @param len Length of outStr + * + * + * @return A pointer to the first character following the substring or the pointer passed in. + */ +const OI_CHAR *OI_ScanStr(const OI_CHAR *str, + OI_CHAR *outStr, + OI_UINT16 len); + + +/** + * Parse a string for one of a set of alternative value. Skips leading whitespace (space and tabs + * only) and parses text matching one of the alternative strings. Returns pointer to first character + * following the matched text. + * + * @param str String to parse. + * + * @param alts Alternative matching strings separated by '|' + * + * @param index Pointer to receive the index of the matching alternative, return value is -1 if + * there is no match. + * + * @return A pointer to the first character following the matched value or the pointer passed in + * if there was no matching text. + */ +const OI_CHAR *OI_ScanAlt(const OI_CHAR *str, + const OI_CHAR *alts, + OI_INT *index); + +/** + * Parse a string for a BD Addr. Skips leading whitespace (space and tabs only) and parses a + * Bluetooth device address with nibbles optionally separated by colons. Return pointet to first + * character following the BD Addr. + * + * @param str String to parse. + * + * @param addr Pointer to receive the Bluetooth device address + * + * @return A pointer to the first character following the BD Addr or the pointer passed in. + */ +const OI_CHAR *OI_ScanBdAddr(const OI_CHAR *str, + OI_BD_ADDR *addr); + + +/** Get a character from a digit integer value (0 - 9). */ +#define OI_DigitToChar(d) ((d) + '0') + +/** + * Determine Maximum and Minimum between two arguments. + * + * @param a 1st value + * @param b 2nd value + * + * @return the max or min value between a & b + */ +#define OI_MAX(a, b) (((a) < (b)) ? (b) : (a) ) +#define OI_MIN(a, b) (((a) > (b)) ? (b) : (a) ) + +/** + * Compare two BD_ADDRs + * SAME_BD_ADDR - Boolean: TRUE if they are the same address + */ + +#define SAME_BD_ADDR(x, y) (0 == OI_MemCmp((x),(y),OI_BD_ADDR_BYTE_SIZE) ) + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif /* _OI_UTILS_H */ diff --git a/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc b/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc new file mode 100644 index 00000000..875a3949 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc @@ -0,0 +1,111 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/******************************************************************************* + * @file readsamplesjoint.inc + * + * This is the body of the generic version of OI_SBC_ReadSamplesJoint(). + * It is designed to be \#included into a function as follows: + \code + void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + #define NROF_SUBBANDS 4 + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + + void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + #define NROF_SUBBANDS 8 + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + \endcode + * Or to make a generic version: + \code + void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs) + { + OI_UINT nrof_subbands = common->frameInfo.nrof_subbands; + + #define NROF_SUBBANDS nrof_subbands + #include "readsamplesjoint.inc" + #undef NROF_SUBBANDS + } + \endcode + * @ingroup codec_internal + *******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +{ + OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common; + OI_UINT bl = common->frameInfo.nrof_blocks; + OI_INT32 * RESTRICT s = common->subdata; + OI_UINT8 *ptr = global_bs->ptr.w; + OI_UINT32 value = global_bs->value; + OI_UINT bitPtr = global_bs->bitPtr; + OI_UINT8 jmask = common->frameInfo.join << (8 - NROF_SUBBANDS); + + do { + OI_INT8 *sf_array = &common->scale_factor[0]; + OI_UINT8 *bits_array = &common->bits.uint8[0]; + OI_UINT8 joint = jmask; + OI_UINT sb; + /* + * Left channel + */ + sb = NROF_SUBBANDS; + do { + OI_UINT32 raw; + OI_INT32 dequant; + OI_UINT8 bits = *bits_array++; + OI_INT sf = *sf_array++; + + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + *s++ = dequant; + } while (--sb); + /* + * Right channel + */ + sb = NROF_SUBBANDS; + do { + OI_UINT32 raw; + OI_INT32 dequant; + OI_UINT8 bits = *bits_array++; + OI_INT sf = *sf_array++; + + OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr); + dequant = OI_SBC_Dequant(raw, sf, bits); + /* + * Check if we need to do mid/side + */ + if (joint & 0x80) { + OI_INT32 mid = *(s - NROF_SUBBANDS); + OI_INT32 side = dequant; + *(s - NROF_SUBBANDS) = mid + side; + dequant = mid - side; + } + joint <<= 1; + *s++ = dequant; + } while (--sb); + } while (--bl); +} diff --git a/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c b/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c new file mode 100644 index 00000000..95a595d8 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c @@ -0,0 +1,138 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/** + @file + + DO NOT EDIT THIS FILE DIRECTLY + + This file is automatically generated by the "synthesis-gen.pl" script. + Any changes to this generated file will be lost when the script is re-run. + + These functions are called by functions in synthesis.c to perform the synthesis + filterbank computations for the SBC decoder. + + + */ +#include + +#if defined(SBC_DEC_INCLUDED) + +#ifndef CLIP_INT16 +#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0) +#endif + +#define MUL_16S_16S(_x, _y) ((_x) * (_y)) + +PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift) +{ + OI_INT32 pcm_a, pcm_b; + /* 1 - stage 0 */ pcm_b = 0; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(8235, buffer[ 12])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(-23167, buffer[ 20])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(26479, buffer[ 28])) >> 2; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(-17397, buffer[ 36])) << 1; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(9399, buffer[ 44])) << 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(17397, buffer[ 52])) << 1; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(26479, buffer[ 60])) >> 2; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(23167, buffer[ 68])) >> 3; + /* 1 - stage 0 */ pcm_b += (MUL_16S_16S(8235, buffer[ 76])) >> 3; + /* 1 - stage 0 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[0 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 1 */ pcm_a = 0; + /* 1 - stage 1 */ pcm_b = 0; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-3263, buffer[ 5])) >> 5; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(9293, buffer[ 5])) >> 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(29293, buffer[ 11])) >> 5; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(-6087, buffer[ 11])) >> 2; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-5229, buffer[ 21])); + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(1247, buffer[ 21])) << 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(30835, buffer[ 27])) >> 3; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(-2893, buffer[ 27])) << 3; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(-27021, buffer[ 37])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(23671, buffer[ 37])) << 2; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(31633, buffer[ 43])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(18055, buffer[ 43])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(17319, buffer[ 53])) << 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(11537, buffer[ 53])) >> 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(26663, buffer[ 59])) >> 2; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(1747, buffer[ 59])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(4555, buffer[ 69])) >> 1; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(685, buffer[ 69])) << 1; + /* 1 - stage 1 */ pcm_a += (MUL_16S_16S(12419, buffer[ 75])) >> 4; + /* 1 - stage 1 */ pcm_b += (MUL_16S_16S(8721, buffer[ 75])) >> 7; + /* 1 - stage 1 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[1 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 1 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[7 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 2 */ pcm_a = 0; + /* 1 - stage 2 */ pcm_b = 0; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-10385, buffer[ 6])) >> 6; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(11167, buffer[ 6])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(24995, buffer[ 10])) >> 5; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(-10337, buffer[ 10])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-309, buffer[ 22])) << 4; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(1917, buffer[ 22])) << 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(9161, buffer[ 26])) >> 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(-30605, buffer[ 26])) >> 1; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(-23063, buffer[ 38])) << 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(8317, buffer[ 38])) << 3; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(27561, buffer[ 42])) << 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(9553, buffer[ 42])) << 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(2309, buffer[ 54])) << 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(22117, buffer[ 54])) >> 4; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(12705, buffer[ 58])) >> 1; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(16383, buffer[ 58])) >> 2; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(6239, buffer[ 70])) >> 3; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(7543, buffer[ 70])) >> 3; + /* 1 - stage 2 */ pcm_a += (MUL_16S_16S(9251, buffer[ 74])) >> 4; + /* 1 - stage 2 */ pcm_b += (MUL_16S_16S(8603, buffer[ 74])) >> 6; + /* 1 - stage 2 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[2 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 2 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[6 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 3 */ pcm_a = 0; + /* 1 - stage 3 */ pcm_b = 0; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-16457, buffer[ 7])) >> 6; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(16913, buffer[ 7])) >> 5; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(19083, buffer[ 9])) >> 5; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-8443, buffer[ 9])) >> 7; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-23641, buffer[ 23])) >> 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(3687, buffer[ 23])) << 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-29015, buffer[ 25])) >> 4; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-301, buffer[ 25])) << 5; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(-12889, buffer[ 39])) << 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(15447, buffer[ 39])) << 2; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(6145, buffer[ 41])) << 3; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(10255, buffer[ 41])) << 2; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(24211, buffer[ 55])) >> 1; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(-18233, buffer[ 55])) >> 3; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(23469, buffer[ 57])) >> 2; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(9405, buffer[ 57])) >> 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(21223, buffer[ 71])) >> 8; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(1499, buffer[ 71])) >> 1; + /* 1 - stage 3 */ pcm_a += (MUL_16S_16S(26913, buffer[ 73])) >> 6; + /* 1 - stage 3 */ pcm_b += (MUL_16S_16S(26189, buffer[ 73])) >> 7; + /* 1 - stage 3 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[3 << strideShift] = (OI_INT16)pcm_a; + /* 1 - stage 3 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[5 << strideShift] = (OI_INT16)pcm_b; + /* 1 - stage 4 */ pcm_a = 0; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(10445, buffer[ 8])) >> 4; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(-5297, buffer[ 24])) << 1; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(22299, buffer[ 40])) << 2; + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(10603, buffer[ 56])); + /* 1 - stage 4 */ pcm_a += (MUL_16S_16S(9539, buffer[ 72])) >> 4; + /* 1 - stage 4 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[4 << strideShift] = (OI_INT16)pcm_a; +} + +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/dec/synthesis-dct8.c b/components/ble/ble_stack/sbc/dec/synthesis-dct8.c new file mode 100644 index 00000000..7ae9c068 --- /dev/null +++ b/components/ble/ble_stack/sbc/dec/synthesis-dct8.c @@ -0,0 +1,307 @@ +/****************************************************************************** + * + * Copyright (C) 2014 The Android Open Source Project + * Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved. + * + * 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. + * + ******************************************************************************/ + +/********************************************************************************** + $Revision: #1 $ +***********************************************************************************/ + +/** @file +@ingroup codec_internal +*/ + +/**@addgroup codec_internal*/ +/**@{*/ + +/* + * Performs an 8-point Type-II scaled DCT using the Arai-Agui-Nakajima + * factorization. The scaling factors are folded into the windowing + * constants. 29 adds and 5 16x32 multiplies per 8 samples. + */ +#include "oi_codec_sbc_private.h" + +#if defined(SBC_DEC_INCLUDED) + +#define AAN_C4_FIX (759250125)/* S1.30 759250125 0.707107*/ + +#define AAN_C6_FIX (410903207)/* S1.30 410903207 0.382683*/ + +#define AAN_Q0_FIX (581104888)/* S1.30 581104888 0.541196*/ + +#define AAN_Q1_FIX (1402911301)/* S1.30 1402911301 1.306563*/ + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +/** + * Default C language implementation of a 32x32->32 multiply. This function may + * be replaced by a platform-specific version for speed. + * + * @param u A signed 32-bit multiplicand + * @param v A signed 32-bit multiplier + + * @return A signed 32-bit value corresponding to the 32 most significant bits + * of the 64-bit product of u and v. + */ +static INLINE OI_INT32 default_mul_32s_32s_hi(OI_INT32 u, OI_INT32 v) +{ + OI_UINT32 u0, v0; + OI_INT32 u1, v1, w1, w2, t; + + u0 = u & 0xFFFF; u1 = u >> 16; + v0 = v & 0xFFFF; v1 = v >> 16; + t = u0 * v0; + t = u1 * v0 + ((OI_UINT32)t >> 16); + w1 = t & 0xFFFF; + w2 = t >> 16; + w1 = u0 * v1 + w1; + return u1 * v1 + w2 + (w1 >> 16); +} + +#define MUL_32S_32S_HI(_x, _y) default_mul_32s_32s_hi(_x, _y) + + +#ifdef DEBUG_DCT +PRIVATE void float_dct2_8(float *RESTRICT out, OI_INT32 const *RESTRICT in) +{ +#define FIX(x,bits) (((int)floor(0.5f+((x)*((float)(1<= 8 ? 16 : 0) + and VSIGN(i) maps i%16 into {1, 1, 1, 1, 0, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1 } + These values correspond to the + signs of the redundant values as + shown in the explanation three + paragraphs above. +@endcode + +We saw above how V[4..8,13..15] (and by extension +V[(4..8,13..15)+16*n]) can be defined in terms of other elements +within the subblock of V. V[0..3,9..12] correspond to DCT elements. + +@code + for i=0 to 79 do + W[i] = D[i]*DSIGN(i)*DCT[remap_DCT(i)] +@endcode + +The DCT is calculated using the Arai-Agui-Nakajima factorization, +which saves some computation by producing output that needs to be +multiplied by scaling factors before being used. + +@code + for i=0 to 79 do + W[i] = D[i]*SCALE[i%8]*AAN_DCT[remap_DCT(i)] +@endcode + +D can be premultiplied with the DCT scaling factors to yield + +@code + for i=0 to 79 do + W[i] = DSCALED[i]*AAN_DCT[remap_DCT(i)] where DSCALED[i] = D[i]*SCALE[i%8] +@endcode + +The output samples X[0..7] are defined as sums of W: + +@code + X[j] = sum{i=0..9}(W[j+8*i]) +@endcode + +@ingroup codec_internal +*/ + +/** +@addtogroup codec_internal +@{ +*/ +#include "oi_codec_sbc_private.h" + +#if defined(SBC_DEC_INCLUDED) + +const OI_INT32 dec_window_4[21] = { + 0, /* +0.00000000E+00 */ + 97, /* +5.36548976E-04 */ + 270, /* +1.49188357E-03 */ + 495, /* +2.73370904E-03 */ + 694, /* +3.83720193E-03 */ + 704, /* +3.89205149E-03 */ + 338, /* +1.86581691E-03 */ + -554, /* -3.06012286E-03 */ + 1974, /* +1.09137620E-02 */ + 3697, /* +2.04385087E-02 */ + 5224, /* +2.88757392E-02 */ + 5824, /* +3.21939290E-02 */ + 4681, /* +2.58767811E-02 */ + 1109, /* +6.13245186E-03 */ + -5214, /* -2.88217274E-02 */ + -14047, /* -7.76463494E-02 */ + 24529, /* +1.35593274E-01 */ + 35274, /* +1.94987841E-01 */ + 44618, /* +2.46636662E-01 */ + 50984, /* +2.81828203E-01 */ + 53243, /* +2.94315332E-01 */ +}; + +#define DCTII_4_K06_FIX ( 11585)/* S1.14 11585 0.707107*/ + +#define DCTII_4_K08_FIX ( 21407)/* S1.14 21407 1.306563*/ + +#define DCTII_4_K09_FIX (-15137)/* S1.14 -15137 -0.923880*/ + +#define DCTII_4_K10_FIX ( -8867)/* S1.14 -8867 -0.541196*/ + +/** Scales x by y bits to the right, adding a rounding factor. + */ +#ifndef SCALE +#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y)) +#endif + +#ifndef CLIP_INT16 +#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0) +#endif + +/** + * Default C language implementation of a 16x32->32 multiply. This function may + * be replaced by a platform-specific version for speed. + * + * @param u A signed 16-bit multiplicand + * @param v A signed 32-bit multiplier + + * @return A signed 32-bit value corresponding to the 32 most significant bits + * of the 48-bit product of u and v. + */ +static INLINE OI_INT32 default_mul_16s_32s_hi(OI_INT16 u, OI_INT32 v) +{ + OI_UINT16 v0; + OI_INT16 v1; + + OI_INT32 w, x; + + v0 = (OI_UINT16)(v & 0xffff); + v1 = (OI_INT16) (v >> 16); + + w = v1 * u; + x = u * v0; + + return w + (x >> 16); +} + +#define MUL_16S_32S_HI(_x, _y) default_mul_16s_32s_hi(_x, _y) + +#define LONG_MULT_DCT(K, sample) (MUL_16S_32S_HI(K, sample)<<2) + +PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift); +PRIVATE void SynthWindow112_generated(OI_INT16 *pcm, SBC_BUFFER_T const *RESTRICT buffer, OI_UINT strideShift); +PRIVATE void dct2_8(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT x); + +typedef void (*SYNTH_FRAME)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount); + +#ifndef COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS +#define COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(dest, src) do { shift_buffer(dest, src, 72); } while (0) +#endif + +#ifndef DCT2_8 +#define DCT2_8(dst, src) dct2_8(dst, src) +#endif + +#ifndef SYNTH80 +#define SYNTH80 SynthWindow80_generated +#endif + +#ifndef SYNTH112 +#define SYNTH112 SynthWindow112_generated +#endif + +PRIVATE void OI_SBC_SynthFrame_80(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 80; + } else { + offset -= 1 * 8; + } + + for (ch = 0; ch < nrof_channels; ch++) { + DCT2_8(context->common.filterBuffer[ch] + offset, s); + SYNTH80(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift); + s += 8; + } + pcm += (8 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +PRIVATE void OI_SBC_SynthFrame_4SB(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 80; + } else { + offset -= 8; + } + for (ch = 0; ch < nrof_channels; ch++) { + cosineModulateSynth4(context->common.filterBuffer[ch] + offset, s); + SynthWindow40_int32_int32_symmetry_with_sum(pcm + ch, + context->common.filterBuffer[ch] + offset, + pcmStrideShift); + s += 4; + } + pcm += (4 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +#ifdef SBC_ENHANCED + +PRIVATE void OI_SBC_SynthFrame_Enhanced(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount) +{ + OI_UINT blk; + OI_UINT ch; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1; + OI_UINT offset = context->common.filterBufferOffset; + OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart; + OI_UINT blkstop = blkstart + blkcount; + + for (blk = blkstart; blk < blkstop; blk++) { + if (offset == 0) { + COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 104, context->common.filterBuffer[0]); + if (nrof_channels == 2) { + COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 104, context->common.filterBuffer[1]); + } + offset = context->common.filterBufferLen - 112; + } else { + offset -= 8; + } + for (ch = 0; ch < nrof_channels; ++ch) { + DCT2_8(context->common.filterBuffer[ch] + offset, s); + SYNTH112(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift); + s += 8; + } + pcm += (8 << pcmStrideShift); + } + context->common.filterBufferOffset = offset; +} + +static const SYNTH_FRAME SynthFrameEnhanced[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_Enhanced, /* mono */ + OI_SBC_SynthFrame_Enhanced /* stereo */ +}; + +#endif + +static const SYNTH_FRAME SynthFrame8SB[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_80, /* mono */ + OI_SBC_SynthFrame_80 /* stereo */ +}; + + +static const SYNTH_FRAME SynthFrame4SB[] = { + NULL, /* invalid */ + OI_SBC_SynthFrame_4SB, /* mono */ + OI_SBC_SynthFrame_4SB /* stereo */ +}; + +PRIVATE void OI_SBC_SynthFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT start_block, OI_UINT nrof_blocks) +{ + OI_UINT nrof_subbands = context->common.frameInfo.nrof_subbands; + OI_UINT nrof_channels = context->common.frameInfo.nrof_channels; + + OI_ASSERT(nrof_subbands == 4 || nrof_subbands == 8); + if (nrof_subbands == 4) { + SynthFrame4SB[nrof_channels](context, pcm, start_block, nrof_blocks); +#ifdef SBC_ENHANCED + } else if (context->common.frameInfo.enhanced) { + SynthFrameEnhanced[nrof_channels](context, pcm, start_block, nrof_blocks); +#endif /* SBC_ENHANCED */ + } else { + SynthFrame8SB[nrof_channels](context, pcm, start_block, nrof_blocks); + } +} + + +void SynthWindow40_int32_int32_symmetry_with_sum(OI_INT16 *pcm, SBC_BUFFER_T buffer[80], OI_UINT strideShift) +{ + OI_INT32 pa; + OI_INT32 pb; + + /* These values should be zero, since out[2] of the 4-band cosine modulation + * is always zero. */ + OI_ASSERT(buffer[ 2] == 0); + OI_ASSERT(buffer[10] == 0); + OI_ASSERT(buffer[18] == 0); + OI_ASSERT(buffer[26] == 0); + OI_ASSERT(buffer[34] == 0); + OI_ASSERT(buffer[42] == 0); + OI_ASSERT(buffer[50] == 0); + OI_ASSERT(buffer[58] == 0); + OI_ASSERT(buffer[66] == 0); + OI_ASSERT(buffer[74] == 0); + + + pa = dec_window_4[ 4] * (buffer[12] + buffer[76]); + pa += dec_window_4[ 8] * (buffer[16] - buffer[64]); + pa += dec_window_4[12] * (buffer[28] + buffer[60]); + pa += dec_window_4[16] * (buffer[32] - buffer[48]); + pa += dec_window_4[20] * buffer[44]; + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[0 << strideShift] = (OI_INT16)pa; + + + pa = dec_window_4[ 1] * buffer[ 1]; pb = dec_window_4[ 1] * buffer[79]; + pb += dec_window_4[ 3] * buffer[ 3]; pa += dec_window_4[ 3] * buffer[77]; + pa += dec_window_4[ 5] * buffer[13]; pb += dec_window_4[ 5] * buffer[67]; + pb += dec_window_4[ 7] * buffer[15]; pa += dec_window_4[ 7] * buffer[65]; + pa += dec_window_4[ 9] * buffer[17]; pb += dec_window_4[ 9] * buffer[63]; + pb += dec_window_4[11] * buffer[19]; pa += dec_window_4[11] * buffer[61]; + pa += dec_window_4[13] * buffer[29]; pb += dec_window_4[13] * buffer[51]; + pb += dec_window_4[15] * buffer[31]; pa += dec_window_4[15] * buffer[49]; + pa += dec_window_4[17] * buffer[33]; pb += dec_window_4[17] * buffer[47]; + pb += dec_window_4[19] * buffer[35]; pa += dec_window_4[19] * buffer[45]; + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[1 << strideShift] = (OI_INT16)(pa); + pb = SCALE(-pb, 15); + CLIP_INT16(pb); + pcm[3 << strideShift] = (OI_INT16)(pb); + + + pa = dec_window_4[2] * (/*buffer[ 2] + */ buffer[78]); /* buffer[ 2] is always zero */ + pa += dec_window_4[6] * (buffer[14] /* + buffer[66]*/); /* buffer[66] is always zero */ + pa += dec_window_4[10] * (/*buffer[18] + */ buffer[62]); /* buffer[18] is always zero */ + pa += dec_window_4[14] * (buffer[30] /* + buffer[50]*/); /* buffer[50] is always zero */ + pa += dec_window_4[18] * (/*buffer[34] + */ buffer[46]); /* buffer[34] is always zero */ + pa = SCALE(-pa, 15); + CLIP_INT16(pa); + pcm[2 << strideShift] = (OI_INT16)(pa); +} + + +/** + This routine implements the cosine modulation matrix for 4-subband + synthesis. This is called "matrixing" in the SBC specification. This + matrix, M4, can be factored into an 8-point Type II Discrete Cosine + Transform, DCTII_4 and a matrix S4, given here: + + @code + __ __ + | 0 0 1 0 | + | 0 0 0 1 | + | 0 0 0 0 | + | 0 0 0 -1 | + S4 = | 0 0 -1 0 | + | 0 -1 0 0 | + | -1 0 0 0 | + |__ 0 -1 0 0 __| + + M4 * in = S4 * (DCTII_4 * in) + @endcode + + (DCTII_4 * in) is computed using a Fast Cosine Transform. The algorithm + here is based on an implementation computed by the SPIRAL computer + algebra system, manually converted to fixed-point arithmetic. S4 can be + implemented using only assignment and negation. + */ +PRIVATE void cosineModulateSynth4(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT in) +{ + OI_INT32 f0, f1, f2, f3, f4, f7, f8, f9, f10; + OI_INT32 y0, y1, y2, y3; + + f0 = (in[0] - in[3]); + f1 = (in[0] + in[3]); + f2 = (in[1] - in[2]); + f3 = (in[1] + in[2]); + + f4 = f1 - f3; + + y0 = -SCALE(f1 + f3, DCT_SHIFT); + y2 = -SCALE(LONG_MULT_DCT(DCTII_4_K06_FIX, f4), DCT_SHIFT); + f7 = f0 + f2; + f8 = LONG_MULT_DCT(DCTII_4_K08_FIX, f0); + f9 = LONG_MULT_DCT(DCTII_4_K09_FIX, f7); + f10 = LONG_MULT_DCT(DCTII_4_K10_FIX, f2); + y3 = -SCALE(f8 + f9, DCT_SHIFT); + y1 = -SCALE(f10 - f9, DCT_SHIFT); + + out[0] = (OI_INT16) - y2; + out[1] = (OI_INT16) - y3; + out[2] = (OI_INT16)0; + out[3] = (OI_INT16)y3; + out[4] = (OI_INT16)y2; + out[5] = (OI_INT16)y1; + out[6] = (OI_INT16)y0; + out[7] = (OI_INT16)y1; +} + +/** +@} +*/ +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_analysis.c b/components/ble/ble_stack/sbc/enc/sbc_analysis.c new file mode 100644 index 00000000..daace0ae --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_analysis.c @@ -0,0 +1,1104 @@ +/****************************************************************************** + * + * 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 code that performs Analysis of the input audio + * stream. + * + ******************************************************************************/ +#include +#include +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" +//#include "osi/allocator.h" +/*#include */ +#if defined(SBC_ENC_INCLUDED) + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WIND_4_SUBBANDS_0_1 (SINT32)0x01659F45 /* gas32CoeffFor4SBs[8] = -gas32CoeffFor4SBs[32] = 0x01659F45 */ +#define WIND_4_SUBBANDS_0_2 (SINT32)0x115B1ED2 /* gas32CoeffFor4SBs[16] = -gas32CoeffFor4SBs[24] = 0x115B1ED2 */ +#define WIND_4_SUBBANDS_1_0 (SINT32)0x001194E6 /* gas32CoeffFor4SBs[1 et 39] = 0x001194E6 */ +#define WIND_4_SUBBANDS_1_1 (SINT32)0x029DBAA3 /* gas32CoeffFor4SBs[9 et 31] = 0x029DBAA3 */ +#define WIND_4_SUBBANDS_1_2 (SINT32)0x18F55C90 /* gas32CoeffFor4SBs[17 et 23] = 0x18F55C90 */ +#define WIND_4_SUBBANDS_1_3 (SINT32)0xF60FAF37 /* gas32CoeffFor4SBs[15 et 25] = 0xF60FAF37 */ +#define WIND_4_SUBBANDS_1_4 (SINT32)0xFF9BB9D5 /* gas32CoeffFor4SBs[7 et 33] = 0xFF9BB9D5 */ +#define WIND_4_SUBBANDS_2_0 (SINT32)0x0030E2D3 /* gas32CoeffFor4SBs[2 et 38] = 0x0030E2D3 */ +#define WIND_4_SUBBANDS_2_1 (SINT32)0x03B23341 /* gas32CoeffFor4SBs[10 et 30] = 0x03B23341 */ +#define WIND_4_SUBBANDS_2_2 (SINT32)0x1F91CA46 /* gas32CoeffFor4SBs[18 et 22] = 0x1F91CA46 */ +#define WIND_4_SUBBANDS_2_3 (SINT32)0xFC4F91D4 /* gas32CoeffFor4SBs[14 et 26] = 0xFC4F91D4 */ +#define WIND_4_SUBBANDS_2_4 (SINT32)0x003D239B /* gas32CoeffFor4SBs[6 et 34] = 0x003D239B */ +#define WIND_4_SUBBANDS_3_0 (SINT32)0x00599403 /* gas32CoeffFor4SBs[3 et 37] = 0x00599403 */ +#define WIND_4_SUBBANDS_3_1 (SINT32)0x041EEE40 /* gas32CoeffFor4SBs[11 et 29] = 0x041EEE40 */ +#define WIND_4_SUBBANDS_3_2 (SINT32)0x2412F251 /* gas32CoeffFor4SBs[19 et 21] = 0x2412F251 */ +#define WIND_4_SUBBANDS_3_3 (SINT32)0x00C8F2BC /* gas32CoeffFor4SBs[13 et 27] = 0x00C8F2BC */ +#define WIND_4_SUBBANDS_3_4 (SINT32)0x007F88E4 /* gas32CoeffFor4SBs[5 et 35] = 0x007F88E4 */ +#define WIND_4_SUBBANDS_4_0 (SINT32)0x007DBCC8 /* gas32CoeffFor4SBs[4 et 36] = 0x007DBCC8 */ +#define WIND_4_SUBBANDS_4_1 (SINT32)0x034FEE2C /* gas32CoeffFor4SBs[12 et 28] = 0x034FEE2C */ +#define WIND_4_SUBBANDS_4_2 (SINT32)0x25AC1FF2 /* gas32CoeffFor4SBs[20] = 0x25AC1FF2 */ + +#define WIND_8_SUBBANDS_0_1 (SINT32)0x00B97348 /* 16 0x00B97348 */ +#define WIND_8_SUBBANDS_0_2 (SINT32)0x08B4307A /* 32 0x08B4307A */ +#define WIND_8_SUBBANDS_1_0 (SINT32)0x00052173 /* 1 et 79 = 0x00052173 */ +#define WIND_8_SUBBANDS_1_1 (SINT32)0x01071B96 /* 17 et 63 = 0x01071B96 */ +#define WIND_8_SUBBANDS_1_2 (SINT32)0x0A9F3E9A /* 33 et 47 = 0x0A9F3E9A*/ +#define WIND_8_SUBBANDS_1_3 (SINT32)0xF9312891 /* 31 et 49 = 0xF9312891 */ +#define WIND_8_SUBBANDS_1_4 (SINT32)0xFF8D6793 /* 15 et 65 = 0xFF8D6793 */ +#define WIND_8_SUBBANDS_2_0 (SINT32)0x000B3F71 /* 2 et 78 = 0x000B3F71 */ +#define WIND_8_SUBBANDS_2_1 (SINT32)0x0156B3CA /* 18 et 62 = 0x0156B3CA */ +#define WIND_8_SUBBANDS_2_2 (SINT32)0x0C7D59B6 /* 34 et 46 = 0x0C7D59B6 */ +#define WIND_8_SUBBANDS_2_3 (SINT32)0xFAFF95FC /* 30 et 50 = 0xFAFF95FC */ +#define WIND_8_SUBBANDS_2_4 (SINT32)0xFFC9F10E /* 14 et 66 = 0xFFC9F10E */ +#define WIND_8_SUBBANDS_3_0 (SINT32)0x00122C7D /* 3 et 77 = 0x00122C7D*/ +#define WIND_8_SUBBANDS_3_1 (SINT32)0x01A1B38B /* 19 et 61 = 0x01A1B38B */ +#define WIND_8_SUBBANDS_3_2 (SINT32)0x0E3BB16F /* 35 et 45 = 0x0E3BB16F */ +#define WIND_8_SUBBANDS_3_3 (SINT32)0xFCA86E7E /* 29 et 51 = 0xFCA86E7E */ +#define WIND_8_SUBBANDS_3_4 (SINT32)0xFFFA2413 /* 13 et 67 = 0xFFFA2413 */ +#define WIND_8_SUBBANDS_4_0 (SINT32)0x001AFF89 /* 4 et 66 = 0x001AFF89 */ +#define WIND_8_SUBBANDS_4_1 (SINT32)0x01E0224C /* 20 et 60 = 0x01E0224C */ +#define WIND_8_SUBBANDS_4_2 (SINT32)0x0FC721F9 /* 36 et 44 = 0x0FC721F9 */ +#define WIND_8_SUBBANDS_4_3 (SINT32)0xFE20435D /* 28 et 52 = 0xFE20435D */ +#define WIND_8_SUBBANDS_4_4 (SINT32)0x001D8FD2 /* 12 et 68 = 0x001D8FD2 */ +#define WIND_8_SUBBANDS_5_0 (SINT32)0x00255A62 /* 5 et 75 = 0x00255A62 */ +#define WIND_8_SUBBANDS_5_1 (SINT32)0x0209291F /* 21 et 59 = 0x0209291F */ +#define WIND_8_SUBBANDS_5_2 (SINT32)0x110ECEF0 /* 37 et 43 = 0x110ECEF0 */ +#define WIND_8_SUBBANDS_5_3 (SINT32)0xFF5EEB73 /* 27 et 53 = 0xFF5EEB73 */ +#define WIND_8_SUBBANDS_5_4 (SINT32)0x0034F8B6 /* 11 et 69 = 0x0034F8B6 */ +#define WIND_8_SUBBANDS_6_0 (SINT32)0x003060F4 /* 6 et 74 = 0x003060F4 */ +#define WIND_8_SUBBANDS_6_1 (SINT32)0x02138653 /* 22 et 58 = 0x02138653 */ +#define WIND_8_SUBBANDS_6_2 (SINT32)0x120435FA /* 38 et 42 = 0x120435FA */ +#define WIND_8_SUBBANDS_6_3 (SINT32)0x005FD0FF /* 26 et 54 = 0x005FD0FF */ +#define WIND_8_SUBBANDS_6_4 (SINT32)0x00415B75 /* 10 et 70 = 0x00415B75 */ +#define WIND_8_SUBBANDS_7_0 (SINT32)0x003A72E7 /* 7 et 73 = 0x003A72E7 */ +#define WIND_8_SUBBANDS_7_1 (SINT32)0x01F5F424 /* 23 et 57 = 0x01F5F424 */ +#define WIND_8_SUBBANDS_7_2 (SINT32)0x129C226F /* 39 et 41 = 0x129C226F */ +#define WIND_8_SUBBANDS_7_3 (SINT32)0x01223EBA /* 25 et 55 = 0x01223EBA */ +#define WIND_8_SUBBANDS_7_4 (SINT32)0x0044EF48 /* 9 et 71 = 0x0044EF48 */ +#define WIND_8_SUBBANDS_8_0 (SINT32)0x0041EC6A /* 8 et 72 = 0x0041EC6A */ +#define WIND_8_SUBBANDS_8_1 (SINT32)0x01A7ECEF /* 24 et 56 = 0x01A7ECEF */ +#define WIND_8_SUBBANDS_8_2 (SINT32)0x12CF6C75 /* 40 = 0x12CF6C75 */ +#else +#define WIND_4_SUBBANDS_0_1 (SINT16)0x0166 /* gas32CoeffFor4SBs[8] = -gas32CoeffFor4SBs[32] = 0x01659F45 */ +#define WIND_4_SUBBANDS_0_2 (SINT16)0x115B /* gas32CoeffFor4SBs[16] = -gas32CoeffFor4SBs[24] = 0x115B1ED2 */ +#define WIND_4_SUBBANDS_1_0 (SINT16)0x0012 /* gas32CoeffFor4SBs[1 et 39] = 0x001194E6 */ +#define WIND_4_SUBBANDS_1_1 (SINT16)0x029E /* gas32CoeffFor4SBs[9 et 31] = 0x029DBAA3 */ +#define WIND_4_SUBBANDS_1_2 (SINT16)0x18F5 /* gas32CoeffFor4SBs[17 et 23] = 0x18F55C90 */ +#define WIND_4_SUBBANDS_1_3 (SINT16)0xF610 /* gas32CoeffFor4SBs[15 et 25] = 0xF60FAF37 */ +#define WIND_4_SUBBANDS_1_4 (SINT16)0xFF9C /* gas32CoeffFor4SBs[7 et 33] = 0xFF9BB9D5 */ +#define WIND_4_SUBBANDS_2_0 (SINT16)0x0031 /* gas32CoeffFor4SBs[2 et 38] = 0x0030E2D3 */ +#define WIND_4_SUBBANDS_2_1 (SINT16)0x03B2 /* gas32CoeffFor4SBs[10 et 30] = 0x03B23341 */ +#define WIND_4_SUBBANDS_2_2 (SINT16)0x1F91 /* gas32CoeffFor4SBs[18 et 22] = 0x1F91CA46 */ +#define WIND_4_SUBBANDS_2_3 (SINT16)0xFC50 /* gas32CoeffFor4SBs[14 et 26] = 0xFC4F91D4 */ +#define WIND_4_SUBBANDS_2_4 (SINT16)0x003D /* gas32CoeffFor4SBs[6 et 34] = 0x003D239B */ +#define WIND_4_SUBBANDS_3_0 (SINT16)0x005A /* gas32CoeffFor4SBs[3 et 37] = 0x00599403 */ +#define WIND_4_SUBBANDS_3_1 (SINT16)0x041F /* gas32CoeffFor4SBs[11 et 29] = 0x041EEE40 */ +#define WIND_4_SUBBANDS_3_2 (SINT16)0x2413 /* gas32CoeffFor4SBs[19 et 21] = 0x2412F251 */ +#define WIND_4_SUBBANDS_3_3 (SINT16)0x00C9 /* gas32CoeffFor4SBs[13 et 27] = 0x00C8F2BC */ +#define WIND_4_SUBBANDS_3_4 (SINT16)0x0080 /* gas32CoeffFor4SBs[5 et 35] = 0x007F88E4 */ +#define WIND_4_SUBBANDS_4_0 (SINT16)0x007E /* gas32CoeffFor4SBs[4 et 36] = 0x007DBCC8 */ +#define WIND_4_SUBBANDS_4_1 (SINT16)0x0350 /* gas32CoeffFor4SBs[12 et 28] = 0x034FEE2C */ +#define WIND_4_SUBBANDS_4_2 (SINT16)0x25AC /* gas32CoeffFor4SBs[20] = 25AC1FF2 */ + +#define WIND_8_SUBBANDS_0_1 (SINT16)0x00B9 /* 16 0x12CF6C75 */ +#define WIND_8_SUBBANDS_0_2 (SINT16)0x08B4 /* 32 0x08B4307A */ +#define WIND_8_SUBBANDS_1_0 (SINT16)0x0005 /* 1 et 79 = 0x00052173 */ +#define WIND_8_SUBBANDS_1_1 (SINT16)0x0107 /* 17 et 63 = 0x01071B96 */ +#define WIND_8_SUBBANDS_1_2 (SINT16)0x0A9F /* 33 et 47 = 0x0A9F3E9A*/ +#define WIND_8_SUBBANDS_1_3 (SINT16)0xF931 /* 31 et 49 = 0xF9312891 */ +#define WIND_8_SUBBANDS_1_4 (SINT16)0xFF8D /* 15 et 65 = 0xFF8D6793 */ +#define WIND_8_SUBBANDS_2_0 (SINT16)0x000B /* 2 et 78 = 0x000B3F71 */ +#define WIND_8_SUBBANDS_2_1 (SINT16)0x0157 /* 18 et 62 = 0x0156B3CA */ +#define WIND_8_SUBBANDS_2_2 (SINT16)0x0C7D /* 34 et 46 = 0x0C7D59B6 */ +#define WIND_8_SUBBANDS_2_3 (SINT16)0xFB00 /* 30 et 50 = 0xFAFF95FC */ +#define WIND_8_SUBBANDS_2_4 (SINT16)0xFFCA /* 14 et 66 = 0xFFC9F10E */ +#define WIND_8_SUBBANDS_3_0 (SINT16)0x0012 /* 3 et 77 = 0x00122C7D*/ +#define WIND_8_SUBBANDS_3_1 (SINT16)0x01A2 /* 19 et 61 = 0x01A1B38B */ +#define WIND_8_SUBBANDS_3_2 (SINT16)0x0E3C /* 35 et 45 = 0x0E3BB16F */ +#define WIND_8_SUBBANDS_3_3 (SINT16)0xFCA8 /* 29 et 51 = 0xFCA86E7E */ +#define WIND_8_SUBBANDS_3_4 (SINT16)0xFFFA /* 13 et 67 = 0xFFFA2413 */ +#define WIND_8_SUBBANDS_4_0 (SINT16)0x001B /* 4 et 66 = 0x001AFF89 */ +#define WIND_8_SUBBANDS_4_1 (SINT16)0x01E0 /* 20 et 60 = 0x01E0224C */ +#define WIND_8_SUBBANDS_4_2 (SINT16)0x0FC7 /* 36 et 44 = 0x0FC721F9 */ +#define WIND_8_SUBBANDS_4_3 (SINT16)0xFE20 /* 28 et 52 = 0xFE20435D */ +#define WIND_8_SUBBANDS_4_4 (SINT16)0x001E /* 12 et 68 = 0x001D8FD2 */ +#define WIND_8_SUBBANDS_5_0 (SINT16)0x0025 /* 5 et 75 = 0x00255A62 */ +#define WIND_8_SUBBANDS_5_1 (SINT16)0x0209 /* 21 et 59 = 0x0209291F */ +#define WIND_8_SUBBANDS_5_2 (SINT16)0x110F /* 37 et 43 = 0x110ECEF0 */ +#define WIND_8_SUBBANDS_5_3 (SINT16)0xFF5F /* 27 et 53 = 0xFF5EEB73 */ +#define WIND_8_SUBBANDS_5_4 (SINT16)0x0035 /* 11 et 69 = 0x0034F8B6 */ +#define WIND_8_SUBBANDS_6_0 (SINT16)0x0030 /* 6 et 74 = 0x003060F4 */ +#define WIND_8_SUBBANDS_6_1 (SINT16)0x0214 /* 22 et 58 = 0x02138653 */ +#define WIND_8_SUBBANDS_6_2 (SINT16)0x1204 /* 38 et 42 = 0x120435FA */ +#define WIND_8_SUBBANDS_6_3 (SINT16)0x0060 /* 26 et 54 = 0x005FD0FF */ +#define WIND_8_SUBBANDS_6_4 (SINT16)0x0041 /* 10 et 70 = 0x00415B75 */ +#define WIND_8_SUBBANDS_7_0 (SINT16)0x003A /* 7 et 73 = 0x003A72E7 */ +#define WIND_8_SUBBANDS_7_1 (SINT16)0x01F6 /* 23 et 57 = 0x01F5F424 */ +#define WIND_8_SUBBANDS_7_2 (SINT16)0x129C /* 39 et 41 = 0x129C226F */ +#define WIND_8_SUBBANDS_7_3 (SINT16)0x0122 /* 25 et 55 = 0x01223EBA */ +#define WIND_8_SUBBANDS_7_4 (SINT16)0x0045 /* 9 et 71 = 0x0044EF48 */ +#define WIND_8_SUBBANDS_8_0 (SINT16)0x0042 /* 8 et 72 = 0x0041EC6A */ +#define WIND_8_SUBBANDS_8_1 (SINT16)0x01A8 /* 24 et 56 = 0x01A7ECEF */ +#define WIND_8_SUBBANDS_8_2 (SINT16)0x12CF /* 40 = 0x12CF6C75 */ +#endif + +#if (SBC_USE_ARM_PRAGMA==TRUE) +#pragma arm section zidata = "sbc_s32_analysis_section" +#endif +#if BT_BLE_DYNAMIC_ENV_MEMORY == FALSE +static SINT32 s32DCTY[16] = {0}; +static SINT32 s32X[ENC_VX_BUFFER_SIZE / 2]; +static SINT16 *s16X = (SINT16 *) s32X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#else +static SINT32 *s32DCTY; +static SINT32 *s32X; +static SINT16 *s16X; /* s16X must be 32 bits aligned cf SHIFTUP_X8_2*/ +#endif //BT_BLE_DYNAMIC_ENV_MEMORY == FALSE + +#if (SBC_USE_ARM_PRAGMA==TRUE) +#pragma arm section zidata +#endif + +/* This macro is for 4 subbands */ +#define SHIFTUP_X4 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+38); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); ps32X--; \ + } \ +} +#define SHIFTUP_X4_2 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+38); \ + ps32X2=(SINT32 *)(s16X+(EncMaxShiftCounter<<1)+78); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-2-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-2-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-2-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + } \ +} + +/* This macro is for 8 subbands */ +#define SHIFTUP_X8 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+78); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); ps32X--; \ + } \ +} +#define SHIFTUP_X8_2 \ +{ \ + ps32X=(SINT32 *)(s16X+EncMaxShiftCounter+78); \ + ps32X2=(SINT32 *)(s16X+(EncMaxShiftCounter<<1)+158); \ + for (i=0;i<9;i++) \ + { \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + *ps32X=*(ps32X-4-(ShiftCounter>>1)); *(ps32X2)=*(ps32X2-4-(ShiftCounter>>1)); ps32X--; ps32X2--; \ + } \ +} + +#if (SBC_ARM_ASM_OPT==TRUE) +#define WINDOW_ACCU_8_0 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_0_1,(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + MLA s32Hi,WIND_8_SUBBANDS_0_2,(s16X[ChOffset+32]-s16X[ChOffset+48]),s32Hi;\ + MOV s32DCTY[0],s32Hi;\ + }\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_1_0,s16X[ChOffset+1];\ + MUL s32Hi2,WIND_8_SUBBANDS_1_0,s16X[ChOffset+64+15];\ + MLA s32Hi,WIND_8_SUBBANDS_1_1,s16X[ChOffset+16+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_1,s16X[ChOffset+48+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_2,s16X[ChOffset+32+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_2,s16X[ChOffset+32+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_3,s16X[ChOffset+48+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_3,s16X[ChOffset+16+15],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_1_4,s16X[ChOffset+64+1],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_1_4,s16X[ChOffset+15],s32Hi2;\ + MOV s32DCTY[1],s32Hi;\ + MOV s32DCTY[15],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_2_0,s16X[ChOffset+2];\ + MUL s32Hi2,WIND_8_SUBBANDS_2_0,s16X[ChOffset+64+14];\ + MLA s32Hi,WIND_8_SUBBANDS_2_1,s16X[ChOffset+16+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_1,s16X[ChOffset+48+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_2,s16X[ChOffset+32+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_2,s16X[ChOffset+32+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_3,s16X[ChOffset+48+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_3,s16X[ChOffset+16+14],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_2_4,s16X[ChOffset+64+2],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_2_4,s16X[ChOffset+14],s32Hi2;\ + MOV s32DCTY[2],s32Hi;\ + MOV s32DCTY[14],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_3_0,s16X[ChOffset+3];\ + MUL s32Hi2,WIND_8_SUBBANDS_3_0,s16X[ChOffset+64+13];\ + MLA s32Hi,WIND_8_SUBBANDS_3_1,s16X[ChOffset+16+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_1,s16X[ChOffset+48+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_2,s16X[ChOffset+32+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_2,s16X[ChOffset+32+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_3,s16X[ChOffset+48+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_3,s16X[ChOffset+16+13],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_3_4,s16X[ChOffset+64+3],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_3_4,s16X[ChOffset+13],s32Hi2;\ + MOV s32DCTY[3],s32Hi;\ + MOV s32DCTY[13],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_4_0,s16X[ChOffset+4];\ + MUL s32Hi2,WIND_8_SUBBANDS_4_0,s16X[ChOffset+64+12];\ + MLA s32Hi,WIND_8_SUBBANDS_4_1,s16X[ChOffset+16+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_1,s16X[ChOffset+48+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_2,s16X[ChOffset+32+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_2,s16X[ChOffset+32+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_3,s16X[ChOffset+48+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_3,s16X[ChOffset+16+12],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_4_4,s16X[ChOffset+64+4],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_4_4,s16X[ChOffset+12],s32Hi2;\ + MOV s32DCTY[4],s32Hi;\ + MOV s32DCTY[12],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_5_0,s16X[ChOffset+5];\ + MUL s32Hi2,WIND_8_SUBBANDS_5_0,s16X[ChOffset+64+11];\ + MLA s32Hi,WIND_8_SUBBANDS_5_1,s16X[ChOffset+16+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_1,s16X[ChOffset+48+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_2,s16X[ChOffset+32+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_2,s16X[ChOffset+32+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_3,s16X[ChOffset+48+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_3,s16X[ChOffset+16+11],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_5_4,s16X[ChOffset+64+5],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_5_4,s16X[ChOffset+11],s32Hi2;\ + MOV s32DCTY[5],s32Hi;\ + MOV s32DCTY[11],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_6_0,s16X[ChOffset+6];\ + MUL s32Hi2,WIND_8_SUBBANDS_6_0,s16X[ChOffset+64+10];\ + MLA s32Hi,WIND_8_SUBBANDS_6_1,s16X[ChOffset+16+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_1,s16X[ChOffset+48+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_2,s16X[ChOffset+32+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_2,s16X[ChOffset+32+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_3,s16X[ChOffset+48+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_3,s16X[ChOffset+16+10],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_6_4,s16X[ChOffset+64+6],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_6_4,s16X[ChOffset+10],s32Hi2;\ + MOV s32DCTY[6],s32Hi;\ + MOV s32DCTY[10],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_7_0,s16X[ChOffset+7];\ + MUL s32Hi2,WIND_8_SUBBANDS_7_0,s16X[ChOffset+64+9];\ + MLA s32Hi,WIND_8_SUBBANDS_7_1,s16X[ChOffset+16+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_1,s16X[ChOffset+48+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_2,s16X[ChOffset+32+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_2,s16X[ChOffset+32+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_3,s16X[ChOffset+48+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_3,s16X[ChOffset+16+9],s32Hi2;\ + MLA s32Hi,WIND_8_SUBBANDS_7_4,s16X[ChOffset+64+7],s32Hi;\ + MLA s32Hi2,WIND_8_SUBBANDS_7_4,s16X[ChOffset+9],s32Hi2;\ + MOV s32DCTY[7],s32Hi;\ + MOV s32DCTY[9],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_8_8 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_8_SUBBANDS_8_0,(s16X[ChOffset+8]+s16X[ChOffset+8+64]);\ + MLA s32Hi,WIND_8_SUBBANDS_8_1,(s16X[ChOffset+8+16]+s16X[ChOffset+8+64]),s32Hi;\ + MLA s32Hi,WIND_8_SUBBANDS_8_2,s16X[ChOffset+8+32],s32Hi;\ + MOV s32DCTY[8],s32Hi;\ + }\ +} +#define WINDOW_ACCU_4_0 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_0_1,(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + MLA s32Hi,WIND_4_SUBBANDS_0_2,(s16X[ChOffset+16]-s16X[ChOffset+24]),s32Hi;\ + MOV s32DCTY[0],s32Hi;\ + }\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_1_0,s16X[ChOffset+1];\ + MUL s32Hi2,WIND_4_SUBBANDS_1_0,s16X[ChOffset+32+7];\ + MLA s32Hi,WIND_4_SUBBANDS_1_1,s16X[ChOffset+8+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_1,s16X[ChOffset+24+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_2,s16X[ChOffset+16+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_2,s16X[ChOffset+16+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_3,s16X[ChOffset+24+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_3,s16X[ChOffset+8+7],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_1_4,s16X[ChOffset+32+1],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_1_4,s16X[ChOffset+7],s32Hi2;\ + MOV s32DCTY[1],s32Hi;\ + MOV s32DCTY[7],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_2_0,s16X[ChOffset+2];\ + MUL s32Hi2,WIND_4_SUBBANDS_2_0,s16X[ChOffset+32+6];\ + MLA s32Hi,WIND_4_SUBBANDS_2_1,s16X[ChOffset+8+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_1,s16X[ChOffset+24+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_2,s16X[ChOffset+16+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_2,s16X[ChOffset+16+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_3,s16X[ChOffset+24+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_3,s16X[ChOffset+8+6],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_2_4,s16X[ChOffset+32+2],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_2_4,s16X[ChOffset+6],s32Hi2;\ + MOV s32DCTY[2],s32Hi;\ + MOV s32DCTY[6],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_3_0,s16X[ChOffset+3];\ + MUL s32Hi2,WIND_4_SUBBANDS_3_0,s16X[ChOffset+32+5];\ + MLA s32Hi,WIND_4_SUBBANDS_3_1,s16X[ChOffset+8+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_1,s16X[ChOffset+24+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_2,s16X[ChOffset+16+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_2,s16X[ChOffset+16+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_3,s16X[ChOffset+24+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_3,s16X[ChOffset+8+5],s32Hi2;\ + MLA s32Hi,WIND_4_SUBBANDS_3_4,s16X[ChOffset+32+3],s32Hi;\ + MLA s32Hi2,WIND_4_SUBBANDS_3_4,s16X[ChOffset+5],s32Hi2;\ + MOV s32DCTY[3],s32Hi;\ + MOV s32DCTY[5],s32Hi2;\ + }\ +} +#define WINDOW_ACCU_4_4 \ +{\ + __asm\ + {\ + MUL s32Hi,WIND_4_SUBBANDS_4_0,(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + MLA s32Hi,WIND_4_SUBBANDS_4_1,(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]),s32Hi;\ + MLA s32Hi,WIND_4_SUBBANDS_4_2,s16X[ChOffset+4+16],s32Hi;\ + MOV s32DCTY[4],s32Hi;\ + }\ +} + +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4_0; WINDOW_ACCU_4_1_7;\ + WINDOW_ACCU_4_2_6; WINDOW_ACCU_4_3_5;\ + WINDOW_ACCU_4_4;\ +} + +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8_0; WINDOW_ACCU_8_1_15;\ + WINDOW_ACCU_8_2_14; WINDOW_ACCU_8_3_13;\ + WINDOW_ACCU_8_4_12; WINDOW_ACCU_8_5_11;\ + WINDOW_ACCU_8_6_10; WINDOW_ACCU_8_7_9;\ + WINDOW_ACCU_8_8;\ +} + +#else +#if (SBC_IPAQ_OPT==TRUE) + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_8_0 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_0_1*(SINT64)(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_0_2*(SINT64)(s16X[ChOffset+32]-s16X[ChOffset+48]);\ + s32DCTY[0]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_1_0*(SINT64)s16X[ChOffset+1];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_1_0*(SINT64)s16X[ChOffset+64+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_1*(SINT64)s16X[ChOffset+16+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_1*(SINT64)s16X[ChOffset+48+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_2*(SINT64)s16X[ChOffset+32+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_2*(SINT64)s16X[ChOffset+32+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_3*(SINT64)s16X[ChOffset+48+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_3*(SINT64)s16X[ChOffset+16+15];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_1_4*(SINT64)s16X[ChOffset+64+1];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_1_4*(SINT64)s16X[ChOffset+15];\ + s32DCTY[1]=(SINT32)(s64Temp>>16);\ + s32DCTY[15]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_2_0*(SINT64)s16X[ChOffset+2];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_2_0*(SINT64)s16X[ChOffset+64+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_1*(SINT64)s16X[ChOffset+16+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_1*(SINT64)s16X[ChOffset+48+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_2*(SINT64)s16X[ChOffset+32+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_2*(SINT64)s16X[ChOffset+32+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_3*(SINT64)s16X[ChOffset+48+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_3*(SINT64)s16X[ChOffset+16+14];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_2_4*(SINT64)s16X[ChOffset+64+2];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_2_4*(SINT64)s16X[ChOffset+14];\ + s32DCTY[2]=(SINT32)(s64Temp>>16);\ + s32DCTY[14]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_3_0*(SINT64)s16X[ChOffset+3];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_3_0*(SINT64)s16X[ChOffset+64+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_1*(SINT64)s16X[ChOffset+16+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_1*(SINT64)s16X[ChOffset+48+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_2*(SINT64)s16X[ChOffset+32+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_2*(SINT64)s16X[ChOffset+32+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_3*(SINT64)s16X[ChOffset+48+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_3*(SINT64)s16X[ChOffset+16+13];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_3_4*(SINT64)s16X[ChOffset+64+3];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_3_4*(SINT64)s16X[ChOffset+13];\ + s32DCTY[3]=(SINT32)(s64Temp>>16);\ + s32DCTY[13]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_4_0*(SINT64)s16X[ChOffset+4];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_4_0*(SINT64)s16X[ChOffset+64+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_1*(SINT64)s16X[ChOffset+16+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_1*(SINT64)s16X[ChOffset+48+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_2*(SINT64)s16X[ChOffset+32+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_2*(SINT64)s16X[ChOffset+32+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_3*(SINT64)s16X[ChOffset+48+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_3*(SINT64)s16X[ChOffset+16+12];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_4_4*(SINT64)s16X[ChOffset+64+4];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_4_4*(SINT64)s16X[ChOffset+12];\ + s32DCTY[4]=(SINT32)(s64Temp>>16);\ + s32DCTY[12]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_5_0*(SINT64)s16X[ChOffset+5];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_5_0*(SINT64)s16X[ChOffset+64+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_1*(SINT64)s16X[ChOffset+16+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_1*(SINT64)s16X[ChOffset+48+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_2*(SINT64)s16X[ChOffset+32+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_2*(SINT64)s16X[ChOffset+32+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_3*(SINT64)s16X[ChOffset+48+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_3*(SINT64)s16X[ChOffset+16+11];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_5_4*(SINT64)s16X[ChOffset+64+5];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_5_4*(SINT64)s16X[ChOffset+11];\ + s32DCTY[5]=(SINT32)(s64Temp>>16);\ + s32DCTY[11]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_6_0*(SINT64)s16X[ChOffset+6];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_6_0*(SINT64)s16X[ChOffset+64+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_1*(SINT64)s16X[ChOffset+16+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_1*(SINT64)s16X[ChOffset+48+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_2*(SINT64)s16X[ChOffset+32+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_2*(SINT64)s16X[ChOffset+32+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_3*(SINT64)s16X[ChOffset+48+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_3*(SINT64)s16X[ChOffset+16+10];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_6_4*(SINT64)s16X[ChOffset+64+6];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_6_4*(SINT64)s16X[ChOffset+10];\ + s32DCTY[6]=(SINT32)(s64Temp>>16);\ + s32DCTY[10]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_7_0*(SINT64)s16X[ChOffset+7];\ + s64Temp2=(SINT64)WIND_8_SUBBANDS_7_0*(SINT64)s16X[ChOffset+64+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_1*(SINT64)s16X[ChOffset+16+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_1*(SINT64)s16X[ChOffset+48+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_2*(SINT64)s16X[ChOffset+32+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_2*(SINT64)s16X[ChOffset+32+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_3*(SINT64)s16X[ChOffset+48+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_3*(SINT64)s16X[ChOffset+16+9];\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_7_4*(SINT64)s16X[ChOffset+64+7];\ + s64Temp2+=(SINT64)WIND_8_SUBBANDS_7_4*(SINT64)s16X[ChOffset+9];\ + s32DCTY[7]=(SINT32)(s64Temp>>16);\ + s32DCTY[9]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_8_8 \ +{\ + s64Temp=(SINT64)WIND_8_SUBBANDS_8_0*(SINT64)(s16X[ChOffset+8]+s16X[ChOffset+64+8]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_8_1*(SINT64)(s16X[ChOffset+16+8]+s16X[ChOffset+48+8]);\ + s64Temp+=(SINT64)WIND_8_SUBBANDS_8_2*(SINT64)s16X[ChOffset+32+8];\ + s32DCTY[8]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_4_0 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_0_1*(SINT64)(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_0_2*(SINT64)(s16X[ChOffset+16]-s16X[ChOffset+24]);\ + s32DCTY[0]=(SINT32)(s64Temp>>16);\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_1_0*(SINT64)s16X[ChOffset+1];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_1_0*(SINT64)s16X[ChOffset+32+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_1*(SINT64)s16X[ChOffset+8+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_1*(SINT64)s16X[ChOffset+24+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_2*(SINT64)s16X[ChOffset+16+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_2*(SINT64)s16X[ChOffset+16+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_3*(SINT64)s16X[ChOffset+24+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_3*(SINT64)s16X[ChOffset+8+7];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_1_4*(SINT64)s16X[ChOffset+32+1];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_1_4*(SINT64)s16X[ChOffset+7];\ + s32DCTY[1]=(SINT32)(s64Temp>>16);\ + s32DCTY[7]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_2_0*(SINT64)s16X[ChOffset+2];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_2_0*(SINT64)s16X[ChOffset+32+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_1*(SINT64)s16X[ChOffset+8+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_1*(SINT64)s16X[ChOffset+24+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_2*(SINT64)s16X[ChOffset+16+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_2*(SINT64)s16X[ChOffset+16+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_3*(SINT64)s16X[ChOffset+24+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_3*(SINT64)s16X[ChOffset+8+6];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_2_4*(SINT64)s16X[ChOffset+32+2];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_2_4*(SINT64)s16X[ChOffset+6];\ + s32DCTY[2]=(SINT32)(s64Temp>>16);\ + s32DCTY[6]=(SINT32)(s64Temp2>>16);\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_3_0*(SINT64)s16X[ChOffset+3];\ + s64Temp2=(SINT64)WIND_4_SUBBANDS_3_0*(SINT64)s16X[ChOffset+32+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_1*(SINT64)s16X[ChOffset+8+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_1*(SINT64)s16X[ChOffset+24+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_2*(SINT64)s16X[ChOffset+16+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_2*(SINT64)s16X[ChOffset+16+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_3*(SINT64)s16X[ChOffset+24+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_3*(SINT64)s16X[ChOffset+8+5];\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_3_4*(SINT64)s16X[ChOffset+32+3];\ + s64Temp2+=(SINT64)WIND_4_SUBBANDS_3_4*(SINT64)s16X[ChOffset+5];\ + s32DCTY[3]=(SINT32)(s64Temp>>16);\ + s32DCTY[5]=(SINT32)(s64Temp2>>16);\ +} + +#define WINDOW_ACCU_4_4 \ +{\ + s64Temp=(SINT64)WIND_4_SUBBANDS_4_0*(SINT64)(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_4_1*(SINT64)(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]);\ + s64Temp+=(SINT64)WIND_4_SUBBANDS_4_2*(SINT64)s16X[ChOffset+4+16];\ + s32DCTY[4]=(SINT32)(s64Temp>>16);\ +} +#else /* SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE */ +#define WINDOW_ACCU_8_0 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_0_1*(SINT32)(s16X[ChOffset+16]-s16X[ChOffset+64]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_0_2*(SINT32)(s16X[ChOffset+32]-s16X[ChOffset+48]);\ + s32DCTY[0]=(SINT32)s32Temp;\ +} +#define WINDOW_ACCU_8_1_15 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_1_0*(SINT32)s16X[ChOffset+1];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_1_0*(SINT32)s16X[ChOffset+64+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_1*(SINT32)s16X[ChOffset+16+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_1*(SINT32)s16X[ChOffset+48+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_2*(SINT32)s16X[ChOffset+32+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_2*(SINT32)s16X[ChOffset+32+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_3*(SINT32)s16X[ChOffset+48+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_3*(SINT32)s16X[ChOffset+16+15];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_1_4*(SINT32)s16X[ChOffset+64+1];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_1_4*(SINT32)s16X[ChOffset+15];\ + s32DCTY[1]=(SINT32)s32Temp;\ + s32DCTY[15]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_2_14 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_2_0*(SINT32)s16X[ChOffset+2];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_2_0*(SINT32)s16X[ChOffset+64+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_1*(SINT32)s16X[ChOffset+16+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_1*(SINT32)s16X[ChOffset+48+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_2*(SINT32)s16X[ChOffset+32+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_2*(SINT32)s16X[ChOffset+32+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_3*(SINT32)s16X[ChOffset+48+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_3*(SINT32)s16X[ChOffset+16+14];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_2_4*(SINT32)s16X[ChOffset+64+2];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_2_4*(SINT32)s16X[ChOffset+14];\ + s32DCTY[2]=(SINT32)s32Temp;\ + s32DCTY[14]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_3_13 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_3_0*(SINT32)s16X[ChOffset+3];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_3_0*(SINT32)s16X[ChOffset+64+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_1*(SINT32)s16X[ChOffset+16+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_1*(SINT32)s16X[ChOffset+48+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_2*(SINT32)s16X[ChOffset+32+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_2*(SINT32)s16X[ChOffset+32+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_3*(SINT32)s16X[ChOffset+48+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_3*(SINT32)s16X[ChOffset+16+13];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_3_4*(SINT32)s16X[ChOffset+64+3];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_3_4*(SINT32)s16X[ChOffset+13];\ + s32DCTY[3]=(SINT32)s32Temp;\ + s32DCTY[13]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_4_12 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_4_0*(SINT32)s16X[ChOffset+4];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_4_0*(SINT32)s16X[ChOffset+64+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_1*(SINT32)s16X[ChOffset+16+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_1*(SINT32)s16X[ChOffset+48+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_2*(SINT32)s16X[ChOffset+32+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_2*(SINT32)s16X[ChOffset+32+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_3*(SINT32)s16X[ChOffset+48+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_3*(SINT32)s16X[ChOffset+16+12];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_4_4*(SINT32)s16X[ChOffset+64+4];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_4_4*(SINT32)s16X[ChOffset+12];\ + s32DCTY[4]=(SINT32)s32Temp;\ + s32DCTY[12]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_5_11 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_5_0*(SINT32)s16X[ChOffset+5];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_5_0*(SINT32)s16X[ChOffset+64+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_1*(SINT32)s16X[ChOffset+16+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_1*(SINT32)s16X[ChOffset+48+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_2*(SINT32)s16X[ChOffset+32+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_2*(SINT32)s16X[ChOffset+32+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_3*(SINT32)s16X[ChOffset+48+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_3*(SINT32)s16X[ChOffset+16+11];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_5_4*(SINT32)s16X[ChOffset+64+5];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_5_4*(SINT32)s16X[ChOffset+11];\ + s32DCTY[5]=(SINT32)s32Temp;\ + s32DCTY[11]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_6_10 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_6_0*(SINT32)s16X[ChOffset+6];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_6_0*(SINT32)s16X[ChOffset+64+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_1*(SINT32)s16X[ChOffset+16+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_1*(SINT32)s16X[ChOffset+48+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_2*(SINT32)s16X[ChOffset+32+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_2*(SINT32)s16X[ChOffset+32+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_3*(SINT32)s16X[ChOffset+48+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_3*(SINT32)s16X[ChOffset+16+10];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_6_4*(SINT32)s16X[ChOffset+64+6];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_6_4*(SINT32)s16X[ChOffset+10];\ + s32DCTY[6]=(SINT32)s32Temp;\ + s32DCTY[10]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_7_9 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_7_0*(SINT32)s16X[ChOffset+7];\ + s32Temp2=(SINT32)WIND_8_SUBBANDS_7_0*(SINT32)s16X[ChOffset+64+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_1*(SINT32)s16X[ChOffset+16+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_1*(SINT32)s16X[ChOffset+48+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_2*(SINT32)s16X[ChOffset+32+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_2*(SINT32)s16X[ChOffset+32+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_3*(SINT32)s16X[ChOffset+48+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_3*(SINT32)s16X[ChOffset+16+9];\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_7_4*(SINT32)s16X[ChOffset+64+7];\ + s32Temp2+=(SINT32)WIND_8_SUBBANDS_7_4*(SINT32)s16X[ChOffset+9];\ + s32DCTY[7]=(SINT32)s32Temp;\ + s32DCTY[9]=(SINT32)s32Temp2;\ +} +#define WINDOW_ACCU_8_8 \ +{\ + s32Temp=(SINT32)WIND_8_SUBBANDS_8_0*(SINT32)(s16X[ChOffset+8]+s16X[ChOffset+64+8]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_8_1*(SINT32)(s16X[ChOffset+16+8]+s16X[ChOffset+48+8]);\ + s32Temp+=(SINT32)WIND_8_SUBBANDS_8_2*(SINT32)s16X[ChOffset+32+8];\ + s32DCTY[8]=(SINT32)s32Temp;\ +} +#define WINDOW_ACCU_4_0 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_0_1*(SINT32)(s16X[ChOffset+8]-s16X[ChOffset+32]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_0_2*(SINT32)(s16X[ChOffset+16]-s16X[ChOffset+24]);\ + s32DCTY[0]=(SINT32)(s32Temp);\ +} +#define WINDOW_ACCU_4_1_7 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_1_0*(SINT32)s16X[ChOffset+1];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_1_0*(SINT32)s16X[ChOffset+32+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_1*(SINT32)s16X[ChOffset+8+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_1*(SINT32)s16X[ChOffset+24+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_2*(SINT32)s16X[ChOffset+16+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_2*(SINT32)s16X[ChOffset+16+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_3*(SINT32)s16X[ChOffset+24+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_3*(SINT32)s16X[ChOffset+8+7];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_1_4*(SINT32)s16X[ChOffset+32+1];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_1_4*(SINT32)s16X[ChOffset+7];\ + s32DCTY[1]=(SINT32)(s32Temp);\ + s32DCTY[7]=(SINT32)(s32Temp2);\ +} +#define WINDOW_ACCU_4_2_6 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_2_0*(SINT32)s16X[ChOffset+2];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_2_0*(SINT32)s16X[ChOffset+32+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_1*(SINT32)s16X[ChOffset+8+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_1*(SINT32)s16X[ChOffset+24+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_2*(SINT32)s16X[ChOffset+16+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_2*(SINT32)s16X[ChOffset+16+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_3*(SINT32)s16X[ChOffset+24+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_3*(SINT32)s16X[ChOffset+8+6];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_2_4*(SINT32)s16X[ChOffset+32+2];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_2_4*(SINT32)s16X[ChOffset+6];\ + s32DCTY[2]=(SINT32)(s32Temp);\ + s32DCTY[6]=(SINT32)(s32Temp2);\ +} +#define WINDOW_ACCU_4_3_5 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_3_0*(SINT32)s16X[ChOffset+3];\ + s32Temp2=(SINT32)WIND_4_SUBBANDS_3_0*(SINT32)s16X[ChOffset+32+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_1*(SINT32)s16X[ChOffset+8+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_1*(SINT32)s16X[ChOffset+24+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_2*(SINT32)s16X[ChOffset+16+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_2*(SINT32)s16X[ChOffset+16+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_3*(SINT32)s16X[ChOffset+24+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_3*(SINT32)s16X[ChOffset+8+5];\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_3_4*(SINT32)s16X[ChOffset+32+3];\ + s32Temp2+=(SINT32)WIND_4_SUBBANDS_3_4*(SINT32)s16X[ChOffset+5];\ + s32DCTY[3]=(SINT32)(s32Temp);\ + s32DCTY[5]=(SINT32)(s32Temp2);\ +} + +#define WINDOW_ACCU_4_4 \ +{\ + s32Temp=(SINT32)WIND_4_SUBBANDS_4_0*(SINT32)(s16X[ChOffset+4]+s16X[ChOffset+4+32]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_4_1*(SINT32)(s16X[ChOffset+4+8]+s16X[ChOffset+4+24]);\ + s32Temp+=(SINT32)WIND_4_SUBBANDS_4_2*(SINT32)s16X[ChOffset+4+16];\ + s32DCTY[4]=(SINT32)(s32Temp);\ +} +#endif +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4_0; WINDOW_ACCU_4_1_7;\ + WINDOW_ACCU_4_2_6; WINDOW_ACCU_4_3_5;\ + WINDOW_ACCU_4_4;\ +} + +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8_0; WINDOW_ACCU_8_1_15;\ + WINDOW_ACCU_8_2_14; WINDOW_ACCU_8_3_13;\ + WINDOW_ACCU_8_4_12; WINDOW_ACCU_8_5_11;\ + WINDOW_ACCU_8_6_10; WINDOW_ACCU_8_7_9;\ + WINDOW_ACCU_8_8;\ +} +#else +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_4(i) \ +{\ + s64Temp=((SINT64)gas32CoeffFor4SBs[i] * (SINT64)s16X[ChOffset+i]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+8)] * (SINT64)s16X[ChOffset+i+8]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+16)] * (SINT64)s16X[ChOffset+i+16]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+24)] * (SINT64)s16X[ChOffset+i+24]); \ + s64Temp+=((SINT64)gas32CoeffFor4SBs[(i+32)] * (SINT64)s16X[ChOffset+i+32]); \ + s32DCTY[i]=(SINT32)(s64Temp>>16);\ + /*BT_WARN("s32DCTY4: 0x%x \n", s32DCTY[i]);*/\ +} +#else +#define WINDOW_ACCU_4(i) \ +{\ + s32DCTY[i]=(gas32CoeffFor4SBs[i * 2] * s16X[ChOffset+i]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[(i * 2) + 1]) * s16X[ChOffset+i]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+8) * 2] * s16X[ChOffset+i+8]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+8) * 2) + 1]) * s16X[ChOffset+i+8]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+16) * 2] * s16X[ChOffset+i+16]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+16) * 2) + 1]) * s16X[ChOffset+i+16]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+24) * 2] * s16X[ChOffset+i+24]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+24) * 2) + 1]) * s16X[ChOffset+i+24]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor4SBs[(i+32) * 2] * s16X[ChOffset+i+32]) \ + + (((SINT32)(UINT16)(gas32CoeffFor4SBs[((i+32) * 2) + 1]) * s16X[ChOffset+i+32]) >> 16); \ +} +#endif +#define WINDOW_PARTIAL_4 \ +{\ + WINDOW_ACCU_4(0); WINDOW_ACCU_4(1);\ + WINDOW_ACCU_4(2); WINDOW_ACCU_4(3);\ + WINDOW_ACCU_4(4); WINDOW_ACCU_4(5);\ + WINDOW_ACCU_4(6); WINDOW_ACCU_4(7);\ +} + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) +#define WINDOW_ACCU_8(i) \ +{\ + s64Temp = ((((SINT64)gas32CoeffFor8SBs[i] * (SINT64)s16X[ChOffset+i] ))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+16)] * (SINT64)s16X[ChOffset+i+16]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+32)] * (SINT64)s16X[ChOffset+i+32]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+48)] * (SINT64)s16X[ChOffset+i+48]))); \ + s64Temp+= ((((SINT64)gas32CoeffFor8SBs[(i+64)] * (SINT64)s16X[ChOffset+i+64]))); \ + /*BT_WARN("s32DCTY8: %d= 0x%x * %d\n", s32DCTY[i], gas32CoeffFor8SBs[i], s16X[ChOffset+i]);*/ \ + s32DCTY[i]=(SINT32)(s64Temp>>16);\ +} +#else +#define WINDOW_ACCU_8(i) \ +{\ + s32DCTY[i]=(gas32CoeffFor8SBs[i * 2] * s16X[ChOffset+i]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[(i * 2) + 1]) * s16X[ChOffset+i]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+16) * 2] * s16X[ChOffset+i+16]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+16) * 2) + 1]) * s16X[ChOffset+i+16]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+32) * 2] * s16X[ChOffset+i+32]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+32) * 2) + 1]) * s16X[ChOffset+i+32]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+48) * 2] * s16X[ChOffset+i+48]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+48) * 2) + 1]) * s16X[ChOffset+i+48]) >> 16); \ + s32DCTY[i]+=(gas32CoeffFor8SBs[(i+64) * 2] * s16X[ChOffset+i+64]) \ + + (((SINT32)(UINT16)(gas32CoeffFor8SBs[((i+64) * 2) + 1]) * s16X[ChOffset+i+64]) >> 16); \ + /*BT_WARN("s32DCTY8: %d = 0x%4x%4x * %d\n", s32DCTY[i], gas32CoeffFor8SBs[i * 2], (gas32CoeffFor8SBs[(i * 2) + 1]), s16X[ChOffset+i]);*/\ + /*s32DCTY[i]=(SINT32)(s64Temp>>16);*/\ +} +#endif +#define WINDOW_PARTIAL_8 \ +{\ + WINDOW_ACCU_8(0); WINDOW_ACCU_8(1);\ + WINDOW_ACCU_8(2); WINDOW_ACCU_8(3);\ + WINDOW_ACCU_8(4); WINDOW_ACCU_8(5);\ + WINDOW_ACCU_8(6); WINDOW_ACCU_8(7);\ + WINDOW_ACCU_8(8); WINDOW_ACCU_8(9);\ + WINDOW_ACCU_8(10); WINDOW_ACCU_8(11);\ + WINDOW_ACCU_8(12); WINDOW_ACCU_8(13);\ + WINDOW_ACCU_8(14); WINDOW_ACCU_8(15);\ +} +#endif +#endif + +static SINT16 ShiftCounter = 0; +extern SINT16 EncMaxShiftCounter; +/**************************************************************************** +* SbcAnalysisFilter - performs Analysis of the input audio stream +* +* RETURNS : N/A +*/ +void SbcAnalysisFilter4(SBC_ENC_PARAMS *pstrEncParams) +{ + SINT16 *ps16PcmBuf; + SINT32 *ps32SbBuf; + SINT32 s32Blk, s32Ch; + SINT32 s32NumOfChannels, s32NumOfBlocks; + SINT32 i, *ps32X, *ps32X2; + SINT32 Offset, Offset2, ChOffset; +#if (SBC_ARM_ASM_OPT==TRUE) + register SINT32 s32Hi, s32Hi2; +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + register SINT64 s64Temp, s64Temp2; +#else + register SINT32 s32Temp, s32Temp2; +#endif +#else + +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + SINT64 s64Temp; +#endif + +#endif +#endif + + s32NumOfChannels = pstrEncParams->s16NumOfChannels; + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + + ps16PcmBuf = pstrEncParams->ps16NextPcmBuffer; + + ps32SbBuf = pstrEncParams->s32SbBuffer; + Offset2 = (SINT32)(EncMaxShiftCounter + 40); + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + Offset = (SINT32)(EncMaxShiftCounter - ShiftCounter); + /* Store new samples */ + if (s32NumOfChannels == 1) { + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } else { + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } + for (s32Ch = 0; s32Ch < s32NumOfChannels; s32Ch++) { + ChOffset = s32Ch * Offset2 + Offset; + + WINDOW_PARTIAL_4 + + SBC_FastIDCT4(s32DCTY, ps32SbBuf); + + ps32SbBuf += SUB_BANDS_4; + } + if (s32NumOfChannels == 1) { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X4; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_4; + } + } else { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X4_2; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_4; + } + } + } +} + +/* //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */ +void SbcAnalysisFilter8 (SBC_ENC_PARAMS *pstrEncParams) +{ + SINT16 *ps16PcmBuf; + SINT32 *ps32SbBuf; + SINT32 s32Blk, s32Ch; /* counter for block*/ + SINT32 Offset, Offset2; + SINT32 s32NumOfChannels, s32NumOfBlocks; + SINT32 i, *ps32X, *ps32X2; + SINT32 ChOffset; +#if (SBC_ARM_ASM_OPT==TRUE) + register SINT32 s32Hi, s32Hi2; +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + register SINT64 s64Temp, s64Temp2; +#else + register SINT32 s32Temp, s32Temp2; +#endif +#else +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) + SINT64 s64Temp; +#endif +#endif +#endif + + s32NumOfChannels = pstrEncParams->s16NumOfChannels; + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + + ps16PcmBuf = pstrEncParams->ps16NextPcmBuffer; + + ps32SbBuf = pstrEncParams->s32SbBuffer; + Offset2 = (SINT32)(EncMaxShiftCounter + 80); + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + Offset = (SINT32)(EncMaxShiftCounter - ShiftCounter); + /* Store new samples */ + if (s32NumOfChannels == 1) { + s16X[7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } else { + s16X[7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 7 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 6 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 5 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 4 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 3 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 2 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 1 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + s16X[Offset2 + 0 + Offset] = *ps16PcmBuf; ps16PcmBuf++; + } + for (s32Ch = 0; s32Ch < s32NumOfChannels; s32Ch++) { + ChOffset = s32Ch * Offset2 + Offset; + + WINDOW_PARTIAL_8 + + SBC_FastIDCT8 (s32DCTY, ps32SbBuf); + + ps32SbBuf += SUB_BANDS_8; + } + if (s32NumOfChannels == 1) { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X8; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_8; + } + } else { + if (ShiftCounter >= EncMaxShiftCounter) { + SHIFTUP_X8_2; + ShiftCounter = 0; + } else { + ShiftCounter += SUB_BANDS_8; + } + } + } +} + +void SbcAnalysisInit (void) +{ + static bool loaded = false; + if (!loaded) { + loaded = true; +#if BT_BLE_DYNAMIC_ENV_MEMORY == TRUE + s32X = (SINT32 *)osi_malloc(sizeof(SINT32) * (ENC_VX_BUFFER_SIZE / 2)); + s32DCTY = (SINT32 *)osi_malloc(sizeof(SINT32) * 16); + assert(s32X); + assert(s32DCTY); + memset(s32X, 0, sizeof(SINT16) * ENC_VX_BUFFER_SIZE); + memset(s32DCTY, 0, sizeof(SINT32) * 16); + s16X = (SINT16 *) s32X; +#endif + } + memset(s16X, 0, ENC_VX_BUFFER_SIZE * sizeof(SINT16)); + ShiftCounter = 0; +} + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_dct.c b/components/ble/ble_stack/sbc/enc/sbc_dct.c new file mode 100644 index 00000000..34f2a053 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_dct.c @@ -0,0 +1,243 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * source file for fast dct operations + * + ******************************************************************************/ +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" +#include "sbc_dct.h" + + +#if defined(SBC_ENC_INCLUDED) + +/******************************************************************************* +** +** Function SBC_FastIDCT8 +** +** Description implementation of fast DCT algorithm by Feig and Winograd +** +** +** Returns y = dct(pInVect) +** +** +*******************************************************************************/ + +#if (SBC_IS_64_MULT_IN_IDCT == FALSE) +#define SBC_COS_PI_SUR_4 (0x00005a82) /* ((0x8000) * 0.7071) = cos(pi/4) */ +#define SBC_COS_PI_SUR_8 (0x00007641) /* ((0x8000) * 0.9239) = (cos(pi/8)) */ +#define SBC_COS_3PI_SUR_8 (0x000030fb) /* ((0x8000) * 0.3827) = (cos(3*pi/8)) */ +#define SBC_COS_PI_SUR_16 (0x00007d8a) /* ((0x8000) * 0.9808)) = (cos(pi/16)) */ +#define SBC_COS_3PI_SUR_16 (0x00006a6d) /* ((0x8000) * 0.8315)) = (cos(3*pi/16)) */ +#define SBC_COS_5PI_SUR_16 (0x0000471c) /* ((0x8000) * 0.5556)) = (cos(5*pi/16)) */ +#define SBC_COS_7PI_SUR_16 (0x000018f8) /* ((0x8000) * 0.1951)) = (cos(7*pi/16)) */ +#define SBC_IDCT_MULT(a,b,c) SBC_MULT_32_16_SIMPLIFIED(a,b,c) +#else +#define SBC_COS_PI_SUR_4 (0x5A827999) /* ((0x80000000) * 0.707106781) = (cos(pi/4) ) */ +#define SBC_COS_PI_SUR_8 (0x7641AF3C) /* ((0x80000000) * 0.923879533) = (cos(pi/8) ) */ +#define SBC_COS_3PI_SUR_8 (0x30FBC54D) /* ((0x80000000) * 0.382683432) = (cos(3*pi/8) ) */ +#define SBC_COS_PI_SUR_16 (0x7D8A5F3F) /* ((0x80000000) * 0.98078528 )) = (cos(pi/16) ) */ +#define SBC_COS_3PI_SUR_16 (0x6A6D98A4) /* ((0x80000000) * 0.831469612)) = (cos(3*pi/16)) */ +#define SBC_COS_5PI_SUR_16 (0x471CECE6) /* ((0x80000000) * 0.555570233)) = (cos(5*pi/16)) */ +#define SBC_COS_7PI_SUR_16 (0x18F8B83C) /* ((0x80000000) * 0.195090322)) = (cos(7*pi/16)) */ +#define SBC_IDCT_MULT(a,b,c) SBC_MULT_32_32(a,b,c) +#endif /* SBC_IS_64_MULT_IN_IDCT */ + +#if (SBC_FAST_DCT == FALSE) +extern const SINT16 gas16AnalDCTcoeff8[]; +extern const SINT16 gas16AnalDCTcoeff4[]; +#endif + +void SBC_FastIDCT8(SINT32 *pInVect, SINT32 *pOutVect) +{ +#if (SBC_FAST_DCT == TRUE) +#if (SBC_ARM_ASM_OPT==TRUE) +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT64 s64Temp; +#endif +#else +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT32 s32HiTemp; +#else + SINT32 s32In2Temp; + register SINT32 s32In1Temp; +#endif +#endif +#endif + + register SINT32 x0, x1, x2, x3, x4, x5, x6, x7, temp; + SINT32 res_even[4], res_odd[4]; + /*x0= (pInVect[4])/2 ;*/ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, pInVect[4], x0); + /*BT_WARN("x0 0x%x = %d = %d * %d\n", x0, x0, SBC_COS_PI_SUR_4, pInVect[4]);*/ + + x1 = (pInVect[3] + pInVect[5]) >> 1; + x2 = (pInVect[2] + pInVect[6]) >> 1; + x3 = (pInVect[1] + pInVect[7]) >> 1; + x4 = (pInVect[0] + pInVect[8]) >> 1; + x5 = (pInVect[9] - pInVect[15]) >> 1; + x6 = (pInVect[10] - pInVect[14]) >> 1; + x7 = (pInVect[11] - pInVect[13]) >> 1; + + /* 2-point IDCT of x0 and x4 as in (11) */ + temp = x0 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, ( x0 + x4 ), x0); /*x0 = ( x0 + x4 ) * cos(1*pi/4) ; */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, ( temp - x4 ), x4); /*x4 = ( temp - x4 ) * cos(1*pi/4) ; */ + + /* rearrangement of x2 and x6 as in (15) */ + x2 -= x6; + x6 <<= 1 ; + + /* 2-point IDCT of x2 and x6 and post-multiplication as in (15) */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x6, x6); /*x6 = x6 * cos(1*pi/4) ; */ + temp = x2 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_8, ( x2 + x6 ), x2); /*x2 = ( x2 + x6 ) * cos(1*pi/8) ; */ + SBC_IDCT_MULT(SBC_COS_3PI_SUR_8, ( temp - x6 ), x6); /*x6 = ( temp - x6 ) * cos(3*pi/8) ;*/ + + /* 4-point IDCT of x0,x2,x4 and x6 as in (11) */ + res_even[ 0 ] = x0 + x2 ; + res_even[ 1 ] = x4 + x6 ; + res_even[ 2 ] = x4 - x6 ; + res_even[ 3 ] = x0 - x2 ; + + + /* rearrangement of x1,x3,x5,x7 as in (15) */ + x7 <<= 1 ; + x5 = ( x5 << 1 ) - x7 ; + x3 = ( x3 << 1 ) - x5 ; + x1 -= x3 >> 1 ; + + /* two-dimensional IDCT of x1 and x5 */ + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x5, x5); /*x5 = x5 * cos(1*pi/4) ; */ + temp = x1 ; + x1 = x1 + x5 ; + x5 = temp - x5 ; + + /* rearrangement of x3 and x7 as in (15) */ + x3 -= x7; + x7 <<= 1 ; + SBC_IDCT_MULT(SBC_COS_PI_SUR_4, x7, x7); /*x7 = x7 * cos(1*pi/4) ; */ + + /* 2-point IDCT of x3 and x7 and post-multiplication as in (15) */ + temp = x3 ; + SBC_IDCT_MULT( SBC_COS_PI_SUR_8, ( x3 + x7 ), x3); /*x3 = ( x3 + x7 ) * cos(1*pi/8) ; */ + SBC_IDCT_MULT( SBC_COS_3PI_SUR_8, ( temp - x7 ), x7); /*x7 = ( temp - x7 ) * cos(3*pi/8) ;*/ + + /* 4-point IDCT of x1,x3,x5 and x7 and post multiplication by diagonal matrix as in (14) */ + SBC_IDCT_MULT((SBC_COS_PI_SUR_16), ( x1 + x3 ) , res_odd[0]); /*res_odd[ 0 ] = ( x1 + x3 ) * cos(1*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_3PI_SUR_16), ( x5 + x7 ) , res_odd[1]); /*res_odd[ 1 ] = ( x5 + x7 ) * cos(3*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_5PI_SUR_16), ( x5 - x7 ) , res_odd[2]); /*res_odd[ 2 ] = ( x5 - x7 ) * cos(5*pi/16) ; */ + SBC_IDCT_MULT((SBC_COS_7PI_SUR_16), ( x1 - x3 ) , res_odd[3]); /*res_odd[ 3 ] = ( x1 - x3 ) * cos(7*pi/16) ; */ + + /* additions and subtractions as in (9) */ + pOutVect[0] = (res_even[ 0 ] + res_odd[ 0 ]) ; + pOutVect[1] = (res_even[ 1 ] + res_odd[ 1 ]) ; + pOutVect[2] = (res_even[ 2 ] + res_odd[ 2 ]) ; + pOutVect[3] = (res_even[ 3 ] + res_odd[ 3 ]) ; + pOutVect[7] = (res_even[ 0 ] - res_odd[ 0 ]) ; + pOutVect[6] = (res_even[ 1 ] - res_odd[ 1 ]) ; + pOutVect[5] = (res_even[ 2 ] - res_odd[ 2 ]) ; + pOutVect[4] = (res_even[ 3 ] - res_odd[ 3 ]) ; +#else + UINT8 Index, k; + SINT32 temp; + /*Calculate 4 subband samples by matrixing*/ + for (Index = 0; Index < 8; Index++) { + temp = 0; + for (k = 0; k < 16; k++) { + /*temp += (SINT32)(((SINT64)M[(Index*strEncParams->numOfSubBands*2)+k] * Y[k]) >> 16 );*/ + temp += (gas16AnalDCTcoeff8[(Index * 8 * 2) + k] * (pInVect[k] >> 16)); + temp += ((gas16AnalDCTcoeff8[(Index * 8 * 2) + k] * (pInVect[k] & 0xFFFF)) >> 16); + } + pOutVect[Index] = temp; + } +#endif + /* BT_WARN("pOutVect: 0x%x;0x%x;0x%x;0x%x;0x%x;0x%x;0x%x;0x%x\n",\ + pOutVect[0],pOutVect[1],pOutVect[2],pOutVect[3],pOutVect[4],pOutVect[5],pOutVect[6],pOutVect[7]);*/ +} + +/******************************************************************************* +** +** Function SBC_FastIDCT4 +** +** Description implementation of fast DCT algorithm by Feig and Winograd +** +** +** Returns y = dct(x0) +** +** +*******************************************************************************/ +void SBC_FastIDCT4(SINT32 *pInVect, SINT32 *pOutVect) +{ +#if (SBC_FAST_DCT == TRUE) +#if (SBC_ARM_ASM_OPT==TRUE) +#else +#if (SBC_IPAQ_OPT==TRUE) +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT64 s64Temp; +#endif +#else +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) + SINT32 s32HiTemp; +#else + UINT16 s32In2Temp; + SINT32 s32In1Temp; +#endif +#endif +#endif + SINT32 temp, x2; + SINT32 tmp[8]; + + x2 = pInVect[2] >> 1; + temp = (pInVect[0] + pInVect[4]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_4 >> 1), temp , tmp[0]); + tmp[1] = x2 - tmp[0]; + tmp[0] += x2; + temp = (pInVect[1] + pInVect[3]); + SBC_IDCT_MULT((SBC_COS_3PI_SUR_8 >> 1), temp , tmp[3]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_8 >> 1), temp , tmp[2]); + temp = (pInVect[5] - pInVect[7]); + SBC_IDCT_MULT((SBC_COS_3PI_SUR_8 >> 1), temp , tmp[5]); + SBC_IDCT_MULT((SBC_COS_PI_SUR_8 >> 1), temp , tmp[4]); + tmp[6] = tmp[2] + tmp[5]; + tmp[7] = tmp[3] - tmp[4]; + pOutVect[0] = (tmp[0] + tmp[6]); + pOutVect[1] = (tmp[1] + tmp[7]); + pOutVect[2] = (tmp[1] - tmp[7]); + pOutVect[3] = (tmp[0] - tmp[6]); +#else + UINT8 Index, k; + SINT32 temp; + /*Calculate 4 subband samples by matrixing*/ + for (Index = 0; Index < 4; Index++) { + temp = 0; + for (k = 0; k < 8; k++) { + /*temp += (SINT32)(((SINT64)M[(Index*strEncParams->numOfSubBands*2)+k] * Y[k]) >> 16 ); */ + temp += (gas16AnalDCTcoeff4[(Index * 4 * 2) + k] * (pInVect[k] >> 16)); + temp += ((gas16AnalDCTcoeff4[(Index * 4 * 2) + k] * (pInVect[k] & 0xFFFF)) >> 16); + } + pOutVect[Index] = temp; + } +#endif +} + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_dct.h b/components/ble/ble_stack/sbc/enc/sbc_dct.h new file mode 100644 index 00000000..165a8c1c --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_dct.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Definitions for the fast DCT. + * + ******************************************************************************/ + +#ifndef SBC_DCT_H +#define SBC_DCT_H + +#if (SBC_ARM_ASM_OPT==TRUE) +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1, s32OutLow) \ +{ \ + __asm \ +{ \ + MUL s32OutLow,(SINT32)s16In2, (s32In1>>15) \ +} \ +} +#else +#if (SBC_DSP_OPT==TRUE) +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow = SBC_Multiply_32_16_Simplified((SINT32)s16In2,s32In1); +#else +#if (SBC_IPAQ_OPT==TRUE) +/*#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow=(SINT32)((SINT32)(s16In2)*(SINT32)(s32In1>>15)); */ +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) s32OutLow=(SINT32)(((SINT64)s16In2*(SINT64)s32In1)>>15); +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) +#define SBC_MULT_32_32(s32In2, s32In1, s32OutLow) \ +{ \ + s64Temp = ((SINT64) s32In2) * ((SINT64) s32In1)>>31; \ + s32OutLow = (SINT32) s64Temp; \ +} +#endif +#else +#define SBC_MULT_32_16_SIMPLIFIED(s16In2, s32In1 , s32OutLow) \ +{ \ + s32In1Temp = s32In1; \ + s32In2Temp = (SINT32)s16In2; \ + \ + /* Multiply one +ve and the other -ve number */ \ + if (s32In1Temp < 0) \ + { \ + s32In1Temp ^= 0xFFFFFFFF; \ + s32In1Temp++; \ + s32OutLow = (s32In2Temp * (s32In1Temp >> 16)); \ + s32OutLow += (( s32In2Temp * (s32In1Temp & 0xFFFF)) >> 16); \ + s32OutLow ^= 0xFFFFFFFF; \ + s32OutLow++; \ + } \ + else \ + { \ + s32OutLow = (s32In2Temp * (s32In1Temp >> 16)); \ + s32OutLow += (( s32In2Temp * (s32In1Temp & 0xFFFF)) >> 16); \ + } \ + s32OutLow <<= 1; \ +} +#if (SBC_IS_64_MULT_IN_IDCT == TRUE) +#define SBC_MULT_64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{\ + s32OutLow=(SINT32)(((SINT64)s32In1*(SINT64)s32In2)& 0x00000000FFFFFFFF);\ + s32OutHi=(SINT32)(((SINT64)s32In1*(SINT64)s32In2)>>32);\ +} +#define SBC_MULT_32_32(s32In2, s32In1, s32OutLow) \ +{ \ + s32HiTemp = 0; \ + SBC_MULT_64(s32In2,s32In1 , s32OutLow, s32HiTemp); \ + s32OutLow = (((s32OutLow>>15)&0x1FFFF) | (s32HiTemp << 17)); \ +} +#endif + +#endif +#endif +#endif + +#endif diff --git a/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c b/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c new file mode 100644 index 00000000..800a722a --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * 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 coefficient table used for DCT computation in + * analysis. + * + ******************************************************************************/ + +#include "sbc_encoder.h" + +#if defined(SBC_ENC_INCLUDED) + +/*DCT coeff for 4 sub-band case.*/ +#if (SBC_FAST_DCT == FALSE) +const SINT16 gas16AnalDCTcoeff4[] = { + (SINT16)(0.7071 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.9239 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.3827 * 32768), + + (SINT16)(-0.7071 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.9239 * 32768), + + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.9239 * 32768), + + (SINT16)(0.7071 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.3827 * 32768) +}; + +/*DCT coeff for 8 sub-band case.*/ +const SINT16 gas16AnalDCTcoeff8[] = { + (SINT16)(0.7071 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.9808 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.8315 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(0.0000 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.5556 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.8315 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.7071 * 32768), + (SINT16)(0.9808 * 32768), + (SINT16)(-0.9239 * 32768), + (SINT16)(0.5556 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.9808 * 32768), + (SINT16)(1.0000 * 32767), + (SINT16)(-0.9808 * 32768), + (SINT16)(0.9239 * 32768), + (SINT16)(-0.8315 * 32768), + (SINT16)(0.7071 * 32768), + (SINT16)(-0.5556 * 32768), + (SINT16)(0.3827 * 32768), + (SINT16)(-0.1951 * 32768), + (SINT16)(-0.0000 * 32768), + (SINT16)(0.1951 * 32768), + (SINT16)(-0.3827 * 32768), + (SINT16)(0.5556 * 32768) +}; +#endif + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c b/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c new file mode 100644 index 00000000..0a776857 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c @@ -0,0 +1,188 @@ +/****************************************************************************** + * + * 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 code for bit allocation algorithm. It calculates + * the number of bits required for the encoded stream of data. + * + ******************************************************************************/ + +/*Includes*/ +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if defined(SBC_ENC_INCLUDED) + +/*global arrays*/ +const SINT16 sbc_enc_as16Offset4[4][4] = { { -1, 0, 0, 0}, { -2, 0, 0, 1}, + { -2, 0, 0, 1}, { -2, 0, 0, 1} +}; +const SINT16 sbc_enc_as16Offset8[4][8] = { { -2, 0, 0, 0, 0, 0, 0, 1}, + { -3, 0, 0, 0, 0, 0, 1, 2}, + { -4, 0, 0, 0, 0, 0, 1, 2}, + { -4, 0, 0, 0, 0, 0, 1, 2} +}; + +/**************************************************************************** +* BitAlloc - Calculates the required number of bits for the given scale factor +* and the number of subbands. +* +* RETURNS : N/A +*/ + +void sbc_enc_bit_alloc_mono(SBC_ENC_PARAMS *pstrCodecParams) +{ + SINT32 s32MaxBitNeed; /*to store the max bits needed per sb*/ + SINT32 s32BitCount; /*the used number of bits*/ + SINT32 s32SliceCount; /*to store hwo many slices can be put in bitpool*/ + SINT32 s32BitSlice; /*number of bitslices in bitpool*/ + SINT32 s32Sb; /*counter for sub-band*/ + SINT32 s32Ch; /*counter for channel*/ + SINT16 *ps16BitNeed; /*temp memory to store required number of bits*/ + SINT32 s32Loudness; /*used in Loudness calculation*/ + SINT16 *ps16GenBufPtr; + SINT16 *ps16GenArrPtr; + SINT16 *ps16GenTabPtr; + SINT32 s32NumOfSubBands = pstrCodecParams->s16NumOfSubBands; + + ps16BitNeed = pstrCodecParams->s16ScartchMemForBitAlloc; + + for (s32Ch = 0; s32Ch < pstrCodecParams->s16NumOfChannels; s32Ch++) { + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * SBC_MAX_NUM_OF_SUBBANDS; + + /* bitneed values are derived from scale factor */ + if (pstrCodecParams->s16AllocationMethod == SBC_SNR) { + ps16BitNeed = pstrCodecParams->as16ScaleFactor; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + } else { + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + if (s32NumOfSubBands == 4) { + ps16GenTabPtr = (SINT16 *) + sbc_enc_as16Offset4[pstrCodecParams->s16SamplingFreq]; + } else { + ps16GenTabPtr = (SINT16 *) + sbc_enc_as16Offset8[pstrCodecParams->s16SamplingFreq]; + } + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (pstrCodecParams->as16ScaleFactor[s32Ch * s32NumOfSubBands + s32Sb] == 0) { + *(ps16GenBufPtr) = -5; + } else { + s32Loudness = + (SINT32)(pstrCodecParams->as16ScaleFactor[s32Ch * s32NumOfSubBands + s32Sb] + - *ps16GenTabPtr); + if (s32Loudness > 0) { + *(ps16GenBufPtr) = (SINT16)(s32Loudness >> 1); + } else { + *(ps16GenBufPtr) = (SINT16)s32Loudness; + } + } + ps16GenBufPtr++; + ps16GenTabPtr++; + } + + } + + /* max bitneed index is searched*/ + s32MaxBitNeed = 0; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if ( *(ps16GenBufPtr) > s32MaxBitNeed) { + s32MaxBitNeed = *(ps16GenBufPtr); + } + + ps16GenBufPtr++; + } + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + /*iterative process to find hwo many bitslices fit into the bitpool*/ + s32BitSlice = s32MaxBitNeed + 1; + s32BitCount = pstrCodecParams->s16BitPool; + s32SliceCount = 0; + do { + s32BitSlice --; + s32BitCount -= s32SliceCount; + s32SliceCount = 0; + + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if ( (((*ps16GenBufPtr - s32BitSlice) < 16) && (*ps16GenBufPtr - s32BitSlice) >= 1)) { + if ((*ps16GenBufPtr - s32BitSlice) == 1) { + s32SliceCount += 2; + } else { + s32SliceCount++; + } + } + ps16GenBufPtr++; + + }/*end of for*/ + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + } while (s32BitCount - s32SliceCount > 0); + + if (s32BitCount == 0) { + s32BitCount -= s32SliceCount; + s32BitSlice --; + } + + /*Bits are distributed until the last bitslice is reached*/ + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*(ps16GenBufPtr) < s32BitSlice + 2) { + *(ps16GenArrPtr) = 0; + } else { + *(ps16GenArrPtr) = ((*(ps16GenBufPtr) - s32BitSlice) < 16) ? + (SINT16)(*(ps16GenBufPtr) - s32BitSlice) : 16; + } + + ps16GenBufPtr++; + ps16GenArrPtr++; + } + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + ps16GenBufPtr = ps16BitNeed + s32Ch * s32NumOfSubBands; + /*the remaining bits are allocated starting at subband 0*/ + s32Sb = 0; + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( (*(ps16GenArrPtr) >= 2) && (*(ps16GenArrPtr) < 16) ) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } else if ( (*(ps16GenBufPtr) == s32BitSlice + 1) && + (s32BitCount > 1) ) { + *(ps16GenArrPtr) = 2; + s32BitCount -= 2; + } + s32Sb++; + ps16GenArrPtr++; + ps16GenBufPtr++; + } + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Ch * s32NumOfSubBands; + + + s32Sb = 0; + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( *(ps16GenArrPtr) < 16) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } + s32Sb++; + ps16GenArrPtr++; + } + } +} +/*End of BitAlloc() function*/ + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c b/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c new file mode 100644 index 00000000..e0637362 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c @@ -0,0 +1,192 @@ +/****************************************************************************** + * + * 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 code for bit allocation algorithm. It calculates + * the number of bits required for the encoded stream of data. + * + ******************************************************************************/ + +/*Includes*/ +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if defined(SBC_ENC_INCLUDED) + +/*global arrays*/ +extern const SINT16 sbc_enc_as16Offset4[4][4]; +extern const SINT16 sbc_enc_as16Offset8[4][8]; + +/**************************************************************************** +* BitAlloc - Calculates the required number of bits for the given scale factor +* and the number of subbands. +* +* RETURNS : N/A +*/ + +void sbc_enc_bit_alloc_ste(SBC_ENC_PARAMS *pstrCodecParams) +{ + /* CAUTIOM -> mips optim for arm 32 require to use SINT32 instead of SINT16 */ + /* Do not change variable type or name */ + SINT32 s32MaxBitNeed; /*to store the max bits needed per sb*/ + SINT32 s32BitCount; /*the used number of bits*/ + SINT32 s32SliceCount; /*to store hwo many slices can be put in bitpool*/ + SINT32 s32BitSlice; /*number of bitslices in bitpool*/ + SINT32 s32Sb; /*counter for sub-band*/ + SINT32 s32Ch; /*counter for channel*/ + SINT16 *ps16BitNeed; /*temp memory to store required number of bits*/ + SINT32 s32Loudness; /*used in Loudness calculation*/ + SINT16 *ps16GenBufPtr, *pas16ScaleFactor; + SINT16 *ps16GenArrPtr; + SINT16 *ps16GenTabPtr; + SINT32 s32NumOfSubBands = pstrCodecParams->s16NumOfSubBands; + SINT32 s32BitPool = pstrCodecParams->s16BitPool; + + /* bitneed values are derived from scale factor */ + if (pstrCodecParams->s16AllocationMethod == SBC_SNR) { + ps16BitNeed = pstrCodecParams->as16ScaleFactor; + s32MaxBitNeed = pstrCodecParams->s16MaxBitNeed; + } else { + ps16BitNeed = pstrCodecParams->s16ScartchMemForBitAlloc; + pas16ScaleFactor = pstrCodecParams->as16ScaleFactor; + s32MaxBitNeed = 0; + ps16GenBufPtr = ps16BitNeed; + for (s32Ch = 0; s32Ch < 2; s32Ch++) { + if (s32NumOfSubBands == 4) { + ps16GenTabPtr = (SINT16 *)sbc_enc_as16Offset4[pstrCodecParams->s16SamplingFreq]; + } else { + ps16GenTabPtr = (SINT16 *)sbc_enc_as16Offset8[pstrCodecParams->s16SamplingFreq]; + } + + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*pas16ScaleFactor == 0) { + *ps16GenBufPtr = -5; + } else { + s32Loudness = (SINT32)(*pas16ScaleFactor - *ps16GenTabPtr); + + if (s32Loudness > 0) { + *ps16GenBufPtr = (SINT16)(s32Loudness >> 1); + } else { + *ps16GenBufPtr = (SINT16)s32Loudness; + } + } + + if (*ps16GenBufPtr > s32MaxBitNeed) { + s32MaxBitNeed = *ps16GenBufPtr; + } + pas16ScaleFactor++; + ps16GenBufPtr++; + ps16GenTabPtr++; + } + } + } + + /* iterative process to find out hwo many bitslices fit into the bitpool */ + s32BitSlice = s32MaxBitNeed + 1; + s32BitCount = s32BitPool; + s32SliceCount = 0; + do { + s32BitSlice --; + s32BitCount -= s32SliceCount; + s32SliceCount = 0; + ps16GenBufPtr = ps16BitNeed; + + for (s32Sb = 0; s32Sb < 2 * s32NumOfSubBands; s32Sb++) { + if ( (*ps16GenBufPtr >= s32BitSlice + 1) && (*ps16GenBufPtr < s32BitSlice + 16) ) { + if (*(ps16GenBufPtr) == s32BitSlice + 1) { + s32SliceCount += 2; + } else { + s32SliceCount++; + } + } + ps16GenBufPtr++; + } + } while (s32BitCount - s32SliceCount > 0); + + if (s32BitCount - s32SliceCount == 0) { + s32BitCount -= s32SliceCount; + s32BitSlice --; + } + + /* Bits are distributed until the last bitslice is reached */ + ps16GenBufPtr = ps16BitNeed; + ps16GenArrPtr = pstrCodecParams->as16Bits; + for (s32Ch = 0; s32Ch < 2; s32Ch++) { + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + if (*ps16GenBufPtr < s32BitSlice + 2) { + *ps16GenArrPtr = 0; + } else { + *ps16GenArrPtr = ((*(ps16GenBufPtr) - s32BitSlice) < 16) ? + (SINT16)(*(ps16GenBufPtr) - s32BitSlice) : 16; + } + ps16GenBufPtr++; + ps16GenArrPtr++; + } + } + + /* the remaining bits are allocated starting at subband 0 */ + s32Ch = 0; + s32Sb = 0; + ps16GenBufPtr = ps16BitNeed; + ps16GenArrPtr -= 2 * s32NumOfSubBands; + + while ( (s32BitCount > 0) && (s32Sb < s32NumOfSubBands) ) { + if ( (*(ps16GenArrPtr) >= 2) && (*(ps16GenArrPtr) < 16) ) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } else if ((*ps16GenBufPtr == s32BitSlice + 1) && (s32BitCount > 1)) { + *(ps16GenArrPtr) = 2; + s32BitCount -= 2; + } + if (s32Ch == 1) { + s32Ch = 0; + s32Sb++; + ps16GenBufPtr = ps16BitNeed + s32Sb; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Sb; + + } else { + s32Ch = 1; + ps16GenBufPtr = ps16BitNeed + s32NumOfSubBands + s32Sb; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32NumOfSubBands + s32Sb; + } + } + + s32Ch = 0; + s32Sb = 0; + ps16GenArrPtr = pstrCodecParams->as16Bits; + + while ((s32BitCount > 0) && (s32Sb < s32NumOfSubBands)) { + if (*(ps16GenArrPtr) < 16) { + (*(ps16GenArrPtr))++; + s32BitCount--; + } + if (s32Ch == 1) { + s32Ch = 0; + s32Sb++; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32Sb; + } else { + s32Ch = 1; + ps16GenArrPtr = pstrCodecParams->as16Bits + s32NumOfSubBands + s32Sb; + } + } +} + +/*End of BitAlloc() function*/ + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c b/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c new file mode 100644 index 00000000..c7bdacec --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c @@ -0,0 +1,318 @@ +/****************************************************************************** + * + * 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 Windowing coeffs for synthesis filter + * + ******************************************************************************/ + +#include "sbc_encoder.h" + +#if defined(SBC_ENC_INCLUDED) + +#if (SBC_ARM_ASM_OPT==FALSE && SBC_IPAQ_OPT==FALSE) +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE) +/*Window coeff for 4 sub band case*/ +const SINT16 gas32CoeffFor4SBs[] = { + (SINT16)((SINT32)0x00000000 >> 16), (SINT16)0x00000000, + (SINT16)((SINT32)0x001194E6 >> 16), (SINT16)0x001194E6, + (SINT16)((SINT32)0x0030E2D3 >> 16), (SINT16)0x0030E2D3, + (SINT16)((SINT32)0x00599403 >> 16), (SINT16)0x00599403, + (SINT16)((SINT32)0x007DBCC8 >> 16), (SINT16)0x007DBCC8, + (SINT16)((SINT32)0x007F88E4 >> 16), (SINT16)0x007F88E4, + (SINT16)((SINT32)0x003D239B >> 16), (SINT16)0x003D239B, + (SINT16)((SINT32)0xFF9BB9D5 >> 16), (SINT16)0xFF9BB9D5, + + (SINT16)((SINT32)0x01659F45 >> 16), (SINT16)0x01659F45, + (SINT16)((SINT32)0x029DBAA3 >> 16), (SINT16)0x029DBAA3, + (SINT16)((SINT32)0x03B23341 >> 16), (SINT16)0x03B23341, + (SINT16)((SINT32)0x041EEE40 >> 16), (SINT16)0x041EEE40, + (SINT16)((SINT32)0x034FEE2C >> 16), (SINT16)0x034FEE2C, + (SINT16)((SINT32)0x00C8F2BC >> 16), (SINT16)0x00C8F2BC, + (SINT16)((SINT32)0xFC4F91D4 >> 16), (SINT16)0xFC4F91D4, + (SINT16)((SINT32)0xF60FAF37 >> 16), (SINT16)0xF60FAF37, + + (SINT16)((SINT32)0x115B1ED2 >> 16), (SINT16)0x115B1ED2, + (SINT16)((SINT32)0x18F55C90 >> 16), (SINT16)0x18F55C90, + (SINT16)((SINT32)0x1F91CA46 >> 16), (SINT16)0x1F91CA46, + (SINT16)((SINT32)0x2412F251 >> 16), (SINT16)0x2412F251, + (SINT16)((SINT32)0x25AC1FF2 >> 16), (SINT16)0x25AC1FF2, + (SINT16)((SINT32)0x2412F251 >> 16), (SINT16)0x2412F251, + (SINT16)((SINT32)0x1F91CA46 >> 16), (SINT16)0x1F91CA46, + (SINT16)((SINT32)0x18F55C90 >> 16), (SINT16)0x18F55C90, + + (SINT16)((SINT32)0xEEA4E12E >> 16), (SINT16)0xEEA4E12E, + (SINT16)((SINT32)0xF60FAF37 >> 16), (SINT16)0xF60FAF37, + (SINT16)((SINT32)0xFC4F91D4 >> 16), (SINT16)0xFC4F91D4, + (SINT16)((SINT32)0x00C8F2BC >> 16), (SINT16)0x00C8F2BC, + (SINT16)((SINT32)0x034FEE2C >> 16), (SINT16)0x034FEE2C, + (SINT16)((SINT32)0x041EEE40 >> 16), (SINT16)0x041EEE40, + (SINT16)((SINT32)0x03B23341 >> 16), (SINT16)0x03B23341, + (SINT16)((SINT32)0x029DBAA3 >> 16), (SINT16)0x029DBAA3, + + (SINT16)((SINT32)0xFE9A60BB >> 16), (SINT16)0xFE9A60BB, + (SINT16)((SINT32)0xFF9BB9D5 >> 16), (SINT16)0xFF9BB9D5, + (SINT16)((SINT32)0x003D239B >> 16), (SINT16)0x003D239B, + (SINT16)((SINT32)0x007F88E4 >> 16), (SINT16)0x007F88E4, + (SINT16)((SINT32)0x007DBCC8 >> 16), (SINT16)0x007DBCC8, + (SINT16)((SINT32)0x00599403 >> 16), (SINT16)0x00599403, + (SINT16)((SINT32)0x0030E2D3 >> 16), (SINT16)0x0030E2D3, + (SINT16)((SINT32)0x001194E6 >> 16), (SINT16)0x001194E6 +}; + +/*Window coeff for 8 sub band case*/ +const SINT16 gas32CoeffFor8SBs[] = { + (SINT16)((SINT32)0x00000000 >> 16), (SINT16)0x00000000, + (SINT16)((SINT32)0x00052173 >> 16), (SINT16)0x00052173, + (SINT16)((SINT32)0x000B3F71 >> 16), (SINT16)0x000B3F71, + (SINT16)((SINT32)0x00122C7D >> 16), (SINT16)0x00122C7D, + (SINT16)((SINT32)0x001AFF89 >> 16), (SINT16)0x001AFF89, + (SINT16)((SINT32)0x00255A62 >> 16), (SINT16)0x00255A62, + (SINT16)((SINT32)0x003060F4 >> 16), (SINT16)0x003060F4, + (SINT16)((SINT32)0x003A72E7 >> 16), (SINT16)0x003A72E7, + + (SINT16)((SINT32)0x0041EC6A >> 16), (SINT16)0x0041EC6A, /* 8 */ + (SINT16)((SINT32)0x0044EF48 >> 16), (SINT16)0x0044EF48, + (SINT16)((SINT32)0x00415B75 >> 16), (SINT16)0x00415B75, + (SINT16)((SINT32)0x0034F8B6 >> 16), (SINT16)0x0034F8B6, + (SINT16)((SINT32)0x001D8FD2 >> 16), (SINT16)0x001D8FD2, + (SINT16)((SINT32)0xFFFA2413 >> 16), (SINT16)0xFFFA2413, + (SINT16)((SINT32)0xFFC9F10E >> 16), (SINT16)0xFFC9F10E, + (SINT16)((SINT32)0xFF8D6793 >> 16), (SINT16)0xFF8D6793, + + (SINT16)((SINT32)0x00B97348 >> 16), (SINT16)0x00B97348, /* 16 */ + (SINT16)((SINT32)0x01071B96 >> 16), (SINT16)0x01071B96, + (SINT16)((SINT32)0x0156B3CA >> 16), (SINT16)0x0156B3CA, + (SINT16)((SINT32)0x01A1B38B >> 16), (SINT16)0x01A1B38B, + (SINT16)((SINT32)0x01E0224C >> 16), (SINT16)0x01E0224C, + (SINT16)((SINT32)0x0209291F >> 16), (SINT16)0x0209291F, + (SINT16)((SINT32)0x02138653 >> 16), (SINT16)0x02138653, + (SINT16)((SINT32)0x01F5F424 >> 16), (SINT16)0x01F5F424, + + (SINT16)((SINT32)0x01A7ECEF >> 16), (SINT16)0x01A7ECEF, /* 24 */ + (SINT16)((SINT32)0x01223EBA >> 16), (SINT16)0x01223EBA, + (SINT16)((SINT32)0x005FD0FF >> 16), (SINT16)0x005FD0FF, + (SINT16)((SINT32)0xFF5EEB73 >> 16), (SINT16)0xFF5EEB73, + (SINT16)((SINT32)0xFE20435D >> 16), (SINT16)0xFE20435D, + (SINT16)((SINT32)0xFCA86E7E >> 16), (SINT16)0xFCA86E7E, + (SINT16)((SINT32)0xFAFF95FC >> 16), (SINT16)0xFAFF95FC, + (SINT16)((SINT32)0xF9312891 >> 16), (SINT16)0xF9312891, + + (SINT16)((SINT32)0x08B4307A >> 16), (SINT16)0x08B4307A, /* 32 */ + (SINT16)((SINT32)0x0A9F3E9A >> 16), (SINT16)0x0A9F3E9A, + (SINT16)((SINT32)0x0C7D59B6 >> 16), (SINT16)0x0C7D59B6, + (SINT16)((SINT32)0x0E3BB16F >> 16), (SINT16)0x0E3BB16F, + (SINT16)((SINT32)0x0FC721F9 >> 16), (SINT16)0x0FC721F9, + (SINT16)((SINT32)0x110ECEF0 >> 16), (SINT16)0x110ECEF0, + (SINT16)((SINT32)0x120435FA >> 16), (SINT16)0x120435FA, + (SINT16)((SINT32)0x129C226F >> 16), (SINT16)0x129C226F, + + (SINT16)((SINT32)0x12CF6C75 >> 16), (SINT16)0x12CF6C75, /* 40 */ + (SINT16)((SINT32)0x129C226F >> 16), (SINT16)0x129C226F, + (SINT16)((SINT32)0x120435FA >> 16), (SINT16)0x120435FA, + (SINT16)((SINT32)0x110ECEF0 >> 16), (SINT16)0x110ECEF0, + (SINT16)((SINT32)0x0FC721F9 >> 16), (SINT16)0x0FC721F9, + (SINT16)((SINT32)0x0E3BB16F >> 16), (SINT16)0x0E3BB16F, + (SINT16)((SINT32)0x0C7D59B6 >> 16), (SINT16)0x0C7D59B6, + (SINT16)((SINT32)0x0A9F3E9A >> 16), (SINT16)0x0A9F3E9A, + + (SINT16)((SINT32)0xF74BCF86 >> 16), (SINT16)0xF74BCF86, /* 48 */ + (SINT16)((SINT32)0xF9312891 >> 16), (SINT16)0xF9312891, + (SINT16)((SINT32)0xFAFF95FC >> 16), (SINT16)0xFAFF95FC, + (SINT16)((SINT32)0xFCA86E7E >> 16), (SINT16)0xFCA86E7E, + (SINT16)((SINT32)0xFE20435D >> 16), (SINT16)0xFE20435D, + (SINT16)((SINT32)0xFF5EEB73 >> 16), (SINT16)0xFF5EEB73, + (SINT16)((SINT32)0x005FD0FF >> 16), (SINT16)0x005FD0FF, + (SINT16)((SINT32)0x01223EBA >> 16), (SINT16)0x01223EBA, + + (SINT16)((SINT32)0x01A7ECEF >> 16), (SINT16)0x01A7ECEF, /* 56 */ + (SINT16)((SINT32)0x01F5F424 >> 16), (SINT16)0x01F5F424, + (SINT16)((SINT32)0x02138653 >> 16), (SINT16)0x02138653, + (SINT16)((SINT32)0x0209291F >> 16), (SINT16)0x0209291F, + (SINT16)((SINT32)0x01E0224C >> 16), (SINT16)0x01E0224C, + (SINT16)((SINT32)0x01A1B38B >> 16), (SINT16)0x01A1B38B, + (SINT16)((SINT32)0x0156B3CA >> 16), (SINT16)0x0156B3CA, + (SINT16)((SINT32)0x01071B96 >> 16), (SINT16)0x01071B96, + + (SINT16)((SINT32)0xFF468CB8 >> 16), (SINT16)0xFF468CB8, /* 64 */ + (SINT16)((SINT32)0xFF8D6793 >> 16), (SINT16)0xFF8D6793, + (SINT16)((SINT32)0xFFC9F10E >> 16), (SINT16)0xFFC9F10E, + (SINT16)((SINT32)0xFFFA2413 >> 16), (SINT16)0xFFFA2413, + (SINT16)((SINT32)0x001D8FD2 >> 16), (SINT16)0x001D8FD2, + (SINT16)((SINT32)0x0034F8B6 >> 16), (SINT16)0x0034F8B6, + (SINT16)((SINT32)0x00415B75 >> 16), (SINT16)0x00415B75, + (SINT16)((SINT32)0x0044EF48 >> 16), (SINT16)0x0044EF48, + + (SINT16)((SINT32)0x0041EC6A >> 16), (SINT16)0x0041EC6A, /* 72 */ + (SINT16)((SINT32)0x003A72E7 >> 16), (SINT16)0x003A72E7, + (SINT16)((SINT32)0x003060F4 >> 16), (SINT16)0x003060F4, + (SINT16)((SINT32)0x00255A62 >> 16), (SINT16)0x00255A62, + (SINT16)((SINT32)0x001AFF89 >> 16), (SINT16)0x001AFF89, + (SINT16)((SINT32)0x00122C7D >> 16), (SINT16)0x00122C7D, + (SINT16)((SINT32)0x000B3F71 >> 16), (SINT16)0x000B3F71, + (SINT16)((SINT32)0x00052173 >> 16), (SINT16)0x00052173 +}; + +#else + +/*Window coeff for 4 sub band case*/ +const SINT32 gas32CoeffFor4SBs[] = { + (SINT32)0x00000000, + (SINT32)0x001194E6, + (SINT32)0x0030E2D3, + (SINT32)0x00599403, + (SINT32)0x007DBCC8, + (SINT32)0x007F88E4, + (SINT32)0x003D239B, + (SINT32)0xFF9BB9D5, + + (SINT32)0x01659F45, + (SINT32)0x029DBAA3, + (SINT32)0x03B23341, + (SINT32)0x041EEE40, + (SINT32)0x034FEE2C, + (SINT32)0x00C8F2BC, + (SINT32)0xFC4F91D4, + (SINT32)0xF60FAF37, + + (SINT32)0x115B1ED2, + (SINT32)0x18F55C90, + (SINT32)0x1F91CA46, + (SINT32)0x2412F251, + (SINT32)0x25AC1FF2, + (SINT32)0x2412F251, + (SINT32)0x1F91CA46, + (SINT32)0x18F55C90, + + (SINT32)0xEEA4E12E, + (SINT32)0xF60FAF37, + (SINT32)0xFC4F91D4, + (SINT32)0x00C8F2BC, + (SINT32)0x034FEE2C, + (SINT32)0x041EEE40, + (SINT32)0x03B23341, + (SINT32)0x029DBAA3, + + (SINT32)0xFE9A60BB, + (SINT32)0xFF9BB9D5, + (SINT32)0x003D239B, + (SINT32)0x007F88E4, + (SINT32)0x007DBCC8, + (SINT32)0x00599403, + (SINT32)0x0030E2D3, + (SINT32)0x001194E6 +}; + +/*Window coeff for 8 sub band case*/ +const SINT32 gas32CoeffFor8SBs[] = { + (SINT32)0x00000000, + (SINT32)0x00052173, + (SINT32)0x000B3F71, + (SINT32)0x00122C7D, + (SINT32)0x001AFF89, + (SINT32)0x00255A62, + (SINT32)0x003060F4, + (SINT32)0x003A72E7, + + (SINT32)0x0041EC6A, /* 8 */ + (SINT32)0x0044EF48, + (SINT32)0x00415B75, + (SINT32)0x0034F8B6, + (SINT32)0x001D8FD2, + (SINT32)0xFFFA2413, + (SINT32)0xFFC9F10E, + (SINT32)0xFF8D6793, + + (SINT32)0x00B97348, /* 16 */ + (SINT32)0x01071B96, + (SINT32)0x0156B3CA, + (SINT32)0x01A1B38B, + (SINT32)0x01E0224C, + (SINT32)0x0209291F, + (SINT32)0x02138653, + (SINT32)0x01F5F424, + + (SINT32)0x01A7ECEF, /* 24 */ + (SINT32)0x01223EBA, + (SINT32)0x005FD0FF, + (SINT32)0xFF5EEB73, + (SINT32)0xFE20435D, + (SINT32)0xFCA86E7E, + (SINT32)0xFAFF95FC, + (SINT32)0xF9312891, + + (SINT32)0x08B4307A, /* 32 */ + (SINT32)0x0A9F3E9A, + (SINT32)0x0C7D59B6, + (SINT32)0x0E3BB16F, + (SINT32)0x0FC721F9, + (SINT32)0x110ECEF0, + (SINT32)0x120435FA, + (SINT32)0x129C226F, + + (SINT32)0x12CF6C75, /* 40 */ + (SINT32)0x129C226F, + (SINT32)0x120435FA, + (SINT32)0x110ECEF0, + (SINT32)0x0FC721F9, + (SINT32)0x0E3BB16F, + (SINT32)0x0C7D59B6, + (SINT32)0x0A9F3E9A, + + (SINT32)0xF74BCF86, /* 48 */ + (SINT32)0xF9312891, + (SINT32)0xFAFF95FC, + (SINT32)0xFCA86E7E, + (SINT32)0xFE20435D, + (SINT32)0xFF5EEB73, + (SINT32)0x005FD0FF, + (SINT32)0x01223EBA, + + (SINT32)0x01A7ECEF, /* 56 */ + (SINT32)0x01F5F424, + (SINT32)0x02138653, + (SINT32)0x0209291F, + (SINT32)0x01E0224C, + (SINT32)0x01A1B38B, + (SINT32)0x0156B3CA, + (SINT32)0x01071B96, + + (SINT32)0xFF468CB8, /* 64 */ + (SINT32)0xFF8D6793, + (SINT32)0xFFC9F10E, + (SINT32)0xFFFA2413, + (SINT32)0x001D8FD2, + (SINT32)0x0034F8B6, + (SINT32)0x00415B75, + (SINT32)0x0044EF48, + + (SINT32)0x0041EC6A, /* 72 */ + (SINT32)0x003A72E7, + (SINT32)0x003060F4, + (SINT32)0x00255A62, + (SINT32)0x001AFF89, + (SINT32)0x00122C7D, + (SINT32)0x000B3F71, + (SINT32)0x00052173 +}; + +#endif +#endif + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h b/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h new file mode 100644 index 00000000..b863dd89 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Function declarations. + * + ******************************************************************************/ + +#ifndef SBC_FUNCDECLARE_H +#define SBC_FUNCDECLARE_H + +/*#include "sbc_encoder.h"*/ +/* Global data */ +#if (SBC_IS_64_MULT_IN_WINDOW_ACCU == FALSE) +extern const SINT16 gas32CoeffFor4SBs[]; +extern const SINT16 gas32CoeffFor8SBs[]; +#else +extern const SINT32 gas32CoeffFor4SBs[]; +extern const SINT32 gas32CoeffFor8SBs[]; +#endif + +/* Global functions*/ + +extern void sbc_enc_bit_alloc_mono(SBC_ENC_PARAMS *CodecParams); +extern void sbc_enc_bit_alloc_ste(SBC_ENC_PARAMS *CodecParams); + +extern void SbcAnalysisInit (void); + +extern void SbcAnalysisFilter4(SBC_ENC_PARAMS *strEncParams); +extern void SbcAnalysisFilter8(SBC_ENC_PARAMS *strEncParams); + +extern void SBC_FastIDCT8 (SINT32 *pInVect, SINT32 *pOutVect); +extern void SBC_FastIDCT4 (SINT32 *x0, SINT32 *pOutVect); + +extern void EncPacking(SBC_ENC_PARAMS *strEncParams); +extern void EncQuantizer(SBC_ENC_PARAMS *); +#if (SBC_DSP_OPT==TRUE) +SINT32 SBC_Multiply_32_16_Simplified(SINT32 s32In2Temp, SINT32 s32In1Temp); +#endif +#endif diff --git a/components/ble/ble_stack/sbc/enc/sbc_encoder.c b/components/ble/ble_stack/sbc/enc/sbc_encoder.c new file mode 100644 index 00000000..8db716ae --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_encoder.c @@ -0,0 +1,332 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * contains code for encoder flow and initalization of encoder + * + ******************************************************************************/ + +#include +#include +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if defined(SBC_ENC_INCLUDED) + +SINT16 EncMaxShiftCounter; + +#if (SBC_JOINT_STE_INCLUDED == TRUE) +SINT32 s32LRDiff[SBC_MAX_NUM_OF_BLOCKS] = {0}; +SINT32 s32LRSum[SBC_MAX_NUM_OF_BLOCKS] = {0}; +#endif + +void SBC_Encoder(SBC_ENC_PARAMS *pstrEncParams) +{ + SINT32 s32Ch; /* counter for ch*/ + SINT32 s32Sb; /* counter for sub-band*/ + UINT32 u32Count, maxBit = 0; /* loop count*/ + SINT32 s32MaxValue; /* temp variable to store max value */ + + SINT16 *ps16ScfL; + SINT32 *SbBuffer; + SINT32 s32Blk; /* counter for block*/ + SINT32 s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; +#if (SBC_JOINT_STE_INCLUDED == TRUE) + SINT32 s32MaxValue2; + UINT32 u32CountSum, u32CountDiff; + SINT32 *pSum, *pDiff; +#endif + register SINT32 s32NumOfSubBands = pstrEncParams->s16NumOfSubBands; + + pstrEncParams->pu8NextPacket = pstrEncParams->pu8Packet; + +#if (SBC_NO_PCM_CPY_OPTION == TRUE) + pstrEncParams->ps16NextPcmBuffer = pstrEncParams->ps16PcmBuffer; +#else + pstrEncParams->ps16NextPcmBuffer = pstrEncParams->as16PcmBuffer; +#endif + do { + /* SBC ananlysis filter*/ + if (s32NumOfSubBands == 4) { + SbcAnalysisFilter4(pstrEncParams); + } else { + SbcAnalysisFilter8(pstrEncParams); + } + + /* compute the scale factor, and save the max */ + ps16ScfL = pstrEncParams->as16ScaleFactor; + s32Ch = pstrEncParams->s16NumOfChannels * s32NumOfSubBands; + + pstrEncParams->ps16NextPcmBuffer += s32Ch * s32NumOfBlocks; /* in case of multible sbc frame to encode update the pcm pointer */ + + for (s32Sb = 0; s32Sb < s32Ch; s32Sb++) { + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + s32MaxValue = 0; + for (s32Blk = s32NumOfBlocks; s32Blk > 0; s32Blk--) { + if (s32MaxValue < abs32(*SbBuffer)) { + s32MaxValue = abs32(*SbBuffer); + } + SbBuffer += s32Ch; + } + + u32Count = (s32MaxValue > 0x800000) ? 9 : 0; + + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + *ps16ScfL++ = (SINT16)u32Count; + + if (u32Count > maxBit) { + maxBit = u32Count; + } + } + /* In case of JS processing,check whether to use JS */ +#if (SBC_JOINT_STE_INCLUDED == TRUE) + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + /* Calculate sum and differance scale factors for making JS decision */ + ps16ScfL = pstrEncParams->as16ScaleFactor ; + /* calculate the scale factor of Joint stereo max sum and diff */ + for (s32Sb = 0; s32Sb < s32NumOfSubBands - 1; s32Sb++) { + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + s32MaxValue2 = 0; + s32MaxValue = 0; + pSum = s32LRSum; + pDiff = s32LRDiff; + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + *pSum = (*SbBuffer + * (SbBuffer + s32NumOfSubBands)) >> 1; + if (abs32(*pSum) > s32MaxValue) { + s32MaxValue = abs32(*pSum); + } + pSum++; + *pDiff = (*SbBuffer - * (SbBuffer + s32NumOfSubBands)) >> 1; + if (abs32(*pDiff) > s32MaxValue2) { + s32MaxValue2 = abs32(*pDiff); + } + pDiff++; + SbBuffer += s32Ch; + } + u32Count = (s32MaxValue > 0x800000) ? 9 : 0; + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + u32CountSum = u32Count; + u32Count = (s32MaxValue2 > 0x800000) ? 9 : 0; + for ( ; u32Count < 15; u32Count++) { + if (s32MaxValue2 <= (SINT32)(0x8000 << u32Count)) { + break; + } + } + u32CountDiff = u32Count; + if ( (*ps16ScfL + * (ps16ScfL + s32NumOfSubBands)) > (SINT16)(u32CountSum + u32CountDiff) ) { + + if (u32CountSum > maxBit) { + maxBit = u32CountSum; + } + + if (u32CountDiff > maxBit) { + maxBit = u32CountDiff; + } + + *ps16ScfL = (SINT16)u32CountSum; + *(ps16ScfL + s32NumOfSubBands) = (SINT16)u32CountDiff; + + SbBuffer = pstrEncParams->s32SbBuffer + s32Sb; + pSum = s32LRSum; + pDiff = s32LRDiff; + + for (s32Blk = 0; s32Blk < s32NumOfBlocks; s32Blk++) { + *SbBuffer = *pSum; + *(SbBuffer + s32NumOfSubBands) = *pDiff; + + SbBuffer += s32NumOfSubBands << 1; + pSum++; + pDiff++; + } + + pstrEncParams->as16Join[s32Sb] = 1; + } else { + pstrEncParams->as16Join[s32Sb] = 0; + } + ps16ScfL++; + } + pstrEncParams->as16Join[s32Sb] = 0; + } +#endif + + pstrEncParams->s16MaxBitNeed = (SINT16)maxBit; + + /* bit allocation */ + if ((pstrEncParams->s16ChannelMode == SBC_STEREO) || (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO)) { + sbc_enc_bit_alloc_ste(pstrEncParams); + } else { + sbc_enc_bit_alloc_mono(pstrEncParams); + } + + /* Quantize the encoded audio */ + EncPacking(pstrEncParams); + } while (--(pstrEncParams->u8NumPacketToEncode)); + + pstrEncParams->u8NumPacketToEncode = 1; /* default is one for retrocompatibility purpose */ + +} + +/**************************************************************************** +* InitSbcAnalysisFilt - Initalizes the input data to 0 +* +* RETURNS : N/A +*/ +void SBC_Encoder_Init(SBC_ENC_PARAMS *pstrEncParams) +{ + UINT16 s16SamplingFreq; /*temp variable to store smpling freq*/ + SINT16 s16Bitpool; /*to store bit pool value*/ + SINT16 s16BitRate; /*to store bitrate*/ + SINT16 s16FrameLen; /*to store frame length*/ + UINT16 HeaderParams; + + pstrEncParams->u8NumPacketToEncode = 1; /* default is one for retrocompatibility purpose */ + + if (pstrEncParams->sbc_mode != SBC_MODE_MSBC) { + /* Required number of channels */ + if (pstrEncParams->s16ChannelMode == SBC_MONO) { + pstrEncParams->s16NumOfChannels = 1; + } else { + pstrEncParams->s16NumOfChannels = 2; + } + + /* Bit pool calculation */ + if (pstrEncParams->s16SamplingFreq == SBC_sf16000) { + s16SamplingFreq = 16000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf32000) { + s16SamplingFreq = 32000; + } else if (pstrEncParams->s16SamplingFreq == SBC_sf44100) { + s16SamplingFreq = 44100; + } else { + s16SamplingFreq = 48000; + } + + if ( (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) + || (pstrEncParams->s16ChannelMode == SBC_STEREO) ) { + s16Bitpool = (SINT16)( (pstrEncParams->u16BitRate * + pstrEncParams->s16NumOfSubBands * 1000 / s16SamplingFreq) + - ( (32 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) + + ( (pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands ) ) + / pstrEncParams->s16NumOfBlocks) ); + + s16FrameLen = 4 + (4 * pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfChannels) / 8 + + ( ((pstrEncParams->s16ChannelMode - 2) * + pstrEncParams->s16NumOfSubBands) + + (pstrEncParams->s16NumOfBlocks * s16Bitpool) ) / 8; + + s16BitRate = (8 * s16FrameLen * s16SamplingFreq) + / (pstrEncParams->s16NumOfSubBands * + pstrEncParams->s16NumOfBlocks * 1000); + + if (s16BitRate > pstrEncParams->u16BitRate) { + s16Bitpool--; + } + + if (pstrEncParams->s16NumOfSubBands == 8) { + pstrEncParams->s16BitPool = (s16Bitpool > 255) ? 255 : s16Bitpool; + } else { + pstrEncParams->s16BitPool = (s16Bitpool > 128) ? 128 : s16Bitpool; + } + } else { + s16Bitpool = (SINT16)( ((pstrEncParams->s16NumOfSubBands * + pstrEncParams->u16BitRate * 1000) + / (s16SamplingFreq * pstrEncParams->s16NumOfChannels)) + - ( ( (32 / pstrEncParams->s16NumOfChannels) + + (4 * pstrEncParams->s16NumOfSubBands) ) + / pstrEncParams->s16NumOfBlocks ) ); + + pstrEncParams->s16BitPool = (s16Bitpool > + (16 * pstrEncParams->s16NumOfSubBands)) + ? (16 * pstrEncParams->s16NumOfSubBands) : s16Bitpool; + } + + if (pstrEncParams->s16BitPool < 0) { + pstrEncParams->s16BitPool = 0; + } + /* sampling freq */ + HeaderParams = ((pstrEncParams->s16SamplingFreq & 3) << 6); + + /* number of blocks*/ + HeaderParams |= (((pstrEncParams->s16NumOfBlocks - 4) & 12) << 2); + + /* channel mode: mono, dual...*/ + HeaderParams |= ((pstrEncParams->s16ChannelMode & 3) << 2); + + /* Loudness or SNR */ + HeaderParams |= ((pstrEncParams->s16AllocationMethod & 1) << 1); + HeaderParams |= ((pstrEncParams->s16NumOfSubBands >> 3) & 1); /*4 or 8*/ + + pstrEncParams->FrameHeader = HeaderParams; + } else { + // mSBC + + // Use mSBC encoding parameters to reset the control field + /* Required number of channels: 1 */ + pstrEncParams->s16ChannelMode = SBC_MONO; + pstrEncParams->s16NumOfChannels = 1; + + /* Required Sampling frequency : 16KHz */ + pstrEncParams->s16SamplingFreq = SBC_sf16000; + + /* Bit pool value: 26 */ + pstrEncParams->s16BitPool = 26; + + /* number of subbands: 8 */ + pstrEncParams->s16NumOfSubBands = 8; + + /* number of blocks: 15 */ + pstrEncParams->s16NumOfBlocks = 15; + + /* allocation method: loudness */ + pstrEncParams->s16AllocationMethod = SBC_LOUDNESS; + + /* set the header paramers, unused for mSBC */ + pstrEncParams->FrameHeader = 0; + } + + if (pstrEncParams->s16NumOfSubBands == 4) { + if (pstrEncParams->s16NumOfChannels == 1) { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 4 * 10) >> 2) << 2; + } else { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 4 * 10 * 2) >> 3) << 2; + } + } else { + if (pstrEncParams->s16NumOfChannels == 1) { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 8 * 10) >> 3) << 3; + } else { + EncMaxShiftCounter = ((ENC_VX_BUFFER_SIZE - 8 * 10 * 2) >> 4) << 3; + } + } + + BT_WARN("SBC_Encoder_Init : bitrate %d, bitpool %d\n", pstrEncParams->u16BitRate, pstrEncParams->s16BitPool); + + SbcAnalysisInit(); +} + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_encoder.h b/components/ble/ble_stack/sbc/enc/sbc_encoder.h new file mode 100644 index 00000000..b7b807f5 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_encoder.h @@ -0,0 +1,208 @@ +/****************************************************************************** + * + * 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 constants and structures used by Encoder. + * + ******************************************************************************/ + +#ifndef SBC_ENCODER_H +#define SBC_ENCODER_H + +#define ENCODER_VERSION "0025" + +#ifdef BUILDCFG +#include "common/bt_target.h" +#endif + +/*DEFINES*/ +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define SBC_MAX_NUM_OF_SUBBANDS 8 +#define SBC_MAX_NUM_OF_CHANNELS 2 +#define SBC_MAX_NUM_OF_BLOCKS 16 + +#define SBC_LOUDNESS 0 +#define SBC_SNR 1 + +#define SUB_BANDS_8 8 +#define SUB_BANDS_4 4 + +#define SBC_sf16000 0 +#define SBC_sf32000 1 +#define SBC_sf44100 2 +#define SBC_sf48000 3 + +#define SBC_MONO 0 +#define SBC_DUAL 1 +#define SBC_STEREO 2 +#define SBC_JOINT_STEREO 3 + +#define SBC_BLOCK_0 4 +#define SBC_BLOCK_1 8 +#define SBC_BLOCK_2 12 +#define SBC_BLOCK_3 16 + +#define SBC_NULL 0 + +#define SBC_MODE_STD 0 +#define SBC_MODE_MSBC 1 + +#define SBC_SYNC_WORD_STD (0x9C) +#define SBC_SYNC_WORD_MSBC (0xAD) + +#ifndef SBC_MAX_NUM_FRAME +#define SBC_MAX_NUM_FRAME 1 +#endif + +#ifndef SBC_DSP_OPT +#define SBC_DSP_OPT FALSE +#endif + +/* Set SBC_USE_ARM_PRAGMA to TRUE to use "#pragma arm section zidata" */ +#ifndef SBC_USE_ARM_PRAGMA +#define SBC_USE_ARM_PRAGMA FALSE +#endif + +/* Set SBC_ARM_ASM_OPT to TRUE in case the target is an ARM */ +/* this will replace all the 32 and 64 bit mult by in line assembly code */ +#ifndef SBC_ARM_ASM_OPT +#define SBC_ARM_ASM_OPT FALSE +#endif + +/* green hill compiler option -> Used to distinguish the syntax for inline assembly code*/ +#ifndef SBC_GHS_COMPILER +#define SBC_GHS_COMPILER FALSE +#endif + +/* ARM compiler option -> Used to distinguish the syntax for inline assembly code */ +#ifndef SBC_ARM_COMPILER +#define SBC_ARM_COMPILER TRUE +#endif + +/* Set SBC_IPAQ_OPT to TRUE in case the target is an ARM */ +/* 32 and 64 bit mult will be performed using SINT64 ( usualy __int64 ) cast that usualy give optimal performance if supported */ +#ifndef SBC_IPAQ_OPT +#define SBC_IPAQ_OPT TRUE +#endif + +/* Debug only: set SBC_IS_64_MULT_IN_WINDOW_ACCU to TRUE to use 64 bit multiplication in the windowing */ +/* -> not recomended, more MIPS for the same restitution. */ +#ifndef SBC_IS_64_MULT_IN_WINDOW_ACCU +#define SBC_IS_64_MULT_IN_WINDOW_ACCU FALSE +#endif /*SBC_IS_64_MULT_IN_WINDOW_ACCU */ + +/* Set SBC_IS_64_MULT_IN_IDCT to TRUE to use 64 bits multiplication in the DCT of Matrixing */ +/* -> more MIPS required for a better audio quality. comparasion with the SIG utilities shows a division by 10 of the RMS */ +/* CAUTION: It only apply in the if SBC_FAST_DCT is set to TRUE */ +#ifndef SBC_IS_64_MULT_IN_IDCT +#define SBC_IS_64_MULT_IN_IDCT FALSE +#endif /*SBC_IS_64_MULT_IN_IDCT */ + +/* set SBC_IS_64_MULT_IN_QUANTIZER to TRUE to use 64 bits multiplication in the quantizer */ +/* setting this flag to FALSE add whistling noise at 5.5 and 11 KHz usualy not perceptible by human's hears. */ +#ifndef SBC_IS_64_MULT_IN_QUANTIZER +#define SBC_IS_64_MULT_IN_QUANTIZER TRUE +#endif /*SBC_IS_64_MULT_IN_IDCT */ + +/* Debug only: set this flag to FALSE to disable fast DCT algorithm */ +#ifndef SBC_FAST_DCT +#define SBC_FAST_DCT TRUE +#endif /*SBC_FAST_DCT */ + +/* In case we do not use joint stereo mode the flag save some RAM and ROM in case it is set to FALSE */ +#ifndef SBC_JOINT_STE_INCLUDED +#define SBC_JOINT_STE_INCLUDED TRUE +#endif + +/* TRUE -> application should provide PCM buffer, FALSE PCM buffer reside in SBC_ENC_PARAMS */ +#ifndef SBC_NO_PCM_CPY_OPTION +#define SBC_NO_PCM_CPY_OPTION FALSE +#endif + +#define MINIMUM_ENC_VX_BUFFER_SIZE (8*10*2) +#ifndef ENC_VX_BUFFER_SIZE +#define ENC_VX_BUFFER_SIZE (MINIMUM_ENC_VX_BUFFER_SIZE + 64) +/*#define ENC_VX_BUFFER_SIZE MINIMUM_ENC_VX_BUFFER_SIZE + 1024*/ +#endif + +#ifndef SBC_FOR_EMBEDDED_LINUX +#define SBC_FOR_EMBEDDED_LINUX FALSE +#endif + +/*constants used for index calculation*/ +#define SBC_BLK (SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS) + +#include "sbc_types.h" + +typedef struct SBC_ENC_PARAMS_TAG { + SINT16 s16SamplingFreq; /* 16k, 32k, 44.1k or 48k*/ + SINT16 s16ChannelMode; /* mono, dual, streo or joint streo*/ + SINT16 s16NumOfSubBands; /* 4 or 8 */ + SINT16 s16NumOfChannels; + SINT16 s16NumOfBlocks; /* 4, 8, 12 or 16*/ + SINT16 s16AllocationMethod; /* loudness or SNR*/ + SINT16 s16BitPool; /* 16*numOfSb for mono & dual; + 32*numOfSb for stereo & joint stereo */ + UINT16 u16BitRate; + UINT8 sbc_mode; /* SBC_MODE_STD or SBC_MODE_MSBC */ + UINT8 u8NumPacketToEncode; /* number of sbc frame to encode. Default is 1 */ +#if (SBC_JOINT_STE_INCLUDED == TRUE) + SINT16 as16Join[SBC_MAX_NUM_OF_SUBBANDS]; /*1 if JS, 0 otherwise*/ +#endif + + SINT16 s16MaxBitNeed; + SINT16 as16ScaleFactor[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + + SINT16 *ps16NextPcmBuffer; +#if (SBC_NO_PCM_CPY_OPTION == TRUE) + SINT16 *ps16PcmBuffer; +#else + SINT16 as16PcmBuffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS * SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; +#endif + + SINT16 s16ScartchMemForBitAlloc[16]; + + SINT32 s32SbBuffer[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * SBC_MAX_NUM_OF_BLOCKS]; + + SINT16 as16Bits[SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS]; + + UINT8 *pu8Packet; + UINT8 *pu8NextPacket; + UINT16 FrameHeader; + UINT16 u16PacketLength; + +} SBC_ENC_PARAMS; + +#ifdef __cplusplus +extern "C" +{ +#endif +extern void SBC_Encoder(SBC_ENC_PARAMS *strEncParams); +extern void SBC_Encoder_Init(SBC_ENC_PARAMS *strEncParams); +#ifdef __cplusplus +} +#endif +#endif diff --git a/components/ble/ble_stack/sbc/enc/sbc_packing.c b/components/ble/ble_stack/sbc/enc/sbc_packing.c new file mode 100644 index 00000000..afabb6c1 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_packing.c @@ -0,0 +1,263 @@ +/****************************************************************************** + * + * 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 code for packing the Encoded data into bit streams. + * + ******************************************************************************/ + +#include "sbc_encoder.h" +#include "sbc_enc_func_declare.h" + +#if defined(SBC_ENC_INCLUDED) + +#if (SBC_ARM_ASM_OPT==TRUE) +#define Mult32(s32In1,s32In2,s32OutLow) \ +{ \ + __asm \ + { \ + MUL s32OutLow,s32In1,s32In2; \ + } \ +} +#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{ \ + __asm \ + { \ + SMULL s32OutLow,s32OutHi,s32In1,s32In2 \ + } \ +} +#else +#define Mult32(s32In1,s32In2,s32OutLow) s32OutLow=(SINT32)s32In1*(SINT32)s32In2; +#define Mult64(s32In1, s32In2, s32OutLow, s32OutHi) \ +{ \ + s32OutLow = ((SINT32)(UINT16)s32In1 * (UINT16)s32In2); \ + s32TempVal2 = (SINT32)((s32In1 >> 16) * (UINT16)s32In2); \ + s32Carry = ( (((UINT32)(s32OutLow)>>16)&0xFFFF) + \ + + (s32TempVal2 & 0xFFFF) ) >> 16; \ + s32OutLow += (s32TempVal2 << 16); \ + s32OutHi = (s32TempVal2 >> 16) + s32Carry; \ +} +#endif + +void EncPacking(SBC_ENC_PARAMS *pstrEncParams) +{ + UINT8 *pu8PacketPtr; /* packet ptr*/ + UINT8 Temp; + SINT32 s32Blk; /* counter for block*/ + SINT32 s32Ch; /* counter for channel*/ + SINT32 s32Sb; /* counter for sub-band*/ + SINT32 s32PresentBit; /* represents bit to be stored*/ + /*SINT32 s32LoopCountI; loop counter*/ + SINT32 s32LoopCountJ; /* loop counter*/ + UINT32 u32QuantizedSbValue, u32QuantizedSbValue0; /* temp variable to store quantized sb val*/ + SINT32 s32LoopCount; /* loop counter*/ + UINT8 u8XoredVal; /* to store XORed value in CRC calculation*/ + UINT8 u8CRC; /* to store CRC value*/ + SINT16 *ps16GenPtr; + SINT32 s32NumOfBlocks; + SINT32 s32NumOfSubBands = pstrEncParams->s16NumOfSubBands; + SINT32 s32NumOfChannels = pstrEncParams->s16NumOfChannels; + UINT32 u32SfRaisedToPow2; /*scale factor raised to power 2*/ + SINT16 *ps16ScfPtr; + SINT32 *ps32SbPtr; + UINT16 u16Levels; /*to store levels*/ + SINT32 s32Temp1; /*used in 64-bit multiplication*/ + SINT32 s32Low; /*used in 64-bit multiplication*/ +#if (SBC_IS_64_MULT_IN_QUANTIZER==TRUE) + SINT32 s32Hi1, s32Low1, s32Carry, s32TempVal2, s32Hi, s32Temp2; +#endif + + pu8PacketPtr = pstrEncParams->pu8NextPacket; /*Initialize the ptr*/ + if (pstrEncParams->sbc_mode != SBC_MODE_MSBC) { + *pu8PacketPtr++ = (UINT8)SBC_SYNC_WORD_STD; /*Sync word*/ + *pu8PacketPtr++ = (UINT8)(pstrEncParams->FrameHeader); + + *pu8PacketPtr = (UINT8)(pstrEncParams->s16BitPool & 0x00FF); + } else { + *pu8PacketPtr++ = (UINT8)SBC_SYNC_WORD_MSBC; /*Sync word*/ + // two reserved bytes + *pu8PacketPtr++ = 0; + *pu8PacketPtr = 0; + } + pu8PacketPtr += 2; /*skip for CRC*/ + + /*here it indicate if it is byte boundary or nibble boundary*/ + s32PresentBit = 8; + Temp = 0; +#if (SBC_JOINT_STE_INCLUDED == TRUE) + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + /* pack join stero parameters */ + for (s32Sb = 0; s32Sb < s32NumOfSubBands; s32Sb++) { + Temp <<= 1; + Temp |= pstrEncParams->as16Join[s32Sb]; + } + + /* pack RFA */ + if (s32NumOfSubBands == SUB_BANDS_4) { + s32PresentBit = 4; + } else { + *(pu8PacketPtr++) = Temp; + Temp = 0; + } + } +#endif + + /* Pack Scale factor */ + ps16GenPtr = pstrEncParams->as16ScaleFactor; + s32Sb = s32NumOfChannels * s32NumOfSubBands; + /*Temp=*pu8PacketPtr;*/ + for (s32Ch = s32Sb; s32Ch > 0; s32Ch--) { + Temp <<= 4; + Temp |= *ps16GenPtr++; + + if (s32PresentBit == 4) { + s32PresentBit = 8; + *(pu8PacketPtr++) = Temp; + Temp = 0; + } else { + s32PresentBit = 4; + } + } + + /* Pack samples */ + ps32SbPtr = pstrEncParams->s32SbBuffer; + /*Temp=*pu8PacketPtr;*/ + s32NumOfBlocks = pstrEncParams->s16NumOfBlocks; + for (s32Blk = s32NumOfBlocks - 1; s32Blk >= 0; s32Blk--) { + ps16GenPtr = pstrEncParams->as16Bits; + ps16ScfPtr = pstrEncParams->as16ScaleFactor; + for (s32Ch = s32Sb - 1; s32Ch >= 0; s32Ch--) { + s32LoopCount = *ps16GenPtr++; + if (s32LoopCount != 0) { +#if (SBC_IS_64_MULT_IN_QUANTIZER==TRUE) + /* finding level from reconstruction part of decoder */ + u32SfRaisedToPow2 = ((UINT32)1 << ((*ps16ScfPtr) + 1)); + u16Levels = (UINT16)(((UINT32)1 << s32LoopCount) - 1); + + /* quantizer */ + s32Temp1 = (*ps32SbPtr >> 2) + (u32SfRaisedToPow2 << 12); + s32Temp2 = u16Levels; + + Mult64 (s32Temp1, s32Temp2, s32Low, s32Hi); + + s32Low1 = s32Low >> ((*ps16ScfPtr) + 2); + s32Low1 &= ((UINT32)1 << (32 - ((*ps16ScfPtr) + 2))) - 1; + s32Hi1 = s32Hi << (32 - ((*ps16ScfPtr) + 2)); + + u32QuantizedSbValue0 = (UINT16)((s32Low1 | s32Hi1) >> 12); +#else + /* finding level from reconstruction part of decoder */ + u32SfRaisedToPow2 = ((UINT32)1 << *ps16ScfPtr); + u16Levels = (UINT16)(((UINT32)1 << s32LoopCount) - 1); + + /* quantizer */ + s32Temp1 = (*ps32SbPtr >> 15) + u32SfRaisedToPow2; + Mult32(s32Temp1, u16Levels, s32Low); + s32Low >>= (*ps16ScfPtr + 1); + u32QuantizedSbValue0 = (UINT16)s32Low; +#endif + /*store the number of bits required and the quantized s32Sb + sample to ease the coding*/ + u32QuantizedSbValue = u32QuantizedSbValue0; + + if (s32PresentBit >= s32LoopCount) { + Temp <<= s32LoopCount; + Temp |= u32QuantizedSbValue; + s32PresentBit -= s32LoopCount; + } else { + while (s32PresentBit < s32LoopCount) { + s32LoopCount -= s32PresentBit; + u32QuantizedSbValue >>= s32LoopCount; + + /*remove the unwanted msbs*/ + /*u32QuantizedSbValue <<= 16 - s32PresentBit; + u32QuantizedSbValue >>= 16 - s32PresentBit;*/ + + Temp <<= s32PresentBit; + + Temp |= u32QuantizedSbValue ; + /*restore the original*/ + u32QuantizedSbValue = u32QuantizedSbValue0; + + *(pu8PacketPtr++) = Temp; + Temp = 0; + s32PresentBit = 8; + } + Temp <<= s32LoopCount; + + /* remove the unwanted msbs */ + /*u32QuantizedSbValue <<= 16 - s32LoopCount; + u32QuantizedSbValue >>= 16 - s32LoopCount;*/ + + Temp |= u32QuantizedSbValue; + + s32PresentBit -= s32LoopCount; + } + } + ps16ScfPtr++; + ps32SbPtr++; + } + } + + Temp <<= s32PresentBit; + *pu8PacketPtr = Temp; + pstrEncParams->u16PacketLength = pu8PacketPtr - pstrEncParams->pu8NextPacket + 1; + /*find CRC*/ + pu8PacketPtr = pstrEncParams->pu8NextPacket + 1; /*Initialize the ptr*/ + u8CRC = 0x0F; + s32LoopCount = s32Sb >> 1; + + /* + The loops is run from the start of the packet till the scale factor + parameters. In case of JS, 'join' parameter is included in the packet + so that many more bytes are included in CRC calculation. + */ + Temp = *pu8PacketPtr; + for (s32Ch = 1; s32Ch < (s32LoopCount + 4); s32Ch++) { + /* skip sync word and CRC bytes */ + if (s32Ch != 3) { + for (s32LoopCountJ = 7; s32LoopCountJ >= 0; s32LoopCountJ--) { + u8XoredVal = ((u8CRC >> 7) & 0x01) ^ ((Temp >> s32LoopCountJ) & 0x01); + u8CRC <<= 1; + u8CRC ^= (u8XoredVal * 0x1D); + u8CRC &= 0xFF; + } + } + Temp = *(++pu8PacketPtr); + } + + if (pstrEncParams->s16ChannelMode == SBC_JOINT_STEREO) { + for (s32LoopCountJ = 7; s32LoopCountJ >= (8 - s32NumOfSubBands); s32LoopCountJ--) { + u8XoredVal = ((u8CRC >> 7) & 0x01) ^ ((Temp >> s32LoopCountJ) & 0x01); + u8CRC <<= 1; + u8CRC ^= (u8XoredVal * 0x1D); + u8CRC &= 0xFF; + } + } + + /* CRC calculation ends here */ + + /* store CRC in packet */ + pu8PacketPtr = pstrEncParams->pu8NextPacket; /*Initialize the ptr*/ + pu8PacketPtr += 3; + *pu8PacketPtr = u8CRC; + pstrEncParams->pu8NextPacket += pstrEncParams->u16PacketLength; /* move the pointer to the end in case there is more than one frame to encode */ +} + +#endif /* #if defined(SBC_ENC_INCLUDED) */ diff --git a/components/ble/ble_stack/sbc/enc/sbc_types.h b/components/ble/ble_stack/sbc/enc/sbc_types.h new file mode 100644 index 00000000..6fd5ba42 --- /dev/null +++ b/components/ble/ble_stack/sbc/enc/sbc_types.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Data type declarations. + * + ******************************************************************************/ + +#ifndef SBC_TYPES_H +#define SBC_TYPES_H + +#include + +typedef uint8_t UINT8; +typedef uint16_t UINT16; +typedef uint32_t UINT32; +typedef uint64_t UINT64; +typedef short SINT16; +typedef long SINT32; + +#if (SBC_IPAQ_OPT == TRUE) + +#if (SBC_FOR_EMBEDDED_LINUX == TRUE) +typedef long long SINT64; +#else +typedef int64_t SINT64; +#endif + +#elif (SBC_IS_64_MULT_IN_WINDOW_ACCU == TRUE) || (SBC_IS_64_MULT_IN_IDCT == TRUE) + +#if (SBC_FOR_EMBEDDED_LINUX == TRUE) +typedef long long SINT64; +#else +typedef int64_t SINT64; +#endif + +#endif + +#define abs32(x) ( (x >= 0) ? x : (-x) ) + +#endif diff --git a/components/ble/ble_stack/services/bas.c b/components/ble/ble_stack/services/bas.c new file mode 100644 index 00000000..055b83f4 --- /dev/null +++ b/components/ble/ble_stack/services/bas.c @@ -0,0 +1,92 @@ +/** @file + * @brief GATT Battery Service + */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "bluetooth.h" +#include "conn.h" +#include "gatt.h" +#include "uuid.h" +#include "bas.h" + +#if !defined(BFLB_BLE) +#define LOG_LEVEL CONFIG_BT_GATT_BAS_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bas); +#endif + +static u8_t battery_level = 100U; + +static void blvl_ccc_cfg_changed(const struct bt_gatt_attr *attr, + u16_t value) +{ + ARG_UNUSED(attr); + + bool notif_enabled = (value == BT_GATT_CCC_NOTIFY); + +#if !defined(BFLB_BLE) + LOG_INF("BAS Notifications %s", notif_enabled ? "enabled" : "disabled"); +#endif +} + +static ssize_t read_blvl(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + u8_t lvl8 = battery_level; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &lvl8, + sizeof(lvl8)); +} + +static struct bt_gatt_attr attrs[]= { + BT_GATT_PRIMARY_SERVICE(BT_UUID_BAS), + BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ, read_blvl, NULL, + &battery_level), + BT_GATT_CCC(blvl_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ, + NULL, NULL, NULL), +}; + +struct bt_gatt_service bas = BT_GATT_SERVICE(attrs); + +void bas_init(void) +{ + bt_gatt_service_register(&bas); +} + +u8_t bt_gatt_bas_get_battery_level(void) +{ + return battery_level; +} + +int bt_gatt_bas_set_battery_level(u8_t level) +{ + int rc; + + if (level > 100U) { + return -EINVAL; + } + + battery_level = level; + + rc = bt_gatt_notify(NULL, &bas.attrs[1], &level, sizeof(level)); + + return rc == -ENOTCONN ? 0 : rc; +} + +#if !defined(BFLB_BLE) +SYS_INIT(bas_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); +#endif diff --git a/components/ble/ble_stack/services/bas.h b/components/ble/ble_stack/services/bas.h new file mode 100644 index 00000000..da6ccabf --- /dev/null +++ b/components/ble/ble_stack/services/bas.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_BAS_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_BAS_H_ + +/** + * @brief Battery Service (BAS) + * @defgroup bt_gatt_bas Battery Service (BAS) + * @ingroup bluetooth + * @{ + * + * [Experimental] Users should note that the APIs can change + * as a part of ongoing development. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void bas_init(void); + +/** @brief Read battery level value. + * + * Read the characteristic value of the battery level + * + * @return The battery level in percent. + */ +u8_t bt_gatt_bas_get_battery_level(void); + +/** @brief Update battery level value. + * + * Update the characteristic value of the battery level + * This will send a GATT notification to all current subscribers. + * + * @param level The battery level in percent. + * + * @return Zero in case of success and error code in case of error. + */ +int bt_gatt_bas_set_battery_level(u8_t level); + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_SERVICES_BAS_H_ */ diff --git a/components/ble/ble_stack/services/ble_tp_svc.c b/components/ble/ble_stack/services/ble_tp_svc.c new file mode 100644 index 00000000..d20e7a36 --- /dev/null +++ b/components/ble/ble_stack/services/ble_tp_svc.c @@ -0,0 +1,317 @@ +/**************************************************************************** +FILE NAME + ble_tp_svc.c + +DESCRIPTION + test profile demo + +NOTES +*/ +/****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "bluetooth.h" +#include "conn.h" +#include "gatt.h" +#include "hci_core.h" +#include "uuid.h" +#include "ble_tp_svc.h" +#include "log.h" + +static void ble_tp_connected(struct bt_conn *conn, u8_t err); +static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason); + +struct bt_conn *ble_tp_conn; +struct bt_gatt_exchange_params exchg_mtu; +TaskHandle_t ble_tp_task_h; + +int tx_mtu_size = 20; +u8_t tp_start = 0; +static u8_t created_tp_task = 0; +static u8_t isRegister = 0; + +static struct bt_conn_cb ble_tp_conn_callbacks = { + .connected = ble_tp_connected, + .disconnected = ble_tp_disconnected, +}; + +/************************************************************************* +NAME + ble_tp_tx_mtu_size +*/ +static void ble_tp_tx_mtu_size(struct bt_conn *conn, u8_t err, + struct bt_gatt_exchange_params *params) +{ + if(!err) + { + tx_mtu_size = bt_gatt_get_mtu(ble_tp_conn); + BT_WARN("ble tp echange mtu size success, mtu size: %d\n", tx_mtu_size); + } + else + { + BT_WARN("ble tp echange mtu size failure, err: %d\n", err); + } +} + +/************************************************************************* +NAME + ble_tp_connected +*/ +static void ble_tp_connected(struct bt_conn *conn, u8_t err) +{ + int tx_octets = 0x00fb; + int tx_time = 0x0848; + int ret = -1; + + if( err ) + return; + + printf("%s\n",__func__); + ble_tp_conn = conn; + + //set data length after connected. + ret = bt_le_set_data_len(ble_tp_conn, tx_octets, tx_time); + if(!ret) + { + BT_WARN("ble tp set data length success.\n"); + } + else + { + BT_WARN("ble tp set data length failure, err: %d\n", ret); + } + + //exchange mtu size after connected. + exchg_mtu.func = ble_tp_tx_mtu_size; + ret = bt_gatt_exchange_mtu(ble_tp_conn, &exchg_mtu); + if (!ret) { + BT_WARN("ble tp exchange mtu size pending.\n"); + } else { + BT_WARN("ble tp exchange mtu size failure, err: %d\n", ret); + } +} + +/************************************************************************* +NAME + ble_tp_disconnected +*/ +static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason) +{ + BT_WARN("%s\n",__func__); + + ble_tp_conn = NULL; +} + +/************************************************************************* +NAME + ble_tp_recv_rd +*/ +static int ble_tp_recv_rd(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, u16_t len, u16_t offset) +{ + int size = 9; + char data[9] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}; + + memcpy(buf, data, size); + + return size; +} + +/************************************************************************* +NAME + ble_tp_recv_wr +*/ +static int ble_tp_recv_wr(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, u16_t len, u16_t offset, u8_t flags) +{ + BT_INFO("recv data len=%d, offset=%d, flag=%d\r\n", len, offset, flags); + + if (flags & BT_GATT_WRITE_FLAG_PREPARE) + { + //Don't use prepare write data, execute write will upload data again. + BT_WARN("rcv prepare write request\n"); + return 0; + } + + if(flags & BT_GATT_WRITE_FLAG_CMD) + { + //Use write command data. + BT_INFO("rcv write command\n"); + } + else + { + //Use write request / execute write data. + BT_INFO("rcv write request / exce write\n"); + } + + return len; +} + +/************************************************************************* +NAME + indicate_rsp /bl_tp_send_indicate +*/ +void indicate_rsp(struct bt_conn *conn, const struct bt_gatt_attr *attr, u8_t err) +{ + BT_WARN("receive confirm, err:%d\n", err); +} + +static int bl_tp_send_indicate(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, u16_t len) +{ + //indicate paramete must be allocated statically + static struct bt_gatt_indicate_params ind_params; + ind_params.attr = attr; + ind_params.data = data; + ind_params.len = len; + ind_params.func = indicate_rsp; + ind_params.uuid = NULL; + + return bt_gatt_indicate(conn, &ind_params); +} + +/************************************************************************* +NAME + ble_tp_ind_ccc_changed +*/ +static void ble_tp_ind_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ + int err = -1; + char data[9] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}; + + if(value == BT_GATT_CCC_INDICATE) { + err = bl_tp_send_indicate(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_IND_ATTR_VAL_INDEX), data, 9); + BT_WARN("ble tp send indatcate: %d\n", err); + } +} + +/************************************************************************* +NAME + ble_tp_notify +*/ +static void ble_tp_notify_task(void *pvParameters) +{ + int err = -1; + char data[244] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}; + + while(1) + { + err = bt_gatt_notify(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX), data, (tx_mtu_size - 3)); + BT_WARN("ble tp send notify : %d\n", err); + + } +} + +/************************************************************************* +NAME + ble_tp_not_ccc_changed +*/ +static void ble_tp_not_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ + BT_WARN("ccc:value=[%d]\r\n",value); + + if(tp_start){ + + if(value == BT_GATT_CCC_NOTIFY) { + if(xTaskCreate(ble_tp_notify_task, (char*)"bletp", 256, NULL, 15, &ble_tp_task_h) == pdPASS) + { + created_tp_task = 1; + BT_WARN("Create throughput tx task success .\n"); + } + else + { + created_tp_task = 0; + BT_WARN("Create throughput tx taskfail .\n"); + } + } else { + + if(created_tp_task){ + BT_WARN("Delete throughput tx task .\n"); + vTaskDelete(ble_tp_task_h); + created_tp_task = 0; + } + } + } + else if(tp_start == 0){ + if(created_tp_task){ + BT_WARN("Delete throughput tx task .\n"); + vTaskDelete(ble_tp_task_h); + created_tp_task = 0; + } + } +} + +/************************************************************************* +* DEFINE : attrs +*/ +static struct bt_gatt_attr attrs[]= { + BT_GATT_PRIMARY_SERVICE(BT_UUID_SVC_BLE_TP), + + BT_GATT_CHARACTERISTIC(BT_UUID_CHAR_BLE_TP_RD, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + ble_tp_recv_rd, + NULL, + NULL), + + BT_GATT_CHARACTERISTIC(BT_UUID_CHAR_BLE_TP_WR, + BT_GATT_CHRC_WRITE |BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE|BT_GATT_PERM_PREPARE_WRITE, + NULL, + ble_tp_recv_wr, + NULL), + + BT_GATT_CHARACTERISTIC(BT_UUID_CHAR_BLE_TP_IND, + BT_GATT_CHRC_INDICATE, + NULL, + NULL, + NULL, + NULL), + + BT_GATT_CCC(ble_tp_ind_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + + BT_GATT_CHARACTERISTIC(BT_UUID_CHAR_BLE_TP_NOT, + BT_GATT_CHRC_NOTIFY, + NULL, + NULL, + NULL, + NULL), + + BT_GATT_CCC(ble_tp_not_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE) + +}; + +/************************************************************************* +NAME + get_attr +*/ +struct bt_gatt_attr *get_attr(u8_t index) +{ + return &attrs[index]; +} + + +struct bt_gatt_service ble_tp_server = BT_GATT_SERVICE(attrs); + + +/************************************************************************* +NAME + ble_tp_init +*/ +void ble_tp_init() +{ + if( !isRegister ) + { + isRegister = 1; + bt_conn_cb_register(&ble_tp_conn_callbacks); + bt_gatt_service_register(&ble_tp_server); + } +} + + + diff --git a/components/ble/ble_stack/services/ble_tp_svc.h b/components/ble/ble_stack/services/ble_tp_svc.h new file mode 100644 index 00000000..35fc598b --- /dev/null +++ b/components/ble/ble_stack/services/ble_tp_svc.h @@ -0,0 +1,39 @@ +/**************************************************************************** +FILE NAME + ble_tp_svc.h + +DESCRIPTION +NOTES +*/ +/****************************************************************************/ + +#ifndef _BLE_TP_SVC_H_ +#define _BLE_TP_SVC_H_ + +#include "config.h" + +//07af27a5-9c22-11ea-9afe-02fcdc4e7412 +#define BT_UUID_SVC_BLE_TP BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x07af27a5, 0x9c22, 0x11ea, 0x9afe, 0x02fcdc4e7412)) +//07af27a6-9c22-11ea-9afe-02fcdc4e7412 +#define BT_UUID_CHAR_BLE_TP_RD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x07af27a6, 0x9c22, 0x11ea, 0x9afe, 0x02fcdc4e7412)) +//07af27a7-9c22-11ea-9afe-02fcdc4e7412 +#define BT_UUID_CHAR_BLE_TP_WR BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x07af27a7, 0x9c22, 0x11ea, 0x9afe, 0x02fcdc4e7412)) +//07af27a8-9c22-11ea-9afe-02fcdc4e7412 +#define BT_UUID_CHAR_BLE_TP_IND BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x07af27a8, 0x9c22, 0x11ea, 0x9afe, 0x02fcdc4e7412)) +//07af27a9-9c22-11ea-9afe-02fcdc4e7412 +#define BT_UUID_CHAR_BLE_TP_NOT BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x07af27a9, 0x9c22, 0x11ea, 0x9afe, 0x02fcdc4e7412)) + +//read value handle offset 2 +#define BT_CHAR_BLE_TP_RD_ATTR_VAL_INDEX (2) +//write value handle offset 4 +#define BT_CHAR_BLE_TP_WR_ATTR_VAL_INDEX (4) +//indicate value handle offset 6 +#define BT_CHAR_BLE_TP_IND_ATTR_VAL_INDEX (6) +//notity value handle offset 9 +#define BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX (9) + +void ble_tp_init(); +struct bt_gatt_attr *get_attr(u8_t index); + +#endif + diff --git a/components/ble/ble_stack/services/dis.c b/components/ble/ble_stack/services/dis.c new file mode 100644 index 00000000..863d24d4 --- /dev/null +++ b/components/ble/ble_stack/services/dis.c @@ -0,0 +1,271 @@ +/** @file + * @brief GATT Device Information Service + */ + +/* + * Copyright (c) 2019 Demant + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "settings.h" + +#include "bluetooth.h" +#include "hci_host.h" +#include "conn.h" +#include "uuid.h" +#include "gatt.h" +#include "dis.h" + +#if !defined(BFLB_BLE) +#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SERVICE) +#define LOG_MODULE_NAME bt_dis +#include "log.h" +#endif + +#if CONFIG_BT_GATT_DIS_PNP +struct dis_pnp { + u8_t pnp_vid_src; + u16_t pnp_vid; + u16_t pnp_pid; + u16_t pnp_ver; +} __packed; + +#if defined(BFLB_BLE) +#if defined(BL702) +#define CONFIG_BT_GATT_DIS_MODEL "BL702_BLE_MODEL" +#elif defined(BL602) +#define CONFIG_BT_GATT_DIS_MODEL "BL602_BLE_MODEL" +#else +#define CONFIG_BT_GATT_DIS_MODEL +#endif + +#define CONFIG_BT_GATT_DIS_MANUF "Bouffalo Lab" +#define CONFIG_BT_GATT_DIS_SERIAL_NUMBER_STR "G0G0U40690230TFC" +#define CONFIG_BT_GATT_DIS_FW_REV_STR "52512901" +#define CONFIG_BT_GATT_DIS_HW_REV_STR "0001" +#define CONFIG_BT_GATT_DIS_SW_REV_STR "123" + +#define CONFIG_BT_GATT_DIS_PNP_VID 0x07AF +#define CONFIG_BT_GATT_DIS_PNP_PID 0x707 +#define CONFIG_BT_GATT_DIS_PNP_VER 0x0000 +#endif + +static struct dis_pnp dis_pnp_id = { + .pnp_vid_src = DIS_PNP_VID_SRC, + .pnp_vid = CONFIG_BT_GATT_DIS_PNP_VID, + .pnp_pid = CONFIG_BT_GATT_DIS_PNP_PID, + .pnp_ver = CONFIG_BT_GATT_DIS_PNP_VER, +}; +#endif + +#if defined(CONFIG_BT_GATT_DIS_SETTINGS) +static u8_t dis_model[CONFIG_BT_GATT_DIS_STR_MAX] = CONFIG_BT_GATT_DIS_MODEL; +static u8_t dis_manuf[CONFIG_BT_GATT_DIS_STR_MAX] = CONFIG_BT_GATT_DIS_MANUF; +#if defined(CONFIG_BT_GATT_DIS_SERIAL_NUMBER) +static u8_t dis_serial_number[CONFIG_BT_GATT_DIS_STR_MAX] = + CONFIG_BT_GATT_DIS_SERIAL_NUMBER_STR; +#endif +#if defined(CONFIG_BT_GATT_DIS_FW_REV) +static u8_t dis_fw_rev[CONFIG_BT_GATT_DIS_STR_MAX] = + CONFIG_BT_GATT_DIS_FW_REV_STR; +#endif +#if defined(CONFIG_BT_GATT_DIS_HW_REV) +static u8_t dis_hw_rev[CONFIG_BT_GATT_DIS_STR_MAX] = + CONFIG_BT_GATT_DIS_HW_REV_STR; +#endif +#if defined(CONFIG_BT_GATT_DIS_SW_REV) +static u8_t dis_sw_rev[CONFIG_BT_GATT_DIS_STR_MAX] = + CONFIG_BT_GATT_DIS_SW_REV_STR; +#endif + +#define BT_GATT_DIS_MODEL_REF dis_model +#define BT_GATT_DIS_MANUF_REF dis_manuf +#define BT_GATT_DIS_SERIAL_NUMBER_STR_REF dis_serial_number +#define BT_GATT_DIS_FW_REV_STR_REF dis_fw_rev +#define BT_GATT_DIS_HW_REV_STR_REF dis_hw_rev +#define BT_GATT_DIS_SW_REV_STR_REF dis_sw_rev + +#else /* CONFIG_BT_GATT_DIS_SETTINGS */ + +#define BT_GATT_DIS_MODEL_REF CONFIG_BT_GATT_DIS_MODEL +#define BT_GATT_DIS_MANUF_REF CONFIG_BT_GATT_DIS_MANUF +#define BT_GATT_DIS_SERIAL_NUMBER_STR_REF CONFIG_BT_GATT_DIS_SERIAL_NUMBER_STR +#define BT_GATT_DIS_FW_REV_STR_REF CONFIG_BT_GATT_DIS_FW_REV_STR +#define BT_GATT_DIS_HW_REV_STR_REF CONFIG_BT_GATT_DIS_HW_REV_STR +#define BT_GATT_DIS_SW_REV_STR_REF CONFIG_BT_GATT_DIS_SW_REV_STR + +#endif /* CONFIG_BT_GATT_DIS_SETTINGS */ + +static ssize_t read_str(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, + strlen(attr->user_data)); +} + +#if CONFIG_BT_GATT_DIS_PNP +static ssize_t read_pnp_id(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + u16_t len, u16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, &dis_pnp_id, + sizeof(dis_pnp_id)); +} +#endif + +/* Device Information Service Declaration */ +static struct bt_gatt_attr attrs[]= { + + BT_GATT_PRIMARY_SERVICE(BT_UUID_DIS), + + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MODEL_NUMBER, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, BT_GATT_DIS_MODEL_REF), + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_MANUFACTURER_NAME, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, BT_GATT_DIS_MANUF_REF), +#if CONFIG_BT_GATT_DIS_PNP + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_PNP_ID, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_pnp_id, NULL, &dis_pnp_id), +#endif + +#if defined(CONFIG_BT_GATT_DIS_SERIAL_NUMBER) + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_SERIAL_NUMBER, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, + BT_GATT_DIS_SERIAL_NUMBER_STR_REF), +#endif +#if defined(CONFIG_BT_GATT_DIS_FW_REV) + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_FIRMWARE_REVISION, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, BT_GATT_DIS_FW_REV_STR_REF), +#endif +#if defined(CONFIG_BT_GATT_DIS_HW_REV) + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_HARDWARE_REVISION, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, BT_GATT_DIS_HW_REV_STR_REF), +#endif +#if defined(CONFIG_BT_GATT_DIS_SW_REV) + BT_GATT_CHARACTERISTIC(BT_UUID_DIS_SOFTWARE_REVISION, + BT_GATT_CHRC_READ, BT_GATT_PERM_READ, + read_str, NULL, BT_GATT_DIS_SW_REV_STR_REF), +#endif + +}; + +static struct bt_gatt_service dis_svc = BT_GATT_SERVICE(attrs); + +void dis_init(u8_t vid_src, u16_t vid, u16_t pid, u16_t pid_ver) +{ + dis_pnp_id.pnp_vid_src = vid_src; + dis_pnp_id.pnp_vid = vid; + dis_pnp_id.pnp_pid = pid; + dis_pnp_id.pnp_ver = pid_ver; + bt_gatt_service_register(&dis_svc); +} + +#if defined(CONFIG_BT_SETTINGS) && defined(CONFIG_BT_GATT_DIS_SETTINGS) +static int dis_set(const char *name, size_t len_rd, + settings_read_cb read_cb, void *store) +{ + int len, nlen; + const char *next; + + nlen = settings_name_next(name, &next); + if (!strncmp(name, "manuf", nlen)) { + len = read_cb(store, &dis_manuf, sizeof(dis_manuf) - 1); + if (len < 0) { + BT_ERR("Failed to read manufacturer from storage" + " (err %d)", len); + } else { + dis_manuf[len] = '\0'; + + BT_DBG("Manufacturer set to %s", dis_manuf); + } + return 0; + } + if (!strncmp(name, "model", nlen)) { + len = read_cb(store, &dis_model, sizeof(dis_model) - 1); + if (len < 0) { + BT_ERR("Failed to read model from storage" + " (err %d)", len); + } else { + dis_model[len] = '\0'; + + BT_DBG("Model set to %s", dis_model); + } + return 0; + } +#if defined(CONFIG_BT_GATT_DIS_SERIAL_NUMBER) + if (!strncmp(name, "serial", nlen)) { + len = read_cb(store, &dis_serial_number, + sizeof(dis_serial_number) - 1); + if (len < 0) { + BT_ERR("Failed to read serial number from storage" + " (err %d)", len); + } else { + dis_serial_number[len] = '\0'; + + BT_DBG("Serial number set to %s", dis_serial_number); + } + return 0; + } +#endif +#if defined(CONFIG_BT_GATT_DIS_FW_REV) + if (!strncmp(name, "fw", nlen)) { + len = read_cb(store, &dis_fw_rev, sizeof(dis_fw_rev) - 1); + if (len < 0) { + BT_ERR("Failed to read firmware revision from storage" + " (err %d)", len); + } else { + dis_fw_rev[len] = '\0'; + + BT_DBG("Firmware revision set to %s", dis_fw_rev); + } + return 0; + } +#endif +#if defined(CONFIG_BT_GATT_DIS_HW_REV) + if (!strncmp(name, "hw", nlen)) { + len = read_cb(store, &dis_hw_rev, sizeof(dis_hw_rev) - 1); + if (len < 0) { + BT_ERR("Failed to read hardware revision from storage" + " (err %d)", len); + } else { + dis_hw_rev[len] = '\0'; + + BT_DBG("Hardware revision set to %s", dis_hw_rev); + } + return 0; + } +#endif +#if defined(CONFIG_BT_GATT_DIS_SW_REV) + if (!strncmp(name, "sw", nlen)) { + len = read_cb(store, &dis_sw_rev, sizeof(dis_sw_rev) - 1); + if (len < 0) { + BT_ERR("Failed to read software revision from storage" + " (err %d)", len); + } else { + dis_sw_rev[len] = '\0'; + + BT_DBG("Software revision set to %s", dis_sw_rev); + } + return 0; + } +#endif + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt_dis, "bt/dis", NULL, dis_set, NULL, NULL); +#endif /* CONFIG_BT_GATT_DIS_SETTINGS && CONFIG_BT_SETTINGS*/ diff --git a/components/ble/ble_stack/services/dis.h b/components/ble/ble_stack/services/dis.h new file mode 100644 index 00000000..6219c904 --- /dev/null +++ b/components/ble/ble_stack/services/dis.h @@ -0,0 +1,30 @@ +/** @file + * @brief GATT Device Information Service + */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DIS_H_ +#define _DIS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define AR_VENDOR_ID 0x0001 +#define AR_PRODUCT_ID 0x0002 + +enum{ + DIS_PNP_VID_SRC = 0x01, + USB_IMPL_VID +}; +void dis_init(u8_t vid_src, u16_t vid, u16_t pid, u16_t pnp_ver); +#ifdef __cplusplus +} +#endif +#endif diff --git a/components/ble/ble_stack/services/hog.c b/components/ble/ble_stack/services/hog.c new file mode 100644 index 00000000..3711fd09 --- /dev/null +++ b/components/ble/ble_stack/services/hog.c @@ -0,0 +1,213 @@ +/** @file + * @brief HoG Service sample + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "hog.h" + +enum { + HIDS_REMOTE_WAKE = BIT(0), + HIDS_NORMALLY_CONNECTABLE = BIT(1), +}; + +struct hids_info { + uint16_t version; /* version number of base USB HID Specification */ + uint8_t code; /* country HID Device hardware is localized for. */ + uint8_t flags; +} __packed; + +struct hids_report { + uint8_t id; /* report id */ + uint8_t type; /* report type */ +} __packed; + +static struct hids_info info = { + .version = 0x0000, + .code = 0x00, + .flags = HIDS_NORMALLY_CONNECTABLE, +}; + +enum { + HIDS_INPUT = 0x01, + HIDS_OUTPUT = 0x02, + HIDS_FEATURE = 0x03, +}; + +static struct hids_report input = { + .id = 0x01, + .type = HIDS_INPUT, +}; + +static uint8_t simulate_input; +static uint8_t ctrl_point; +static uint8_t report_map[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x02, /* Usage (Mouse) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xA1, 0x00, /* Collection (Physical) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x03, /* Usage Maximum (0x03) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x95, 0x03, /* Report Count (3) */ + 0x75, 0x01, /* Report Size (1) */ + 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,...) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x05, /* Report Size (5) */ + 0x81, 0x03, /* Input (Const,Var,Abs,No Wrap,Linear,...) */ + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x15, 0x81, /* Logical Minimum (129) */ + 0x25, 0x7F, /* Logical Maximum (127) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,...) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +static ssize_t read_info(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, + sizeof(struct hids_info)); +} + +static ssize_t read_report_map(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map, + sizeof(report_map)); +} + +static ssize_t read_report(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data, + sizeof(struct hids_report)); +} + +static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + simulate_input = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; + BT_WARN("simulate_input = [%d]\r\n",simulate_input); +} + +static ssize_t read_input_report(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0); +} + +static ssize_t write_ctrl_point(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) +{ + uint8_t *value = attr->user_data; + + if (offset + len > sizeof(ctrl_point)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + memcpy(value + offset, buf, len); + + return len; +} + +/* HID Service Declaration */ +static struct bt_gatt_attr attrs[]={ + BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, read_info, NULL, &info), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, read_report_map, NULL, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, + BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ_AUTHEN, + read_input_report, NULL, NULL), + BT_GATT_CCC(input_ccc_changed, + BT_GATT_PERM_READ_AUTHEN | BT_GATT_PERM_WRITE_AUTHEN), + BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ, + read_report, NULL, &input), + BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, + BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE, + NULL, write_ctrl_point, &ctrl_point), +}; + +struct hids_remote_key { + u8_t hid_page; + u16_t hid_usage; +} __packed; + +static struct hids_remote_key remote_kbd_map_tab[] = { + {HID_PAGE_KBD, Key_a_or_A2}, + {HID_PAGE_KBD, Key_b_or_B}, + {HID_PAGE_KBD, Key_c_or_C}, +}; + +int hog_notify(struct bt_conn *conn, uint16_t hid_usage,uint8_t press) +{ + struct bt_gatt_attr *attr; + struct hids_remote_key *remote_key = NULL; + u8_t len = 4, data[4]; + + for(int i = 0; i < (sizeof(remote_kbd_map_tab)/sizeof(remote_kbd_map_tab[0])); i++){ + + if(remote_kbd_map_tab[i].hid_usage == hid_usage){ + remote_key = &remote_kbd_map_tab[i]; + break; + } + } + + if(!remote_key) + return EINVAL; + + if(remote_key->hid_page == HID_PAGE_KBD){ + attr = &attrs[BT_CHAR_BLE_HID_REPORT_ATTR_VAL_INDEX]; + len = 3; + } + else + return EINVAL; + + sys_put_le16(hid_usage, data); + data[2] = 0; + data[3] = 0; + + if(!press){ + memset(data, 0, len); + } + + return bt_gatt_notify(conn, attr, data, len); + } + +struct bt_gatt_service hog_srv = BT_GATT_SERVICE(attrs); + +void hog_init(void) +{ + bt_gatt_service_register(&hog_srv); +} diff --git a/components/ble/ble_stack/services/hog.h b/components/ble/ble_stack/services/hog.h new file mode 100644 index 00000000..7e8dcfe0 --- /dev/null +++ b/components/ble/ble_stack/services/hog.h @@ -0,0 +1,39 @@ +/** @file + * @brief HoG Service sample + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _HOG_H_ +#define _HOG_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#define HID_PAGE_KBD 0x07 +#define HID_PAGE_CONS 0x0C + +#define BT_CHAR_BLE_HID_INFO_ATTR_VAL_INDEX (2) +#define BT_CHAR_BLE_HID_REPORT_MAP_ATTR_VAL_INDEX (4) +#define BT_CHAR_BLE_HID_REPORT_ATTR_VAL_INDEX (6) +#define BT_CHAR_BLE_HID_CTRL_POINT_ATTR_VAL_INDEX (10) + +enum hid_usage{ + Key_a_or_A2 = 0x0004, + Key_b_or_B, + Key_c_or_C +}; + +void hog_init(void); +int hog_notify(struct bt_conn *conn, uint16_t hid_usage,uint8_t press); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/components/ble/ble_stack/services/oad/oad.h b/components/ble/ble_stack/services/oad/oad.h new file mode 100644 index 00000000..7039c921 --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad.h @@ -0,0 +1,89 @@ +#ifndef __OAD_H__ +#define __OAD_H__ + +#include "types.h" +#include "hci_host.h" +#include "work_q.h" + +#define LOCAL_MANU_CODE 0x2c00 +#define LOCAL_FILE_VER 00000001 + +#define OAD_OPCODE_SIZE 1 +//00070000-0745-4650-8d93-df59be2fc10a +#define BT_UUID_OAD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00070000, 0x0745, 0x4650, 0x8d93, 0xdf59be2fc10a)) +//00070001-0745-4650-8d93-df59be2fc10a +#define BT_UUID_OAD_DATA_IN BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00070001, 0x0745, 0x4650, 0x8d93, 0xdf59be2fc10a)) +//00070002-0745-4650-8d93-df59be2fc10a +#define BT_UUID_OAD_DATA_OUT BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x00070002, 0x0745, 0x4650, 0x8d93, 0xdf59be2fc10a)) + +enum{ + OAD_SUCC = 0x00, + OAD_ABORT, + OAD_INVALID_IMAG, + OAD_REQ_MORE_DATA, + OAD_MALORMED_CMD, + OAD_UPGRD_CMPLT, +}; + +enum{ + OAD_CMD_IMAG_IDENTITY = 0x00, + OAD_CMD_IMAG_BLOCK_REQ, + OAD_CMD_IMAG_BLOCK_RESP, + OAD_CMD_IMAG_UPGRD_END, + OAD_CMD_IMAG_INFO, +}; + +struct oad_file_info{ + u16_t manu_code; + u32_t file_ver; +} __packed; + +#if defined(CONFIG_BT_SETTINGS) +struct oad_ef_info{ + struct oad_file_info file_info; + u32_t file_offset; + u32_t last_wflash_addr; + u32_t upgrd_crc32; +}__packed; + +#endif + +struct oad_env_tag{ + struct oad_file_info file_info; + u32_t cur_file_size; + u32_t upgrd_file_ver; + u32_t upgrd_file_size; + u32_t upgrd_offset; + u32_t upgrd_crc32; + + struct k_delayed_work upgrd_work; + u32_t new_img_addr; + u32_t w_img_end_addr; +}; + +struct oad_image_identity_t{ + struct oad_file_info file_info; + u32_t file_size; + u32_t crc32; +} __packed; + +struct oad_block_req_t{ + struct oad_file_info file_info; + u32_t file_offset; +} __packed; + +#define OAD_BLK_RSP_DATA_OFFSET 12 +struct oad_block_rsp_t{ + uint8_t status; + struct oad_file_info file_info; + u32_t file_offset; + u8_t data_size; + u8_t *pdata; +} __packed; + +struct oad_upgrd_end_t{ + u8_t status; + struct oad_file_info file_info; +} __packed; + +#endif //__OAD_H__ diff --git a/components/ble/ble_stack/services/oad/oad_client.c b/components/ble/ble_stack/services/oad/oad_client.c new file mode 100644 index 00000000..a5395136 --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_client.c @@ -0,0 +1,352 @@ +/** + **************************************************************************************** + * + * @file app.c + * + * @brief Bouffalo Lab Ble Demo implementation + * + * Copyright (C) Bouffalo Lab 2018 + * + * History: 2020-12-31 crealted by caofp + * + **************************************************************************************** + */ +#include "config.h" +#include "cli.h" +#include "oad_client.h" +#include "log.h" + +/*The header is 15bytes*/ +#define TRANSPORT_FILE_SIZE (244 - 15) +#define MAX_ATT_SIZE (244) +extern struct bt_conn *default_conn; + +u16_t in_handle = 0; +u16_t out_handle = 0; + +void oad_send_image_identity_to_servicer(struct bt_conn *conn,u8_t *data, u16_t len); +void oad_send_block_resp_to_servicer(struct bt_conn *conn, u8_t *data, u8_t len); +static void oad_start_identity(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void oad_discovery(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void oad_subscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); +static void oad_start_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv); + +const struct cli_command oadCmdSet[] STATIC_CLI_CMD_ATTRIBUTE = { + {"oad_start_indentity", "", oad_start_identity}, + {"oad_discovery", "", oad_discovery}, + {"oad_subscribe", "", oad_subscribe}, + {"oad_start_update", "", oad_start_update}, +}; + +void oad_send_data_to_uart(uint8_t cmd_type, uint8_t *data, uint8_t len) +{ + uint8_t final_data[128]; + + if(len + 2 > 128){ + return; + } + memset(final_data,0,128); + + final_data[0] = cmd_type; + final_data[1] = len; + memcpy(&final_data[2], data, len); + + BT_WARN("cmd_data:%s\r\n",bt_hex(final_data,len + 2)); + +} + +void oad_start_to_uart(void) +{ + uint8_t req[1]; + req[0] = OAD_CMDPROC_START; + + oad_send_data_to_uart(CMDPROC_TYPE_OAD, req, sizeof(req)); +} + +void oad_image_identity_to_uart(struct oad_file_info* file_info) +{ + struct oad_cmdproc_req_t req; + req.cmd_id = OAD_CMDPROC_IMAGE_IDENTITY; + memcpy(&req.q.file_info, file_info, sizeof(*file_info)); + oad_send_data_to_uart(CMDPROC_TYPE_OAD, (uint8_t *)&req, sizeof(req)); +} + +void oad_block_req_to_uart(struct oad_file_info* file_info, uint32_t file_offset, uint8_t data_len) +{ + struct oad_cmdproc_req_t req; + + req.cmd_id = OAD_CMDPROC_BLOCK_REQ; + memcpy(&req.q.block_req.file_info, file_info, sizeof(*file_info)); + req.q.block_req.file_offset = file_offset; + req.q.block_req.data_len = data_len; + oad_send_data_to_uart(CMDPROC_TYPE_OAD, (uint8_t *)&req, sizeof(req)); +} + +void oad_upgrd_end_to_uart(uint8_t status, struct oad_file_info* file_info) +{ + struct oad_cmdproc_req_t req; + + req.cmd_id = OAD_CMDPROC_UPGRD_END; + req.q.upgrd_end.status = status; + memcpy(&req.q.upgrd_end.file_info, file_info, sizeof(*file_info)); + + oad_send_data_to_uart(CMDPROC_TYPE_OAD, (uint8_t *)&req, sizeof(req)); +} + +static void oad_start_identity(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint8_t buf[50]; + int len = 0; + + if(!default_conn) + return; + + memset(buf,0,50); + get_bytearray_from_string(&argv[1],buf,50); + len = buf[1]; + if(len + 2 >= 50){ + BT_WARN("Failed to receved data\r\n"); + return; + } + + oad_send_image_identity_to_servicer(default_conn,buf,len+2); +} + +static void oad_start_update(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + uint8_t buf[MAX_ATT_SIZE]; + + if(!default_conn) + return; + + memset(buf,0,MAX_ATT_SIZE); + get_bytearray_from_string(&argv[1],buf,MAX_ATT_SIZE); + + oad_send_block_resp_to_servicer(default_conn,buf,MAX_ATT_SIZE); +} + +static struct bt_gatt_discover_params discover_params; + +static u8_t oad_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) +{ + struct bt_gatt_service_val *gatt_service; + struct bt_gatt_chrc *gatt_chrc; + struct bt_gatt_include *gatt_include; + struct bt_uuid *uuid_out = BT_UUID_OAD_DATA_OUT; + struct bt_uuid *uuid_in = BT_UUID_OAD_DATA_IN; + char str[37]; + + if (!attr) { + BT_WARN("Discover complete\r\n"); + BT_WARN("in handle = (0x%x) out_handle = (0x%x)\r\n",in_handle,out_handle); + (void)memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + + switch (params->type) { + case BT_GATT_DISCOVER_SECONDARY: + case BT_GATT_DISCOVER_PRIMARY: + gatt_service = attr->user_data; + bt_uuid_to_str(gatt_service->uuid, str, sizeof(str)); + BT_WARN("Service %s found: attr handle %x, end_handle %x\r\n", str, attr->handle, gatt_service->end_handle); + + break; + case BT_GATT_DISCOVER_CHARACTERISTIC: + gatt_chrc = attr->user_data; + bt_uuid_to_str(gatt_chrc->uuid, str, sizeof(str)); + BT_WARN("Characteristic %s found: attr->handle %x chrcval->handle %x \r\n", str, attr->handle,gatt_chrc->value_handle); + //print_chrc_props(gatt_chrc->properties); + break; + case BT_GATT_DISCOVER_INCLUDE: + gatt_include = attr->user_data; + bt_uuid_to_str(gatt_include->uuid, str, sizeof(str)); + BT_WARN("Include %s found: attr handle %x, start %x, end %x\r\n", str, attr->handle, + gatt_include->start_handle, gatt_include->end_handle); + break; + default: + bt_uuid_to_str(attr->uuid, str, sizeof(str)); + + if(!bt_uuid_cmp(attr->uuid, uuid_in)){ + in_handle = attr->handle; + }else if(!bt_uuid_cmp(attr->uuid, uuid_out)){ + out_handle = attr->handle; + } + + BT_WARN("Descriptor %s found: handle %x\r\n", str, attr->handle); + break; + } + + return BT_GATT_ITER_CONTINUE; +} + + +static void oad_discovery(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + u8_t disc_type; + + if (!default_conn) { + BT_WARN("Not connected\r\n"); + return; + } + + discover_params.func = oad_discover_func; + discover_params.start_handle = 0x1; + discover_params.end_handle = 0xffff; + discover_params.uuid = NULL; + + get_uint8_from_string(&argv[1], &disc_type); + BT_WARN("disc_type = [%d]\r\n",disc_type); + if(disc_type == 0){ + discover_params.type = BT_GATT_DISCOVER_PRIMARY; + }else if(disc_type == 1){ + discover_params.type = BT_GATT_DISCOVER_SECONDARY; + }else if(disc_type == 2){ + discover_params.type = BT_GATT_DISCOVER_INCLUDE; + }else if(disc_type == 3){ + discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; + }else if(disc_type == 4){ + discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + }else{ + BT_WARN("Invalid discovery type\r\n"); + return; + } + //discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + BT_WARN("Discover failed (err %d)\r\n", err); + } else { + BT_WARN("Discover pending\r\n"); + } +} + +static struct bt_gatt_subscribe_params subscribe_params; + +static u8_t oad_client_recv_data(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, u16_t length) +{ + u8_t *pdata = data; + + if (!params->value) { + BT_WARN("Unsubscribed\r\n"); + params->value_handle = 0U; + return BT_GATT_ITER_STOP; + } + BT_WARN("oad_client_recv_data:%s\r\n",bt_hex(pdata,length)); + oad_client_notify_handler(data,length); + + return BT_GATT_ITER_CONTINUE; +} + +static void oad_subscribe(char *pcWriteBuffer, int xWriteBufferLen, int argc, char **argv) +{ + int err; + if (!default_conn) { + BT_WARN("Not connected\r\n"); + return; + } + + subscribe_params.ccc_handle = out_handle + 1; + subscribe_params.value_handle = out_handle; + subscribe_params.value = 0x01; + + subscribe_params.notify = oad_client_recv_data; + + err = bt_gatt_subscribe(default_conn, &subscribe_params); + if (err) { + BT_WARN("Subscribe failed (err %d)\r\n", err); + } else { + BT_WARN("Subscribed\r\n"); + } + +} + +void oad_client_notify_handler(void *buf,u16_t len) +{ + u8_t *pdata = (u8_t *)buf; + u8_t cmd_id ; + struct oad_image_identity_t identity; + struct oad_block_req_t req; + struct oad_upgrd_end_t upgrd_end; + + if(!buf){ + BT_WARN("BUF is NULL\r\n"); + return; + } + + cmd_id = pdata[0]; + switch(cmd_id){ + case OAD_CMD_IMAG_IDENTITY: + if(len == sizeof(struct oad_image_identity_t) + 1){ + memset(&identity,0,sizeof(struct oad_image_identity_t)); + memcpy(&identity,buf+1,sizeof(struct oad_image_identity_t)); + BT_WARN("manu_code = [0x%x] file ver = [0x%x]\r\n",identity.file_info.manu_code,identity.file_info.file_ver); + oad_image_identity_to_uart(&(identity.file_info)); + }else{ + BT_WARN("Length (%d) sizeof (%d) errors\r\n",len,sizeof(struct oad_image_identity_t)); + } + break; + + case OAD_CMD_IMAG_BLOCK_REQ: + if(len == sizeof(struct oad_block_req_t) + 1){ + memset(&req,0,sizeof(struct oad_block_req_t)); + memcpy(&req,buf+1,sizeof(struct oad_block_req_t)); + BT_WARN("OAD_CMD_IMAG_BLOCK_REQ: manu_code = [0x%x] file ver = [0x%x]\r\n",req.file_info.manu_code,req.file_info.file_ver); + oad_block_req_to_uart(&(req.file_info), req.file_offset, TRANSPORT_FILE_SIZE); + }else{ + BT_WARN("Length (%d) sizeof (%d) errors\r\n",len,sizeof(struct oad_block_req_t)); + } + break; + + case OAD_CMD_IMAG_UPGRD_END: + if(len == sizeof(struct oad_upgrd_end_t) + 1){ + memset(&upgrd_end,0,sizeof(struct oad_upgrd_end_t)); + memcpy(&upgrd_end,buf+1,sizeof(struct oad_upgrd_end_t)); + oad_upgrd_end_to_uart(upgrd_end.status, &(upgrd_end.file_info)); + }else{ + BT_WARN("Length (%d) sizeof (%d) errors\r\n",len,sizeof(struct oad_upgrd_end_t)); + } + break; + + default: + break; + + } +} + + +void oad_send_image_identity_to_servicer(struct bt_conn *conn,u8_t *data, u16_t len) +{ + u16_t handle = in_handle; + + if(!conn) + return; + + data[0] = OAD_CMD_IMAG_IDENTITY; + bt_gatt_write_without_response(conn, handle, data, len, false); +} + +void oad_send_block_resp_to_servicer(struct bt_conn *conn, u8_t *data, u8_t len) +{ + u16_t handle = in_handle; + u8_t slen = 0; + + if(!conn) + return; + + data[0] = OAD_CMD_IMAG_BLOCK_RESP; + if(len <= bt_gatt_get_mtu(conn)) + slen = len; + else{ + slen = bt_gatt_get_mtu(conn); + } + + bt_gatt_write_without_response(conn, handle, data, slen, false); +} + +void oad_cli_register(void) +{ + return; +} + diff --git a/components/ble/ble_stack/services/oad/oad_client.h b/components/ble/ble_stack/services/oad/oad_client.h new file mode 100644 index 00000000..132f9016 --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_client.h @@ -0,0 +1,50 @@ +#ifndef __BLE_OAD_H__ +#define __BLE_OAD_H__ +#include "gatt.h" +#include "Conn_internal.h" +#include "oad.h" + +enum +{ + OAD_CMDPROC_START = 0x00, + OAD_CMDPROC_IMAGE_IDENTITY, + OAD_CMDPROC_BLOCK_REQ, + OAD_CMDPROC_BLOCK_RESP, + OAD_CMDPROC_UPGRD_END, +}; + +struct oad_cmdproc_block_req_t +{ + struct oad_file_info file_info; + uint32_t file_offset; + uint8_t data_len; +}__packed; + +struct oad_cmdproc_upgrd_end_t +{ + uint8_t status; + struct oad_file_info file_info; +}__packed; + +struct oad_cmdproc_req_t +{ + uint8_t cmd_id; + union{ + struct oad_file_info file_info; + struct oad_cmdproc_block_req_t block_req; + struct oad_cmdproc_upgrd_end_t upgrd_end; + }q; +}__packed; + +enum +{ + CMDPROC_TYPE_OAD = 0x00, + CMDPROC_TYPE_MAX +}; + +void oad_client_notify_handler(void *buf,u16_t len); +void oad_send_image_identity_to_servicer(struct bt_conn *conn,u8_t *data, u16_t len); +void oad_send_block_resp_to_servicer(struct bt_conn *conn, u8_t *data, u8_t len); +void oad_cli_register(void); + +#endif diff --git a/components/ble/ble_stack/services/oad/oad_main.c b/components/ble/ble_stack/services/oad/oad_main.c new file mode 100644 index 00000000..7d8ac57f --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_main.c @@ -0,0 +1,400 @@ +#include +#include +#include +#include "oad_service.h" +#include "oad.h" +#include "oad_main.h" +#ifdef CONFIG_BT_SETTINGS +#include "settings.h" +#include "ef_def.h" +#endif +#include "conn_internal.h" +#include "hal_boot2.h" +#include "bl_flash.h" +#include "bl_sys.h" +#include "log.h" + +#define OTA_WRITE_FLASH_SIZE (256*16) +#define WBUF_SIZE(CON) (OTA_WRITE_FLASH_SIZE + bt_gatt_get_mtu(CON)) +#define UPGRD_TIMEOUT K_SECONDS(2) + +static app_check_oad_cb app_check_cb = NULL; +struct oad_env_tag oad_env; + +struct wflash_data_t{ + u8_t *wdata_buf; + u16_t index; +}__packed; + +static struct wflash_data_t wData; + +static bool check_data_valid(struct oad_file_info *file_info) +{ + if(file_info->manu_code != oad_env.file_info.manu_code || file_info->file_ver != oad_env.upgrd_file_ver) + return false; + + return true; +} + +static void oad_notify_image_info(struct bt_conn *conn) +{ + u8_t *buf = (u8_t*)k_malloc(sizeof(u8_t)*256); + u8_t index = 0; + char *build_date = __DATE__; + char *build_time = __TIME__; + char *build_ver = BL_SDK_VER; + + if(buf){ + memset(buf,0,256); + }else{ + BT_WARN("Buffer allocation failed\r\n"); + return; + } + buf[index++] = OAD_CMD_IMAG_INFO; + if(strlen(build_date)+index <= 256){ + buf[index] = strlen(build_date); + memcpy(&buf[++index],build_date,strlen(build_date)); + index += strlen(build_date); + }else{ + BT_WARN("No enough space\r\n"); + } + if(strlen(build_time)+index <= 256){ + buf[index] = strlen(build_time); + memcpy(&buf[++index],build_time,strlen(build_time)); + index += strlen(build_time); + }else{ + BT_WARN("No enough space\r\n"); + } + + if(strlen(build_ver)+index <= 256){ + buf[index] = strlen(build_ver); + memcpy(&buf[++index],build_ver,strlen(build_ver)); + index += strlen(build_ver); + }else{ + BT_WARN("No enough space\r\n"); + } + BT_WARN("Info:%s,%s,%s\r\n",build_date,build_time,build_ver); + BT_WARN("Send:%s\r\n",bt_hex(buf,index)); + bt_oad_notify(conn, buf, index); + k_free(buf); +} + +static void oad_notify_block_req(struct bt_conn *conn) +{ + struct net_buf_simple *buf = NET_BUF_SIMPLE(sizeof(struct oad_block_req_t) + OAD_OPCODE_SIZE); + struct oad_block_req_t *block_req; + + net_buf_simple_init(buf, 0); + *(buf->data) = OAD_CMD_IMAG_BLOCK_REQ; + block_req = (struct oad_block_req_t *)(buf->data+1); + buf->len = sizeof(struct oad_block_req_t) + OAD_OPCODE_SIZE; + + block_req->file_info.file_ver = oad_env.upgrd_file_ver; + block_req->file_info.manu_code = oad_env.file_info.manu_code; + block_req->file_offset = oad_env.upgrd_offset; + + bt_oad_notify(conn, buf->data, buf->len); +} + +static void oad_notify_upgrd_end(struct bt_conn *conn, u8_t status) +{ + struct net_buf_simple *buf = NET_BUF_SIMPLE(sizeof(struct oad_upgrd_end_t) + OAD_OPCODE_SIZE); + struct oad_upgrd_end_t *upgrd_end; + + if(status == OAD_SUCC) + { + BT_WARN("Submit upgrade work\r\n"); + k_delayed_work_submit(&oad_env.upgrd_work, UPGRD_TIMEOUT); + } + + net_buf_simple_init(buf, 0); + *(buf->data) = OAD_CMD_IMAG_UPGRD_END; + upgrd_end = (struct oad_upgrd_end_t *)(buf->data+1); + buf->len = sizeof(struct oad_upgrd_end_t) + OAD_OPCODE_SIZE; + upgrd_end->file_info.file_ver = oad_env.upgrd_file_ver; + upgrd_end->file_info.manu_code = oad_env.file_info.manu_code; + upgrd_end->status = status; + + bt_oad_notify(conn, buf->data, buf->len); +} + +static void oad_notity_image_identity(struct bt_conn *conn) +{ + struct net_buf_simple *buf = NET_BUF_SIMPLE(sizeof(struct oad_image_identity_t)); + struct oad_image_identity_t *identity; + + net_buf_simple_init(buf, 0); + *(buf->data) = OAD_CMD_IMAG_IDENTITY; + identity = (void *)(buf->data+1); + buf->len = sizeof(struct oad_image_identity_t)+ OAD_OPCODE_SIZE; + + identity->file_info.file_ver = oad_env.file_info.file_ver; + identity->file_info.manu_code = oad_env.file_info.manu_code; + identity->file_size = oad_env.cur_file_size; + identity->crc32 = 0; + + bt_oad_notify(conn, buf->data, buf->len); +} + +void oad_upgrade(struct k_work *work) +{ + int ret = 0; + HALPartition_Entry_Config ptEntry; + + BT_WARN("oad_upgrade\r\n"); + oad_env.file_info.file_ver = oad_env.upgrd_file_ver; + #if defined(CONFIG_BT_SETTINGS) + + struct oad_ef_info ef_info; + memset(&ef_info,0,sizeof(struct oad_ef_info)); + bt_settings_set_bin(NV_IMG_info, (uint8_t*)&ef_info, sizeof(struct oad_ef_info)); + + #endif + + ret=hal_boot2_get_active_entries_byname((uint8_t*)"FW",&ptEntry); + if(ret){ + BT_WARN("Failed to get active entries by name\r\n"); + return; + } + + ptEntry.len = oad_env.upgrd_file_size; + hal_boot2_update_ptable(&ptEntry); + bl_sys_reset_system(); + +} + +static u8_t oad_write_flash(u8_t *data, u16_t len) +{ + uint32_t size = 0; + uint32_t wflash_address = 0; + + if (!oad_env.new_img_addr){ + if (hal_boot2_partition_addr_inactive("FW",(uint32_t *)&oad_env.new_img_addr,&size)){ + BT_WARN("New img address is null\r\n"); + return OAD_ABORT; + } + + BT_WARN("Upgrade file size is %d\r\n", oad_env.upgrd_file_size); + if(oad_env.upgrd_file_size <= size){ + bl_flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); + }else{ + return -1; + } + } + + BT_WARN("upgrd_offset is 0x%x len (%d)\r\n",oad_env.upgrd_offset,len); + + if(oad_env.w_img_end_addr <= oad_env.new_img_addr && !oad_env.w_img_end_addr ){ + wflash_address = oad_env.new_img_addr; + }else if(oad_env.w_img_end_addr){ + wflash_address = oad_env.w_img_end_addr; + }else{ + BT_WARN("Write flash address invalid\r\n"); + } + + BT_WARN("Start address : 0x%x\r\n",wflash_address); + bl_flash_write(wflash_address, data, len); + oad_env.w_img_end_addr = wflash_address + len; + BT_WARN("End address : 0x%x\r\n",wflash_address + len); + + return 0; +} + + + +static u8_t oad_image_data_handler(struct bt_conn *conn,const u8_t *data, u16_t len) +{ + u16_t left_size = 0; + u16_t wlen = 0; +#if defined(CONFIG_BT_SETTINGS) + static u32_t write_count = 0; +#endif + if(!wData.wdata_buf){ + wData.wdata_buf = (u8_t*)k_malloc(WBUF_SIZE(conn)); + if(!wData.wdata_buf){ + BT_WARN("Buf is NULL\r\n"); + return OAD_ABORT; + }; + memset(wData.wdata_buf,0,WBUF_SIZE(conn)); + wData.index = 0; + } + + if(wData.wdata_buf){ + left_size = WBUF_SIZE(conn) - wData.index; + BT_WARN("left_size (0x%x) wData.index (0x%x) len (%d)\r\n",left_size,wData.index,len); + if(left_size >= len ){ + memcpy((wData.wdata_buf+wData.index),data,len); + wData.index += len; + + if(wData.index >= OTA_WRITE_FLASH_SIZE && wData.index <= WBUF_SIZE(conn)){ + wlen = wData.index - OTA_WRITE_FLASH_SIZE; + oad_write_flash(wData.wdata_buf,OTA_WRITE_FLASH_SIZE); +#if defined(CONFIG_BT_SETTINGS) + write_count += 1; + struct oad_ef_info ef_info; + memcpy(&ef_info.file_info,&oad_env.file_info,sizeof(struct oad_file_info)); + ef_info.file_offset = write_count * OTA_WRITE_FLASH_SIZE; + ef_info.last_wflash_addr = oad_env.w_img_end_addr; + ef_info.upgrd_crc32 = oad_env.upgrd_crc32; + + bt_settings_set_bin(NV_IMG_info, (uint8_t*)&ef_info, sizeof(struct oad_ef_info)); + BT_WARN("ef_info: file ver(%d) manu code(0x%x) file offset(0x%x) last_adder (0x%x)\r\n",ef_info.file_info.file_ver,ef_info.file_info.manu_code, + ef_info.file_offset,ef_info.last_wflash_addr); +#endif + wData.index = 0; + memcpy((wData.wdata_buf+wData.index),(wData.wdata_buf+OTA_WRITE_FLASH_SIZE),wlen); + wData.index += wlen; + } + + }else{ + BT_WARN("No space for store data\r\n"); + } + } + + oad_env.upgrd_offset += len; + if(oad_env.upgrd_offset > oad_env.upgrd_file_size){ + return OAD_INVALID_IMAG; + }else if(oad_env.upgrd_offset == oad_env.upgrd_file_size){ + if(wData.index) + oad_write_flash(wData.wdata_buf, wData.index); + + if(wData.wdata_buf){ + k_free(wData.wdata_buf); + wData.wdata_buf = NULL; + } + + return OAD_UPGRD_CMPLT; + }else{ + return OAD_REQ_MORE_DATA; + } +} + +static void oad_image_info_handler(struct bt_conn *conn, const u8_t *data, u16_t len) +{ + oad_notify_image_info(conn); +} + +static u8_t oad_image_block_resp_handler(struct bt_conn *conn, const u8_t *data, u16_t len) +{ + struct oad_block_rsp_t *block_rsp; + const u8_t *rsp_data; + u8_t status = OAD_SUCC; + + switch(*data){ + case OAD_SUCC: + { + block_rsp = (struct oad_block_rsp_t *)data; + if(!check_data_valid(&block_rsp->file_info)){ + status = OAD_INVALID_IMAG; + break; + } + + if(block_rsp->file_offset != oad_env.upgrd_offset){ + status = OAD_MALORMED_CMD; + break; + } + + rsp_data = data + OAD_BLK_RSP_DATA_OFFSET; + status = oad_image_data_handler(conn,rsp_data, block_rsp->data_size); + if(status == OAD_UPGRD_CMPLT){ + oad_notify_upgrd_end(conn, OAD_SUCC); + }else if(status == OAD_REQ_MORE_DATA){ + oad_notify_block_req(conn); + }else{ + oad_notify_upgrd_end(conn, status); + } + } + break; + case OAD_ABORT: + { + bl_flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); + } + break; + + default: + status = OAD_MALORMED_CMD; + + } + return status; +} + +static void oad_image_identity_handler(struct bt_conn *conn, const u8_t *data, u16_t len) +{ + struct oad_image_identity_t *identity = (struct oad_image_identity_t *)(data); + + BT_WARN("File size=[0x%x] [0x%x] [0x%x] [0x%x]\r\n",identity->file_size,identity->file_info.file_ver, + identity->file_info.manu_code,identity->crc32); +#if defined(CONFIG_BT_SETTINGS) + size_t llen = 0; + struct oad_ef_info ef_info; + + memset(&ef_info,0,sizeof(struct oad_ef_info)); + bt_settings_get_bin(NV_IMG_info, (uint8_t*)&ef_info,sizeof(struct oad_ef_info),&llen); + BT_WARN("ef_info: file ver(%d) manu code(0x%x) file offset(0x%x) last_adder (0x%x)\r\n",ef_info.file_info.file_ver,ef_info.file_info.manu_code, + ef_info.file_offset,ef_info.last_wflash_addr); +#endif + + if(identity->file_info.manu_code == oad_env.file_info.manu_code && + (app_check_cb)(oad_env.file_info.file_ver, identity->file_info.file_ver)){ + +#if defined(CONFIG_BT_SETTINGS) + if(identity->crc32 && ef_info.upgrd_crc32 == identity->crc32){ + if(ef_info.file_offset && ef_info.file_offset <= identity->file_size){ + oad_env.upgrd_offset = ef_info.file_offset; + } + + oad_env.new_img_addr = ef_info.last_wflash_addr; + + }else +#endif + { + oad_env.upgrd_offset = 0x00; + } + + oad_env.upgrd_file_ver = identity->file_info.file_ver; + oad_env.upgrd_file_size = identity->file_size; + oad_env.upgrd_crc32 = identity->crc32; + BT_WARN("Send the image block request\r\n"); + oad_notify_block_req(conn); + }else{ + + oad_notity_image_identity(conn); + } +} + +static void oad_recv_callback(struct bt_conn *conn, const u8_t *data, u16_t len) +{ + if (len){ + if (*data == OAD_CMD_IMAG_IDENTITY && ((len - 1) == sizeof(struct oad_image_identity_t))){ + oad_image_identity_handler(conn, data+1, len-1); + }if (*data == OAD_CMD_IMAG_BLOCK_RESP){ + oad_image_block_resp_handler(conn, data+1, len-1); + }if(*data == OAD_CMD_IMAG_INFO){ + oad_image_info_handler(conn, data+1, len-1); + } + } +} + +static void oad_disc_callback(struct bt_conn *conn,u8_t reason) +{ + if(wData.wdata_buf){ + k_free(wData.wdata_buf); + wData.wdata_buf = NULL; + wData.index = 0; + } +} + +void oad_service_enable(app_check_oad_cb cb) +{ + //todo: get current file info for oad_env.fileinfo + + app_check_cb = cb; + oad_env.file_info.file_ver = LOCAL_FILE_VER; + oad_env.file_info.manu_code = LOCAL_MANU_CODE; + oad_env.new_img_addr = 0; + bt_oad_service_enable(); + bt_oad_register_recv_cb(oad_recv_callback); + bt_oad_register_disc_cb(oad_disc_callback); + + k_delayed_work_init(&oad_env.upgrd_work, oad_upgrade); +} diff --git a/components/ble/ble_stack/services/oad/oad_main.h b/components/ble/ble_stack/services/oad/oad_main.h new file mode 100644 index 00000000..ad64a9fe --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_main.h @@ -0,0 +1,9 @@ +#ifndef __OAD_API_H__ +#define __OAD_API_H__ + +#include + +typedef bool (*app_check_oad_cb)(u32_t cur_file_ver, u32_t new_file_ver); +void oad_service_enable(app_check_oad_cb cb); +void oad_upgrade(struct k_work *work); +#endif //__OAD_API_H__ \ No newline at end of file diff --git a/components/ble/ble_stack/services/oad/oad_service.c b/components/ble/ble_stack/services/oad/oad_service.c new file mode 100644 index 00000000..a68808a2 --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_service.c @@ -0,0 +1,83 @@ +#include "byteorder.h" +#include "oad_service.h" +#include "oad.h" + +oad_upper_recv_cb upper_recv_cb; +oad_disc_cb disc_cb; +struct bt_conn *ble_oad_conn = NULL; + +static void ble_oad_connected(struct bt_conn *conn, u8_t err); +static void ble_oad_disconnected(struct bt_conn *conn, u8_t reason); + +static struct bt_conn_cb ble_oad_conn_callbacks = { + .connected = ble_oad_connected, + .disconnected = ble_oad_disconnected, +}; +static void ble_oad_connected(struct bt_conn *conn, u8_t err) +{ + if(!ble_oad_conn){ + ble_oad_conn = conn; + } +} + +static void ble_oad_disconnected(struct bt_conn *conn, u8_t reason) +{ + if(conn == ble_oad_conn){ + (disc_cb)(conn,reason); + } +} + +static int oad_recv(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags) +{ + (upper_recv_cb)(conn, buf, len); + + return 0; +} + +static void oad_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ + BT_WARN("oad ccc:value=[%d]\r\n",value); + +} + +static struct bt_gatt_attr oad_attrs[] = { + BT_GATT_PRIMARY_SERVICE(BT_UUID_OAD), + BT_GATT_CHARACTERISTIC(BT_UUID_OAD_DATA_IN, + BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_WRITE, NULL, oad_recv, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_OAD_DATA_OUT, + BT_GATT_CHRC_NOTIFY,BT_GATT_PERM_NONE, + NULL, NULL, NULL), + BT_GATT_CCC(oad_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + +}; + +static struct bt_gatt_service oad_svc = BT_GATT_SERVICE(oad_attrs); + +void bt_oad_notify(struct bt_conn *conn, const void *data, u16_t len) +{ + bt_gatt_notify(conn, &oad_attrs[3], data, len); +} + +void bt_oad_register_recv_cb(oad_upper_recv_cb cb) +{ + upper_recv_cb = cb; +} + +void bt_oad_register_disc_cb(oad_disc_cb cb) +{ + disc_cb = cb; +} + +void bt_oad_service_enable(void) +{ + bt_gatt_service_register(&oad_svc); + bt_conn_cb_register(&ble_oad_conn_callbacks); +} + +void bt_oad_service_disable(void) +{ + bt_gatt_service_unregister(&oad_svc); +} diff --git a/components/ble/ble_stack/services/oad/oad_service.h b/components/ble/ble_stack/services/oad/oad_service.h new file mode 100644 index 00000000..722d563b --- /dev/null +++ b/components/ble/ble_stack/services/oad/oad_service.h @@ -0,0 +1,14 @@ +#ifndef __OAD_SERVICE_H__ +#define __OAD_SERVICE_H__ + +#include "types.h" +#include "gatt.h" + +typedef void (*oad_upper_recv_cb)(struct bt_conn *conn, const u8_t *data, u16_t len); +typedef void (*oad_disc_cb)(struct bt_conn *conn,u8_t reason); +void bt_oad_register_recv_cb(oad_upper_recv_cb cb); +void bt_oad_register_disc_cb(oad_disc_cb cb); +void bt_oad_service_enable(void); +void bt_oad_servkce_disable(void); +void bt_oad_notify(struct bt_conn *conn, const void *data, u16_t len); +#endif //__OAD_SERVICE_H__ \ No newline at end of file diff --git a/components/ble/ble_stack/services/scps.c b/components/ble/ble_stack/services/scps.c new file mode 100644 index 00000000..1386c382 --- /dev/null +++ b/components/ble/ble_stack/services/scps.c @@ -0,0 +1,74 @@ +/** + **************************************************************************************** + * + * @file Scps.c + * + * @brief Bouffalo Lab GATT Scan Parameters Service implementation + * + * Copyright (C) Bouffalo Lab 2019 + * + * History: 2019-08 crealted by llgong @ Shanghai + * + **************************************************************************************** + */ +#include "bluetooth.h" +#include "gatt.h" +#include "uuid.h" +#include "scps.h" +#include "byteorder.h" + +struct scan_intvl_win { + u16_t scan_intvl; + u16_t scan_win; +} __packed; + +static struct scan_intvl_win intvl_win = { + .scan_intvl = BT_GAP_SCAN_FAST_INTERVAL, + .scan_win = BT_GAP_SCAN_FAST_WINDOW, +}; + +static ssize_t scan_intvl_win_write(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + u16_t len, u16_t offset, u8_t flags) +{ + const u8_t *data = buf; + intvl_win.scan_intvl = sys_get_le16(data); + data += 2; + intvl_win.scan_win = sys_get_le16(data); + + return len; +} + +static struct bt_gatt_attr attrs[]= { + BT_GATT_PRIMARY_SERVICE(BT_UUID_SCPS), + BT_GATT_CHARACTERISTIC(BT_UUID_SCPS_SCAN_INTVL_WIN, + BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_NONE, NULL, NULL, + &intvl_win) +}; + +static struct bt_gatt_service scps = BT_GATT_SERVICE(attrs); + +bool scps_init(u16_t scan_intvl, u16_t scan_win) +{ + int err; + + if (scan_intvl < 0x0004 || scan_intvl > 0x4000) { + return false; + } + + if (scan_win < 0x0004 || scan_win > 0x4000) { + return false; + } + + if (scan_win > scan_intvl) { + return false; + } + + intvl_win.scan_intvl = scan_intvl; + intvl_win.scan_win = scan_win; + + err = bt_gatt_service_register(&scps); + + return err?false:true; +} diff --git a/components/ble/ble_stack/services/scps.h b/components/ble/ble_stack/services/scps.h new file mode 100644 index 00000000..ede76ec2 --- /dev/null +++ b/components/ble/ble_stack/services/scps.h @@ -0,0 +1,30 @@ +#ifndef INCLUDE_BLUETOOTH_SERVICES_SCPS_H_ +#define INCLUDE_BLUETOOTH_SERVICES_SCPS_H_ + +/** + * @brief Scan Parameters Service (SCPS) + * @defgroup bt_gatt_scps Scan Parameters Service (SCPS) + * @ingroup bluetooth + * @{ + * + * [Experimental] Users should note that the APIs can change + * as a part of ongoing development. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +bool scps_init(u16_t scan_itvl, u16_t scan_win); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif diff --git a/components/ble/blecontroller/ble_inc/ble_lib_api.h b/components/ble/blecontroller/ble_inc/ble_lib_api.h new file mode 100644 index 00000000..a712bc30 --- /dev/null +++ b/components/ble/blecontroller/ble_inc/ble_lib_api.h @@ -0,0 +1,84 @@ +#ifndef BLE_LIB_API_H_ +#define BLE_LIB_API_H_ + +#include +#include + +void ble_controller_init(uint8_t task_priority); +void ble_controller_deinit(void); +#if defined(CFG_BT_RESET) +void ble_controller_reset(void); +#endif +char * ble_controller_get_lib_ver(void); + +// return sleep duration, in unit of 1/32768s +// if 0, means not allow sleep +// if -1, means allow sleep, but there is no end of sleep interrupt (ble core deep sleep is not enabled) +int32_t ble_controller_sleep(void); +bool ble_controller_sleep_is_ongoing(void); + +void ble_controller_set_tx_pwr(int ble_tx_power); +void ble_rf_set_tx_channel(uint16_t tx_channel); + + +#if defined(CONFIG_BLE_MFG) +enum +{ + BLE_TEST_TX = 0x00, + BLE_TEST_RX, + BLE_TEST_RXTX, + BLE_TEST_END +}; + +///HCI LE Receiver Test Command parameters structure +struct le_rx_test_cmd +{ + ///RX frequency for Rx test + uint8_t rx_freq; +}; + +///HCI LE Transmitter Test Command parameters structure +struct le_tx_test_cmd +{ + ///TX frequency for Tx test + uint8_t tx_freq; + ///TX test data length + uint8_t test_data_len; + ///TX test payload type - see enum + uint8_t pk_payload_type; +}; + +struct le_enhanced_rx_test_cmd +{ + ///RX frequency for Rx test + uint8_t rx_freq; + ///RX PHY for Rx test + uint8_t rx_phy; + ///Modulation index: Assume transmitter will have a standard or stable modulation index + uint8_t modulation_index; +}; + +///HCI LE Enhanced Transmitter Test Command parameters structure +struct le_enhanced_tx_test_cmd +{ + ///TX frequency for Tx test + uint8_t tx_freq; + ///TX test data length + uint8_t test_data_len; + ///TX test payload type - see enum + uint8_t pk_payload_type; + ///TX PHY for Rx test + uint8_t tx_phy; +}; + +int le_rx_test_cmd_handler(uint16_t src_id, void *param, bool from_hci); +int le_tx_test_cmd_handler(uint16_t src_id, void *param, bool from_hci); +int le_test_end_cmd_handler(bool from_hci); +uint8_t le_get_direct_test_type(void); + +#if defined(CONFIG_BLE_MFG_HCI_CMD) +int reset_cmd_handler(void); +#endif +#endif + +#endif diff --git a/components/ble/blecontroller/ble_inc/hci_onchip.h b/components/ble/blecontroller/ble_inc/hci_onchip.h new file mode 100644 index 00000000..12f5be4e --- /dev/null +++ b/components/ble/blecontroller/ble_inc/hci_onchip.h @@ -0,0 +1,45 @@ +#ifndef HCI_ONCHIP_H_ +#define HCI_ONCHIP_H_ + +enum{ + BT_HCI_CMD, + BT_HCI_ACL_DATA, + BT_HCI_CMD_CMP_EVT, + BT_HCI_CMD_STAT_EVT, + BT_HCI_LE_EVT, + BT_HCI_EVT +}; + +typedef struct{ + uint16_t opcode; + uint8_t *params; + uint8_t param_len; +}bl_hci_cmd_struct; + +typedef struct { + /// connection handle + uint16_t conhdl; + /// broadcast and packet boundary flag + uint8_t pb_bc_flag; + /// length of the data + uint16_t len; + uint8_t* buffer; +}bl_hci_acl_data_tx; + +typedef struct{ + union{ + bl_hci_cmd_struct hci_cmd; + bl_hci_acl_data_tx acl_data; + }p; +}hci_pkt_struct; + +#if defined(OPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER) +typedef void (*bt_hci_recv_cb)(uint8_t pkt_type, uint16_t src_id, uint8_t *param, uint8_t param_len, void *rx_buf); +uint8_t bt_onchiphci_hanlde_rx_acl(void *param, uint8_t *host_buf_data); +#else +typedef void (*bt_hci_recv_cb)(uint8_t pkt_type, uint16_t src_id, uint8_t *param, uint8_t param_len); +#endif +uint8_t bt_onchiphci_interface_init(bt_hci_recv_cb cb); +int bt_onchiphci_send(uint8_t pkt_type, uint16_t dest_id, hci_pkt_struct *pkt); + +#endif diff --git a/components/ble/blecontroller/lib/libblecontroller.a b/components/ble/blecontroller/lib/libblecontroller.a new file mode 100644 index 00000000..fce1b457 Binary files /dev/null and b/components/ble/blecontroller/lib/libblecontroller.a differ