From 0c18c45490f2ccadbd3eb6d45135acc72e91d79e Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 19:19:02 +1100 Subject: [PATCH 001/211] Import BLE --- .../ble_stack/bl_hci_wrapper/bl_hci_wrapper.c | 323 + .../ble_stack/bl_hci_wrapper/bl_hci_wrapper.h | 26 + .../ble/ble_stack/common/atomic_c.c | 353 + .../components/ble/ble_stack/common/buf.c | 1137 +++ .../components/ble/ble_stack/common/dec.c | 33 + .../components/ble/ble_stack/common/dummy.c | 40 + .../components/ble/ble_stack/common/hex.c | 91 + .../ble/ble_stack/common/include/atomic.h | 444 + .../ble/ble_stack/common/include/errno.h | 132 + .../ble_stack/common/include/misc/__assert.h | 114 + .../ble_stack/common/include/misc/byteorder.h | 441 + .../ble/ble_stack/common/include/misc/dlist.h | 501 ++ .../ble_stack/common/include/misc/printk.h | 28 + .../ble/ble_stack/common/include/misc/slist.h | 471 + .../ble/ble_stack/common/include/misc/stack.h | 87 + .../ble/ble_stack/common/include/misc/util.h | 379 + .../common/include/misc/utils_string.h | 7 + .../ble/ble_stack/common/include/net/buf.h | 1542 ++++ .../ble/ble_stack/common/include/toolchain.h | 26 + .../common/include/toolchain/common.h | 193 + .../ble_stack/common/include/toolchain/gcc.h | 473 + .../ble_stack/common/include/toolchain/xcc.h | 51 + .../ble/ble_stack/common/include/work_q.h | 60 + .../ble_stack/common/include/zephyr/types.h | 30 + .../components/ble/ble_stack/common/log.c | 61 + .../components/ble/ble_stack/common/log.h | 142 + .../components/ble/ble_stack/common/poll.c | 240 + .../components/ble/ble_stack/common/rpa.c | 106 + .../components/ble/ble_stack/common/rpa.h | 16 + .../ble/ble_stack/common/tinycrypt/Kconfig | 116 + .../ble/ble_stack/common/tinycrypt/README | 71 + .../common/tinycrypt/include/tinycrypt/aes.h | 130 + .../tinycrypt/include/tinycrypt/cbc_mode.h | 151 + .../tinycrypt/include/tinycrypt/ccm_mode.h | 211 + .../tinycrypt/include/tinycrypt/cmac_mode.h | 194 + .../tinycrypt/include/tinycrypt/constants.h | 61 + .../tinycrypt/include/tinycrypt/ctr_mode.h | 108 + .../tinycrypt/include/tinycrypt/ctr_prng.h | 165 + .../common/tinycrypt/include/tinycrypt/ecc.h | 537 ++ .../tinycrypt/include/tinycrypt/ecc_dh.h | 131 + .../tinycrypt/include/tinycrypt/ecc_dsa.h | 139 + .../include/tinycrypt/ecc_platform_specific.h | 81 + .../common/tinycrypt/include/tinycrypt/hmac.h | 139 + .../tinycrypt/include/tinycrypt/hmac_prng.h | 164 + .../tinycrypt/include/tinycrypt/sha256.h | 129 + .../tinycrypt/include/tinycrypt/utils.h | 122 + .../common/tinycrypt/source/aes_decrypt.c | 183 + .../common/tinycrypt/source/aes_encrypt.c | 211 + .../common/tinycrypt/source/cbc_mode.c | 112 + .../common/tinycrypt/source/ccm_mode.c | 263 + .../common/tinycrypt/source/cmac_mode.c | 252 + .../common/tinycrypt/source/ctr_mode.c | 86 + .../common/tinycrypt/source/ctr_prng.c | 279 + .../ble_stack/common/tinycrypt/source/ecc.c | 923 ++ .../common/tinycrypt/source/ecc_dh.c | 198 + .../common/tinycrypt/source/ecc_dsa.c | 294 + .../tinycrypt/source/ecc_platform_specific.c | 103 + .../ble_stack/common/tinycrypt/source/hmac.c | 145 + .../common/tinycrypt/source/hmac_prng.c | 230 + .../common/tinycrypt/source/sha256.c | 241 + .../ble_stack/common/tinycrypt/source/utils.c | 74 + .../components/ble/ble_stack/common/utils.c | 132 + .../components/ble/ble_stack/common/work_q.c | 348 + .../ble/ble_stack/hci_onchip/hci_driver.c | 560 ++ .../ble/ble_stack/hci_onchip/hci_internal.h | 48 + .../components/ble/ble_stack/host/a2dp.c | 343 + .../ble/ble_stack/host/a2dp_internal.h | 12 + .../components/ble/ble_stack/host/at.c | 537 ++ .../components/ble/ble_stack/host/at.h | 118 + .../components/ble/ble_stack/host/att.c | 2450 ++++++ .../ble/ble_stack/host/att_internal.h | 265 + .../components/ble/ble_stack/host/avdtp.c | 716 ++ .../ble/ble_stack/host/avdtp_internal.h | 175 + .../ble/ble_stack/host/bl_host_assist.c | 363 + .../components/ble/ble_stack/host/conn.c | 2696 ++++++ .../ble/ble_stack/host/conn_internal.h | 341 + .../components/ble/ble_stack/host/crypto.c | 167 + .../components/ble/ble_stack/host/crypto.h | 8 + .../components/ble/ble_stack/host/ecc.h | 63 + .../components/ble/ble_stack/host/gatt.c | 4692 ++++++++++ .../ble/ble_stack/host/gatt_internal.h | 59 + .../components/ble/ble_stack/host/hci_core.c | 7762 +++++++++++++++++ .../components/ble/ble_stack/host/hci_core.h | 285 + .../components/ble/ble_stack/host/hci_ecc.c | 340 + .../components/ble/ble_stack/host/hci_ecc.h | 10 + .../components/ble/ble_stack/host/hfp_hf.c | 887 ++ .../ble/ble_stack/host/hfp_internal.h | 66 + .../components/ble/ble_stack/host/iso.c | 1111 +++ .../ble/ble_stack/host/iso_internal.h | 110 + .../components/ble/ble_stack/host/keys.c | 506 ++ .../components/ble/ble_stack/host/keys.h | 123 + .../components/ble/ble_stack/host/keys_br.c | 241 + .../components/ble/ble_stack/host/l2cap.c | 1966 +++++ .../components/ble/ble_stack/host/l2cap_br.c | 1574 ++++ .../ble/ble_stack/host/l2cap_internal.h | 340 + .../components/ble/ble_stack/host/monitor.c | 28 + .../components/ble/ble_stack/host/monitor.h | 102 + .../components/ble/ble_stack/host/multi_adv.c | 514 ++ .../components/ble/ble_stack/host/multi_adv.h | 66 + .../components/ble/ble_stack/host/rfcomm.c | 1745 ++++ .../ble/ble_stack/host/rfcomm_internal.h | 213 + .../components/ble/ble_stack/host/sdp.c | 2545 ++++++ .../ble/ble_stack/host/sdp_internal.h | 74 + .../components/ble/ble_stack/host/settings.c | 428 + .../components/ble/ble_stack/host/settings.h | 39 + .../components/ble/ble_stack/host/smp.h | 187 + .../components/ble/ble_stack/host/smp_null.c | 101 + .../components/ble/ble_stack/host/uuid.c | 139 + .../ble_stack/include/bluetooth/a2dp-codec.h | 73 + .../ble/ble_stack/include/bluetooth/a2dp.h | 133 + .../ble/ble_stack/include/bluetooth/addr.h | 101 + .../ble/ble_stack/include/bluetooth/att.h | 67 + .../ble/ble_stack/include/bluetooth/avdtp.h | 54 + .../ble_stack/include/bluetooth/bluetooth.h | 869 ++ .../ble/ble_stack/include/bluetooth/buf.h | 139 + .../ble/ble_stack/include/bluetooth/conn.h | 942 ++ .../ble/ble_stack/include/bluetooth/crypto.h | 77 + .../ble/ble_stack/include/bluetooth/gap.h | 85 + .../ble/ble_stack/include/bluetooth/gatt.h | 1391 +++ .../ble/ble_stack/include/bluetooth/hci_err.h | 62 + .../ble_stack/include/bluetooth/hci_host.h | 2624 ++++++ .../ble/ble_stack/include/bluetooth/hci_raw.h | 54 + .../ble/ble_stack/include/bluetooth/hci_vs.h | 318 + .../ble/ble_stack/include/bluetooth/hfp_hf.h | 166 + .../ble/ble_stack/include/bluetooth/iso.h | 249 + .../ble/ble_stack/include/bluetooth/l2cap.h | 393 + .../ble/ble_stack/include/bluetooth/rfcomm.h | 188 + .../ble/ble_stack/include/bluetooth/sdp.h | 611 ++ .../ble/ble_stack/include/bluetooth/uuid.h | 617 ++ .../include/drivers/bluetooth/hci_driver.h | 207 + .../components/ble/ble_stack/port/bl_port.c | 384 + .../ble/ble_stack/port/include/bl_port.h | 279 + .../ble/ble_stack/port/include/ble_config.h | 616 ++ .../ble/ble_stack/port/include/zephyr.h | 151 + .../components/ble/ble_stack/sbc/dec/alloc.c | 82 + .../ble/ble_stack/sbc/dec/bitalloc-sbc.c | 164 + .../ble/ble_stack/sbc/dec/bitalloc.c | 393 + .../ble/ble_stack/sbc/dec/bitstream-decode.c | 94 + .../ble/ble_stack/sbc/dec/decoder-oina.c | 137 + .../ble/ble_stack/sbc/dec/decoder-private.c | 254 + .../ble/ble_stack/sbc/dec/decoder-sbc.c | 468 + .../ble/ble_stack/sbc/dec/dequant.c | 211 + .../ble/ble_stack/sbc/dec/framing-sbc.c | 58 + .../ble/ble_stack/sbc/dec/framing.c | 376 + .../ble/ble_stack/sbc/dec/oi_assert.h | 84 + .../ble/ble_stack/sbc/dec/oi_bitstream.h | 121 + .../ble/ble_stack/sbc/dec/oi_bt_spec.h | 225 + .../ble/ble_stack/sbc/dec/oi_codec_sbc.h | 478 + .../ble_stack/sbc/dec/oi_codec_sbc_private.h | 234 + .../ble/ble_stack/sbc/dec/oi_codec_version.c | 60 + .../ble/ble_stack/sbc/dec/oi_common.h | 42 + .../ble/ble_stack/sbc/dec/oi_cpu_dep.h | 498 ++ .../ble/ble_stack/sbc/dec/oi_modules.h | 166 + .../ble/ble_stack/sbc/dec/oi_osinterface.h | 196 + .../ble/ble_stack/sbc/dec/oi_status.h | 572 ++ .../ble/ble_stack/sbc/dec/oi_stddefs.h | 252 + .../ble/ble_stack/sbc/dec/oi_string.h | 200 + .../ble/ble_stack/sbc/dec/oi_time.h | 188 + .../ble/ble_stack/sbc/dec/oi_utils.h | 362 + .../ble_stack/sbc/dec/readsamplesjoint.inc | 111 + .../ble_stack/sbc/dec/synthesis-8-generated.c | 161 + .../ble/ble_stack/sbc/dec/synthesis-dct8.c | 349 + .../ble/ble_stack/sbc/dec/synthesis-sbc.c | 524 ++ .../ble/ble_stack/sbc/enc/sbc_analysis.c | 1160 +++ .../ble/ble_stack/sbc/enc/sbc_dct.c | 241 + .../ble/ble_stack/sbc/enc/sbc_dct.h | 87 + .../ble/ble_stack/sbc/enc/sbc_dct_coeffs.c | 203 + .../sbc/enc/sbc_enc_bit_alloc_mono.c | 183 + .../ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c | 193 + .../ble/ble_stack/sbc/enc/sbc_enc_coeffs.c | 318 + .../ble_stack/sbc/enc/sbc_enc_func_declare.h | 56 + .../ble/ble_stack/sbc/enc/sbc_encoder.c | 321 + .../ble/ble_stack/sbc/enc/sbc_encoder.h | 207 + .../ble/ble_stack/sbc/enc/sbc_packing.c | 262 + .../ble/ble_stack/sbc/enc/sbc_types.h | 57 + .../components/ble/ble_stack/services/bas.c | 92 + .../components/ble/ble_stack/services/bas.h | 56 + .../ble/ble_stack/services/ble_tp_svc.c | 293 + .../ble/ble_stack/services/ble_tp_svc.h | 38 + .../components/ble/ble_stack/services/dis.c | 277 + .../components/ble/ble_stack/services/dis.h | 30 + .../components/ble/ble_stack/services/hog.c | 212 + .../components/ble/ble_stack/services/hog.h | 38 + .../ble/ble_stack/services/oad/oad.h | 92 + .../ble/ble_stack/services/oad/oad_client.h | 45 + .../ble/ble_stack/services/oad/oad_main.c | 625 ++ .../ble/ble_stack/services/oad/oad_main.h | 9 + .../ble/ble_stack/services/oad/oad_service.h | 14 + .../components/ble/ble_stack/services/scps.c | 74 + .../components/ble/ble_stack/services/scps.h | 30 + .../ble/blecontroller/ble_inc/ble_lib_api.h | 83 + .../ble/blecontroller/ble_inc/hci_onchip.h | 42 + source/Core/BSP/Pinecilv2/ble.c | 353 + 193 files changed, 72986 insertions(+) create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/atomic_c.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/buf.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dec.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dummy.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/atomic.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/errno.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/__assert.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/byteorder.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/dlist.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/printk.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/slist.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/stack.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/util.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/utils_string.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/net/buf.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/common.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/gcc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/xcc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/work_q.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/zephyr/types.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/poll.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/Kconfig create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/README create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/sha256.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/utils.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/utils.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/work_q.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_driver.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/ecc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_hf.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys_br.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp_internal.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp_null.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/uuid.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp-codec.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/addr.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/att.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/avdtp.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/bluetooth.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/buf.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/conn.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/crypto.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gap.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gatt.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_err.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_host.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_raw.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_vs.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hfp_hf.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/iso.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/l2cap.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/rfcomm.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/sdp.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/uuid.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/bl_port.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/bl_port.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/zephyr.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/alloc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitstream-decode.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-oina.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-private.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-sbc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/dequant.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing-sbc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_assert.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bitstream.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bt_spec.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_version.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_common.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_modules.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_osinterface.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_status.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_stddefs.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_string.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_time.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_utils.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-dct8.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-sbc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_analysis.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_packing.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_types.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_client.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_service.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.c create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/hci_onchip.h create mode 100644 source/Core/BSP/Pinecilv2/ble.c diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c new file mode 100644 index 0000000000..238b9b2023 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.c @@ -0,0 +1,323 @@ +/***************************************************************************************** +* +* @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" + +#define DATA_MSG_CNT 10 + +struct rx_msg_struct data_msg[DATA_MSG_CNT]; +struct k_queue msg_queue; +#if defined(BFLB_BLE_NOTIFY_ADV_DISCARDED) +extern void ble_controller_notify_adv_discarded(uint8_t *adv_bd_addr, uint8_t adv_type); +#endif + +struct rx_msg_struct *bl_find_valid_data_msg() +{ + struct rx_msg_struct empty_msg; + memset(&empty_msg, 0, sizeof(struct rx_msg_struct)); + + for (int i = 0; i < DATA_MSG_CNT; i++) { + if (!memcmp(&data_msg[i], &empty_msg, sizeof(struct rx_msg_struct))) { + return (data_msg + i); + } + } + + return NULL; +} + +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) { + //ble 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: + //bredr identify link id, according to dest_id + case BT_HCI_OP_READ_REMOTE_FEATURES: + case BT_HCI_OP_READ_REMOTE_EXT_FEATURES: + case BT_HCI_OP_READ_ENCRYPTION_KEY_SIZE: { + //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; + } + + 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); +} + +void bl_packet_to_host(uint8_t pkt_type, uint16_t src_id, uint8_t *param, uint8_t param_len, struct net_buf *buf) +{ + uint16_t tlt_len; + bool prio = true; + uint8_t nb_h2c_cmd_pkts = 0x01; + + uint8_t *buf_data = net_buf_tail(buf); + bt_buf_set_rx_adv(buf, false); + + 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 (param[0] == BT_HCI_EVT_LE_ADVERTISING_REPORT) { + bt_buf_set_rx_adv(buf, true); + } + 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); + tlt_len = bt_onchiphci_hanlde_rx_acl(param, buf_data); + 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); + } +} + +void bl_trigger_queued_msg() +{ + struct net_buf *buf = NULL; + struct rx_msg_struct *msg = NULL; + + do { + unsigned int lock = irq_lock(); + + if (k_queue_is_empty(&msg_queue)) { + break; + } + + if (bt_buf_get_rx_avail_cnt() <= CONFIG_BT_RX_BUF_RSV_COUNT) + break; + + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); + if (!buf) { + break; + } + + msg = k_fifo_get(&msg_queue, K_NO_WAIT); + + BT_ASSERT(msg); + + bl_packet_to_host(msg->pkt_type, msg->src_id, msg->param, msg->param_len, buf); + + irq_unlock(lock); + + if (msg->param) { + k_free(msg->param); + } + memset(msg, 0, sizeof(struct rx_msg_struct)); + + } while (buf); +} + +static void bl_onchiphci_rx_packet_handler(uint8_t pkt_type, uint16_t src_id, uint8_t *param, uint8_t param_len) +{ + struct net_buf *buf = NULL; + struct rx_msg_struct *rx_msg = NULL; + + if (pkt_type == BT_HCI_CMD_CMP_EVT || pkt_type == BT_HCI_CMD_STAT_EVT) { + buf = bt_buf_get_cmd_complete(K_FOREVER); + bl_packet_to_host(pkt_type, src_id, param, param_len, buf); + return; + } else if (pkt_type == BT_HCI_LE_EVT && param[0] == BT_HCI_EVT_LE_ADVERTISING_REPORT) { + if (bt_buf_get_rx_avail_cnt() <= CONFIG_BT_RX_BUF_RSV_COUNT) { + BT_INFO("Discard adv report."); +#if defined(BFLB_BLE_NOTIFY_ADV_DISCARDED) + ble_controller_notify_adv_discarded(¶m[4], param[2]); +#endif + return; + } + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); + if (buf) + bl_packet_to_host(pkt_type, src_id, param, param_len, buf); + return; + } else { + if (pkt_type != BT_HCI_ACL_DATA) { + /* Using the reserved buf (CONFIG_BT_RX_BUF_RSV_COUNT) firstly. */ + buf = bt_buf_get_rx(BT_BUF_ACL_IN, K_NO_WAIT); + if (buf) { + bl_packet_to_host(pkt_type, src_id, param, param_len, buf); + return; + } + } + + rx_msg = bl_find_valid_data_msg(); + } + + BT_ASSERT(rx_msg); + + rx_msg->pkt_type = pkt_type; + rx_msg->src_id = src_id; + rx_msg->param_len = param_len; + if (param_len) { + rx_msg->param = k_malloc(param_len); + memcpy(rx_msg->param, param, param_len); + } + + k_fifo_put(&msg_queue, rx_msg); + + bl_trigger_queued_msg(); +} + +uint8_t bl_onchiphci_interface_init(void) +{ + for (int i = 0; i < DATA_MSG_CNT; i++) { + memset(data_msg + i, 0, sizeof(struct rx_msg_struct)); + } + + k_queue_init(&msg_queue, DATA_MSG_CNT); + + return bt_onchiphci_interface_init(bl_onchiphci_rx_packet_handler); +} + +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); +} diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h new file mode 100644 index 0000000000..6d13fa9a09 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/bl_hci_wrapper/bl_hci_wrapper.h @@ -0,0 +1,26 @@ +#ifndef __BL_HCI_WRAPPER_H__ +#define __BL_HCI_WRAPPER_H__ + +#include "net/buf.h" +#include "bluetooth.h" + +struct rx_msg_struct { + uint8_t pkt_type; + uint16_t src_id; + uint8_t *param; + uint8_t param_len; +} __packed; + +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); +void bl_onchiphci_interface_deinit(void); +void bl_trigger_queued_msg(void); +int bl_onchiphci_send_2_controller(struct net_buf *buf); + +#endif //__BL_CONTROLLER_H__ \ No newline at end of file diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/atomic_c.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/atomic_c.c new file mode 100644 index 0000000000..25c27fb663 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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 +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/buf.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/buf.c new file mode 100644 index 0000000000..44bdbda0d6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/buf.c @@ -0,0 +1,1137 @@ +/* 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_BLE) +#if defined(BFLB_DYNAMIC_ALLOC_MEM) +#include "bl_port.h" +#endif +#include "bl_hci_wrapper.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 +extern struct net_buf_pool _net_buf_pool_list[]; +#endif //BFLB_DYNAMIC_ALLOC_MEM + +#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; + + k_lifo_init(&(buf_pool->free), buf_count); +} + +void net_buf_deinit(struct net_buf_pool *buf_pool) +{ + extern void bt_delete_queue(struct k_fifo * queue_to_del); + bt_delete_queue((struct k_fifo *)(&(buf_pool->free))); + + 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(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(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(BFLB_BLE) + if (pool == &hci_rx_pool) { + bl_trigger_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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dec.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dec.c new file mode 100644 index 0000000000..7c7f30c0a8 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dec.c @@ -0,0 +1,33 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dummy.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/dummy.c new file mode 100644 index 0000000000..db90bb43cd --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c new file mode 100644 index 0000000000..6f3c3c42d6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/atomic.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/atomic.h new file mode 100644 index 0000000000..6f771c730f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/atomic.h @@ -0,0 +1,444 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/errno.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/errno.h new file mode 100644 index 0000000000..5de315da96 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/errno.h @@ -0,0 +1,132 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/__assert.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/__assert.h new file mode 100644 index 0000000000..a8cc820465 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/byteorder.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/byteorder.h new file mode 100644 index 0000000000..e4180b34a0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/byteorder.h @@ -0,0 +1,441 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/dlist.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/dlist.h new file mode 100644 index 0000000000..74d7ecf117 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/dlist.h @@ -0,0 +1,501 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/printk.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/printk.h new file mode 100644 index 0000000000..6759105d98 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/printk.h @@ -0,0 +1,28 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/slist.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/slist.h new file mode 100644 index 0000000000..0a6386829f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/slist.h @@ -0,0 +1,471 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/stack.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/stack.h new file mode 100644 index 0000000000..0fce5d45e4 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/util.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/util.h new file mode 100644 index 0000000000..77c2958b7e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/util.h @@ -0,0 +1,379 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/utils_string.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/misc/utils_string.h new file mode 100644 index 0000000000..14b8715701 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/net/buf.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/net/buf.h new file mode 100644 index 0000000000..b1b58fba79 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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 "ble_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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain.h new file mode 100644 index 0000000000..3bea0dbc29 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/common.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/common.h new file mode 100644 index 0000000000..2b649dcb9d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/common.h @@ -0,0 +1,193 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/gcc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/gcc.h new file mode 100644 index 0000000000..7fd945fbca --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/gcc.h @@ -0,0 +1,473 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/xcc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/xcc.h new file mode 100644 index 0000000000..6ef926c8f1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/toolchain/xcc.h @@ -0,0 +1,51 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/work_q.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/work_q.h new file mode 100644 index 0000000000..6886cf7ad1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/work_q.h @@ -0,0 +1,60 @@ +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/zephyr/types.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/include/zephyr/types.h new file mode 100644 index 0000000000..3b07e3b1c1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.c new file mode 100644 index 0000000000..3a57907926 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.c @@ -0,0 +1,61 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h new file mode 100644 index 0000000000..1c426de1c8 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h @@ -0,0 +1,142 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/poll.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/poll.c new file mode 100644 index 0000000000..b97a4f798c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.c new file mode 100644 index 0000000000..231ca2215c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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 <../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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/rpa.h new file mode 100644 index 0000000000..dca68f155a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/Kconfig b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/Kconfig new file mode 100644 index 0000000000..1f62654e16 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/README b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/README new file mode 100644 index 0000000000..20cbc5ab73 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/aes.h new file mode 100644 index 0000000000..05c5a4aa9b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cbc_mode.h new file mode 100644 index 0000000000..50c750c39d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ccm_mode.h new file mode 100644 index 0000000000..fa80c8bf56 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/cmac_mode.h new file mode 100644 index 0000000000..5240c87e93 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/constants.h new file mode 100644 index 0000000000..6b82c47a3a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_mode.h new file mode 100644 index 0000000000..d994e3a737 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h new file mode 100644 index 0000000000..3ebdb543e7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ctr_prng.h @@ -0,0 +1,165 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h new file mode 100644 index 0000000000..efe8ff47a0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc.h @@ -0,0 +1,537 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dh.h new file mode 100644 index 0000000000..0ce0f44c6b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_dsa.h new file mode 100644 index 0000000000..355ee23947 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/ecc_platform_specific.h new file mode 100644 index 0000000000..e2c882356e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac.h new file mode 100644 index 0000000000..b8d913f982 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/hmac_prng.h new file mode 100644 index 0000000000..164db43ec4 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/sha256.h new file mode 100644 index 0000000000..c3eb4f7be0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h new file mode 100644 index 0000000000..34a6bc5511 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/include/tinycrypt/utils.h @@ -0,0 +1,122 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c new file mode 100644 index 0000000000..83719acc85 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_decrypt.c @@ -0,0 +1,183 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c new file mode 100644 index 0000000000..0cb47b84d0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/aes_encrypt.c @@ -0,0 +1,211 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c new file mode 100644 index 0000000000..b405d7d297 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cbc_mode.c @@ -0,0 +1,112 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c new file mode 100644 index 0000000000..ed94e75bb0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ccm_mode.c @@ -0,0 +1,263 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c new file mode 100644 index 0000000000..97e7dd34cb --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/cmac_mode.c @@ -0,0 +1,252 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c new file mode 100644 index 0000000000..c9808338b2 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_mode.c @@ -0,0 +1,86 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c new file mode 100644 index 0000000000..d3410bc57c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ctr_prng.c @@ -0,0 +1,279 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc.c new file mode 100644 index 0000000000..eb3fa22380 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc.c @@ -0,0 +1,923 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c new file mode 100644 index 0000000000..e2995ef2a5 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dh.c @@ -0,0 +1,198 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c new file mode 100644 index 0000000000..117e3d26dd --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_dsa.c @@ -0,0 +1,294 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c new file mode 100644 index 0000000000..42dfee828d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/ecc_platform_specific.c @@ -0,0 +1,103 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac.c new file mode 100644 index 0000000000..aee8a44e2d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac.c @@ -0,0 +1,145 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/hmac_prng.c new file mode 100644 index 0000000000..8ef3c0c2b1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/sha256.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/sha256.c new file mode 100644 index 0000000000..57952104c7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/sha256.c @@ -0,0 +1,241 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/utils.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/tinycrypt/source/utils.c new file mode 100644 index 0000000000..137702a868 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/utils.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/utils.c new file mode 100644 index 0000000000..bde3392652 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/utils.c @@ -0,0 +1,132 @@ +/***************************************************************************************** +* +* @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/work_q.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/work_q.c new file mode 100644 index 0000000000..f8f72ad6f3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/work_q.c @@ -0,0 +1,348 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_driver.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_driver.c new file mode 100644 index 0000000000..a992c5d1c6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_driver.c @@ -0,0 +1,560 @@ +/* + * 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 +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/hci_onchip/hci_internal.h new file mode 100644 index 0000000000..a9fa409907 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp.c new file mode 100644 index 0000000000..82901ed233 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp.c @@ -0,0 +1,343 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/a2dp_internal.h new file mode 100644 index 0000000000..08e8b76b4b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.c new file mode 100644 index 0000000000..189ff4b2a6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/at.h new file mode 100644 index 0000000000..7a0d7edabd --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att.c new file mode 100644 index 0000000000..d792f0b6e1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att.c @@ -0,0 +1,2450 @@ +/* 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; i < value_len; i++) { + BT_PTS("%02x", src[value_len - 1 - i]); + } + BT_PTS("]\r\n"); + + BT_PTS("start_handle = [0x%04x] end_handle = [0x%04x]\r\n", data.buf->data[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 CONFIG_BT_ATT_PREPARE_COUNT > 0 + if (att->prep_queue._queue.hdl) { + k_queue_free(&att->prep_queue._queue); + att->prep_queue._queue.hdl = NULL; + } +#endif + + 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) + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/att_internal.h new file mode 100644 index 0000000000..f591a8b895 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c new file mode 100644 index 0000000000..f7b203e00d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c @@ -0,0 +1,716 @@ +/* + * 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; + +static uint8_t tid; + +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; + struct bt_avdtp_single_sig_hdr *hdr; + + BT_DBG("tid = %d", tid); + + 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; + tid = 0; + } 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp_internal.h new file mode 100644 index 0000000000..e182689f4b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c new file mode 100644 index 0000000000..d62716d4b6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c @@ -0,0 +1,363 @@ +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c new file mode 100644 index 0000000000..86b90b3e00 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c @@ -0,0 +1,2696 @@ +/* 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; + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR && conn->err) { + if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { + atomic_clear_bit(bt_dev.flags, BT_DEV_ISCAN); + } + if (atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) { + atomic_clear_bit(bt_dev.flags, BT_DEV_PSCAN); + } + } +#endif + + 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; + +#if defined(CONFIG_BT_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { + atomic_clear_bit(bt_dev.flags, BT_DEV_ISCAN); + } + if (atomic_test_bit(bt_dev.flags, BT_DEV_PSCAN)) { + atomic_clear_bit(bt_dev.flags, BT_DEV_PSCAN); + } + } +#endif + + 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); +} + +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 + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn_internal.h new file mode 100644 index 0000000000..9fbaa0d620 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn_internal.h @@ -0,0 +1,341 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.c new file mode 100644 index 0000000000..b1eeb8390e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/crypto.h new file mode 100644 index 0000000000..7b3aa0f7df --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/ecc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/ecc.h new file mode 100644 index 0000000000..70908c7faf --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c new file mode 100644 index 0000000000..a9c4d9169e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c @@ -0,0 +1,4692 @@ +/* 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; i < len; i++) { + BT_PTS("%s:handle = [0x%04x], data[%d] = [0x%x]\r\n", __func__, + attr->handle, 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt_internal.h new file mode 100644 index 0000000000..2a0ffc80e5 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt_internal.h @@ -0,0 +1,59 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.c new file mode 100644 index 0000000000..f6bbf28ae1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.c @@ -0,0 +1,7762 @@ +/* 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, buf); + 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 */ + +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 + + 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); + + 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) + + bl_onchiphci_interface_deinit(); + + 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 !defined(BFLB_BLE) + 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; + } + } +#endif + + 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 || +#if !defined(BFLB_BLE) + param->interval_min < 0x0020 || +#endif + 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(enable); + 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; + + 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)); + memset(ext_ir, 0, 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.h new file mode 100644 index 0000000000..488e5311a9 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_core.h @@ -0,0 +1,285 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.c new file mode 100644 index 0000000000..e9170a8c98 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hci_ecc.h new file mode 100644 index 0000000000..dcf4cf9dec --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_hf.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_hf.c new file mode 100644 index 0000000000..a8c715a12c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_hf.c @@ -0,0 +1,887 @@ +/* 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) + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/hfp_internal.h new file mode 100644 index 0000000000..8fda8bf47f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c new file mode 100644 index 0000000000..7ab175d1a9 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c @@ -0,0 +1,1111 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso_internal.h new file mode 100644 index 0000000000..1157960ec3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.c new file mode 100644 index 0000000000..1988d7aa02 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.c @@ -0,0 +1,506 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys.h new file mode 100644 index 0000000000..8fd698e8f7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys_br.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/keys_br.c new file mode 100644 index 0000000000..f5cd4e19ed --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c new file mode 100644 index 0000000000..c40183868b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c @@ -0,0 +1,1966 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c new file mode 100644 index 0000000000..a282debca8 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c @@ -0,0 +1,1574 @@ +/* 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) + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_internal.h new file mode 100644 index 0000000000..41dcb2d668 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.c new file mode 100644 index 0000000000..af4ee9c2d3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.c @@ -0,0 +1,28 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/monitor.h new file mode 100644 index 0000000000..43177dc62a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.c new file mode 100644 index 0000000000..4a1418789f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.c @@ -0,0 +1,514 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.h new file mode 100644 index 0000000000..ff292f10ce --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/multi_adv.h @@ -0,0 +1,66 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c new file mode 100644 index 0000000000..67bf469a69 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c @@ -0,0 +1,1745 @@ +/* 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) + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm_internal.h new file mode 100644 index 0000000000..30a9399260 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp.c new file mode 100644 index 0000000000..4cebb5bcc2 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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) + 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp_internal.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/sdp_internal.h new file mode 100644 index 0000000000..c16f9de9b4 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.c new file mode 100644 index 0000000000..1eaffca7b7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/settings.h new file mode 100644 index 0000000000..401b58e80d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp.h new file mode 100644 index 0000000000..9e1eec8dff --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp_null.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/smp_null.c new file mode 100644 index 0000000000..e0b2f74bda --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/uuid.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/uuid.c new file mode 100644 index 0000000000..dee1350438 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp-codec.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp-codec.h new file mode 100644 index 0000000000..cc88b7aebe --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/a2dp.h new file mode 100644 index 0000000000..66ce6dc6f6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/addr.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/addr.h new file mode 100644 index 0000000000..768f3189c9 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/att.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/att.h new file mode 100644 index 0000000000..4e99cf2d84 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/att.h @@ -0,0 +1,67 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/avdtp.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/avdtp.h new file mode 100644 index 0000000000..72cc2c537b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/bluetooth.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/bluetooth.h new file mode 100644 index 0000000000..ec0a5d5204 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/bluetooth.h @@ -0,0 +1,869 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/buf.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/buf.h new file mode 100644 index 0000000000..072b95f0ca --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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(BFLB_BLE) +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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/conn.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/conn.h new file mode 100644 index 0000000000..98ba3c8c9c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/conn.h @@ -0,0 +1,942 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/crypto.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/crypto.h new file mode 100644 index 0000000000..38f7b9ae4e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gap.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gap.h new file mode 100644 index 0000000000..a2152c3668 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gap.h @@ -0,0 +1,85 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gatt.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gatt.h new file mode 100644 index 0000000000..709e8bc21e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/gatt.h @@ -0,0 +1,1391 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_err.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_err.h new file mode 100644 index 0000000000..b3b0e6bf29 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_host.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_host.h new file mode 100644 index 0000000000..5d79f000e3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_host.h @@ -0,0 +1,2624 @@ +/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_raw.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_raw.h new file mode 100644 index 0000000000..dba2d4af5f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_vs.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hci_vs.h new file mode 100644 index 0000000000..b077af8511 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hfp_hf.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/hfp_hf.h new file mode 100644 index 0000000000..e6da3ae87f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/iso.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/iso.h new file mode 100644 index 0000000000..b24988a8b1 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/iso.h @@ -0,0 +1,249 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/l2cap.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/l2cap.h new file mode 100644 index 0000000000..759344391e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/l2cap.h @@ -0,0 +1,393 @@ +/** @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 <../bluetooth/buf.h> +#include +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/rfcomm.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/rfcomm.h new file mode 100644 index 0000000000..3b47a49ed0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/sdp.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/sdp.h new file mode 100644 index 0000000000..34efe7ca18 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/sdp.h @@ -0,0 +1,611 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/uuid.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/uuid.h new file mode 100644 index 0000000000..dcfbc92eed --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/bluetooth/uuid.h @@ -0,0 +1,617 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h new file mode 100644 index 0000000000..2cb23d0619 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/include/drivers/bluetooth/hci_driver.h @@ -0,0 +1,207 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/bl_port.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/bl_port.c new file mode 100644 index 0000000000..ccc1568d0f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/bl_port.c @@ -0,0 +1,384 @@ +#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 +#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 + +int ble_rand() +{ +#if defined(CONFIG_HW_SEC_ENG_DISABLE) + extern long random(void); + return random(); +#else + return bl_rand(); +#endif +} + +#if defined(BL_MCU_SDK) +int bl_rand() +{ + unsigned int val; + int counter = 0; + int32_t ret = 0; + 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)(ble_rand() & 0xFF); + } +} + +void *k_malloc(size_t size) +{ + return pvPortMalloc(size); +} + +void k_free(void *buf) +{ + return vPortFree(buf); +} diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/bl_port.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/bl_port.h new file mode 100644 index 0000000000..266aa530c8 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/bl_port.h @@ -0,0 +1,279 @@ +#ifndef BL_PORT_H +#define BL_PORT_H +#if defined(BL_MCU_SDK) +#include "misc.h" +#endif +#include +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h new file mode 100644 index 0000000000..073c9401f8 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h @@ -0,0 +1,616 @@ +#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(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_OBSERVER) +#define BFLB_BLE_NOTIFY_ADV_DISCARDED +#endif +#if defined(__cplusplus) +} +#endif + +#endif /* BLE_CONFIG_H */ diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/zephyr.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/zephyr.h new file mode 100644 index 0000000000..8282ed76ce --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/zephyr.h @@ -0,0 +1,151 @@ +#ifndef ZEPHYR_H +#define ZEPHYR_H +#include +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/alloc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/alloc.c new file mode 100644 index 0000000000..8e59674b0c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c new file mode 100644 index 0000000000..6eedd26b59 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc-sbc.c @@ -0,0 +1,164 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc.c new file mode 100644 index 0000000000..7b8b4586f5 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitalloc.c @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitstream-decode.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/bitstream-decode.c new file mode 100644 index 0000000000..da20e31492 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-oina.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-oina.c new file mode 100644 index 0000000000..bc57ead003 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-oina.c @@ -0,0 +1,137 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-private.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-private.c new file mode 100644 index 0000000000..0c7ff1256e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-private.c @@ -0,0 +1,254 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-sbc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-sbc.c new file mode 100644 index 0000000000..ee3c451312 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/decoder-sbc.c @@ -0,0 +1,468 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/dequant.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/dequant.c new file mode 100644 index 0000000000..a134d45e77 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/dequant.c @@ -0,0 +1,211 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing-sbc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing-sbc.c new file mode 100644 index 0000000000..ec94375534 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing.c new file mode 100644 index 0000000000..d962eaa05d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/framing.c @@ -0,0 +1,376 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_assert.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_assert.h new file mode 100644 index 0000000000..54939c75f0 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_assert.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bitstream.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bitstream.h new file mode 100644 index 0000000000..886eb6e944 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bitstream.h @@ -0,0 +1,121 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bt_spec.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bt_spec.h new file mode 100644 index 0000000000..75776399d7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_bt_spec.h @@ -0,0 +1,225 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h new file mode 100644 index 0000000000..d8b7206b7a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc.h @@ -0,0 +1,478 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h new file mode 100644 index 0000000000..09b2efc66e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_sbc_private.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_version.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_codec_version.c new file mode 100644 index 0000000000..697ef102fa --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_common.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_common.h new file mode 100644 index 0000000000..39eda8f7e6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_common.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h new file mode 100644 index 0000000000..04d4126630 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_cpu_dep.h @@ -0,0 +1,498 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_modules.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_modules.h new file mode 100644 index 0000000000..90e998ce4d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_modules.h @@ -0,0 +1,166 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_osinterface.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_osinterface.h new file mode 100644 index 0000000000..dfd425a900 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_status.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_status.h new file mode 100644 index 0000000000..b2a3979544 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_status.h @@ -0,0 +1,572 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_stddefs.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_stddefs.h new file mode 100644 index 0000000000..8f210200c2 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_stddefs.h @@ -0,0 +1,252 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_string.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_string.h new file mode 100644 index 0000000000..68e6aad229 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_string.h @@ -0,0 +1,200 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_time.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_time.h new file mode 100644 index 0000000000..b1ef526131 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_time.h @@ -0,0 +1,188 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_utils.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_utils.h new file mode 100644 index 0000000000..764b2680db --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/oi_utils.h @@ -0,0 +1,362 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/readsamplesjoint.inc new file mode 100644 index 0000000000..875a394975 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c new file mode 100644 index 0000000000..71ff6a9e08 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-8-generated.c @@ -0,0 +1,161 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-dct8.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-dct8.c new file mode 100644 index 0000000000..aa70d3bc3c --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-dct8.c @@ -0,0 +1,349 @@ +/****************************************************************************** + * + * 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 << bits))))) / ((float)(1 << bits))) +#define FLOAT_BUTTERFLY(x, y) \ + x += y; \ + y = x - (y * 2); \ + OI_ASSERT(VALID_INT32(x)); \ + OI_ASSERT(VALID_INT32(y)); +#define FLOAT_MULT_DCT(K, sample) (FIX(K, 20) * sample) +#define FLOAT_SCALE(x, y) (((x) / (double)(1 << (y)))) + + double L00, L01, L02, L03, L04, L05, L06, L07; + double L25; + + double in0, in1, in2, in3; + double in4, in5, in6, in7; + + in0 = FLOAT_SCALE(in[0], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in0)); + in1 = FLOAT_SCALE(in[1], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in1)); + in2 = FLOAT_SCALE(in[2], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in2)); + in3 = FLOAT_SCALE(in[3], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in3)); + in4 = FLOAT_SCALE(in[4], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in4)); + in5 = FLOAT_SCALE(in[5], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in5)); + in6 = FLOAT_SCALE(in[6], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in6)); + in7 = FLOAT_SCALE(in[7], DCTII_8_SHIFT_IN); + OI_ASSERT(VALID_INT32(in7)); + + L00 = (in0 + in7); + OI_ASSERT(VALID_INT32(L00)); + L01 = (in1 + in6); + OI_ASSERT(VALID_INT32(L01)); + L02 = (in2 + in5); + OI_ASSERT(VALID_INT32(L02)); + L03 = (in3 + in4); + OI_ASSERT(VALID_INT32(L03)); + + L04 = (in3 - in4); + OI_ASSERT(VALID_INT32(L04)); + L05 = (in2 - in5); + OI_ASSERT(VALID_INT32(L05)); + L06 = (in1 - in6); + OI_ASSERT(VALID_INT32(L06)); + L07 = (in0 - in7); + OI_ASSERT(VALID_INT32(L07)); + + FLOAT_BUTTERFLY(L00, L03); + FLOAT_BUTTERFLY(L01, L02); + + L02 += L03; + OI_ASSERT(VALID_INT32(L02)); + + L02 = FLOAT_MULT_DCT(AAN_C4_FLOAT, L02); + OI_ASSERT(VALID_INT32(L02)); + + FLOAT_BUTTERFLY(L00, L01); + + out[0] = (float)FLOAT_SCALE(L00, DCTII_8_SHIFT_0); + OI_ASSERT(VALID_INT16(out[0])); + out[4] = (float)FLOAT_SCALE(L01, DCTII_8_SHIFT_4); + OI_ASSERT(VALID_INT16(out[4])); + + FLOAT_BUTTERFLY(L03, L02); + out[6] = (float)FLOAT_SCALE(L02, DCTII_8_SHIFT_6); + OI_ASSERT(VALID_INT16(out[6])); + out[2] = (float)FLOAT_SCALE(L03, DCTII_8_SHIFT_2); + OI_ASSERT(VALID_INT16(out[2])); + + L04 += L05; + OI_ASSERT(VALID_INT32(L04)); + L05 += L06; + OI_ASSERT(VALID_INT32(L05)); + L06 += L07; + OI_ASSERT(VALID_INT32(L06)); + + L04 /= 2; + L05 /= 2; + L06 /= 2; + L07 /= 2; + + L05 = FLOAT_MULT_DCT(AAN_C4_FLOAT, L05); + OI_ASSERT(VALID_INT32(L05)); + + L25 = L06 - L04; + OI_ASSERT(VALID_INT32(L25)); + L25 = FLOAT_MULT_DCT(AAN_C6_FLOAT, L25); + OI_ASSERT(VALID_INT32(L25)); + + L04 = FLOAT_MULT_DCT(AAN_Q0_FLOAT, L04); + OI_ASSERT(VALID_INT32(L04)); + L04 -= L25; + OI_ASSERT(VALID_INT32(L04)); + + L06 = FLOAT_MULT_DCT(AAN_Q1_FLOAT, L06); + OI_ASSERT(VALID_INT32(L06)); + L06 -= L25; + OI_ASSERT(VALID_INT32(L25)); + + FLOAT_BUTTERFLY(L07, L05); + + FLOAT_BUTTERFLY(L05, L04); + out[3] = (float)(FLOAT_SCALE(L04, DCTII_8_SHIFT_3 - 1)); + OI_ASSERT(VALID_INT16(out[3])); + out[5] = (float)(FLOAT_SCALE(L05, DCTII_8_SHIFT_5 - 1)); + OI_ASSERT(VALID_INT16(out[5])); + + FLOAT_BUTTERFLY(L07, L06); + out[7] = (float)(FLOAT_SCALE(L06, DCTII_8_SHIFT_7 - 1)); + OI_ASSERT(VALID_INT16(out[7])); + out[1] = (float)(FLOAT_SCALE(L07, DCTII_8_SHIFT_1 - 1)); + OI_ASSERT(VALID_INT16(out[1])); +} +#undef BUTTERFLY +#endif + +/* + * This function calculates the AAN DCT. Its inputs are in S16.15 format, as + * returned by OI_SBC_Dequant. In practice, abs(in[x]) < 52429.0 / 1.38 + * (1244918057 integer). The function it computes is an approximation to the array defined + * by: + * + * diag(aan_s) * AAN= C2 + * + * or + * + * AAN = diag(1/aan_s) * C2 + * + * where C2 is as it is defined in the comment at the head of this file, and + * + * aan_s[i] = aan_s = 1/(2*cos(i*pi/16)) with i = 1..7, aan_s[0] = 1; + * + * aan_s[i] = [ 1.000 0.510 0.541 0.601 0.707 0.900 1.307 2.563 ] + * + * The output ranges are shown as follows: + * + * Let Y[0..7] = AAN * X[0..7] + * + * Without loss of generality, assume the input vector X consists of elements + * between -1 and 1. The maximum possible value of a given output element occurs + * with some particular combination of input vector elements each of which is -1 + * or 1. Consider the computation of Y[i]. Y[i] = sum t=0..7 of AAN[t,i]*X[i]. Y is + * maximized if the sign of X[i] matches the sign of AAN[t,i], ensuring a + * positive contribution to the sum. Equivalently, one may simply sum + * abs(AAN)[t,i] over t to get the maximum possible value of Y[i]. + * + * This yields approximately [8.00 10.05 9.66 8.52 8.00 5.70 4.00 2.00] + * + * Given the maximum magnitude sensible input value of +/-37992, this yields the + * following vector of maximum output magnitudes: + * + * [ 303936 381820 367003 323692 303936 216555 151968 75984 ] + * + * Ultimately, these values must fit into 16 bit signed integers, so they must + * be scaled. A non-uniform scaling helps maximize the kept precision. The + * relative number of extra bits of precision maintainable with respect to the + * largest value is given here: + * + * [ 0 0 0 0 0 0 1 2 ] + * + */ +PRIVATE void dct2_8(SBC_BUFFER_T *RESTRICT out, OI_INT32 const *RESTRICT in) +{ +#define BUTTERFLY(x, y) \ + x += y; \ + y = x - (y << 1); +#define FIX_MULT_DCT(K, x) (MUL_32S_32S_HI(K, x) << 2) + + OI_INT32 L00, L01, L02, L03, L04, L05, L06, L07; + OI_INT32 L25; + + OI_INT32 in0, in1, in2, in3; + OI_INT32 in4, in5, in6, in7; + +#if DCTII_8_SHIFT_IN != 0 + in0 = SCALE(in[0], DCTII_8_SHIFT_IN); + in1 = SCALE(in[1], DCTII_8_SHIFT_IN); + in2 = SCALE(in[2], DCTII_8_SHIFT_IN); + in3 = SCALE(in[3], DCTII_8_SHIFT_IN); + in4 = SCALE(in[4], DCTII_8_SHIFT_IN); + in5 = SCALE(in[5], DCTII_8_SHIFT_IN); + in6 = SCALE(in[6], DCTII_8_SHIFT_IN); + in7 = SCALE(in[7], DCTII_8_SHIFT_IN); +#else + in0 = in[0]; + in1 = in[1]; + in2 = in[2]; + in3 = in[3]; + in4 = in[4]; + in5 = in[5]; + in6 = in[6]; + in7 = in[7]; +#endif + + L00 = in0 + in7; + L01 = in1 + in6; + L02 = in2 + in5; + L03 = in3 + in4; + + L04 = in3 - in4; + L05 = in2 - in5; + L06 = in1 - in6; + L07 = in0 - in7; + + BUTTERFLY(L00, L03); + BUTTERFLY(L01, L02); + + L02 += L03; + + L02 = FIX_MULT_DCT(AAN_C4_FIX, L02); + + BUTTERFLY(L00, L01); + + out[0] = (OI_INT16)SCALE(L00, DCTII_8_SHIFT_0); + out[4] = (OI_INT16)SCALE(L01, DCTII_8_SHIFT_4); + + BUTTERFLY(L03, L02); + out[6] = (OI_INT16)SCALE(L02, DCTII_8_SHIFT_6); + out[2] = (OI_INT16)SCALE(L03, DCTII_8_SHIFT_2); + + L04 += L05; + L05 += L06; + L06 += L07; + + L04 /= 2; + L05 /= 2; + L06 /= 2; + L07 /= 2; + + L05 = FIX_MULT_DCT(AAN_C4_FIX, L05); + + L25 = L06 - L04; + L25 = FIX_MULT_DCT(AAN_C6_FIX, L25); + + L04 = FIX_MULT_DCT(AAN_Q0_FIX, L04); + L04 -= L25; + + L06 = FIX_MULT_DCT(AAN_Q1_FIX, L06); + L06 -= L25; + + BUTTERFLY(L07, L05); + + BUTTERFLY(L05, L04); + out[3] = (OI_INT16)SCALE(L04, DCTII_8_SHIFT_3 - 1); + out[5] = (OI_INT16)SCALE(L05, DCTII_8_SHIFT_5 - 1); + + BUTTERFLY(L07, L06); + out[7] = (OI_INT16)SCALE(L06, DCTII_8_SHIFT_7 - 1); + out[1] = (OI_INT16)SCALE(L07, DCTII_8_SHIFT_1 - 1); +#undef BUTTERFLY + +#ifdef DEBUG_DCT + { + float float_out[8]; + float_dct2_8(float_out, in); + } +#endif +} + +/**@}*/ +#endif /* #if defined(SBC_DEC_INCLUDED) */ diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-sbc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-sbc.c new file mode 100644 index 0000000000..e343cf3dbe --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/dec/synthesis-sbc.c @@ -0,0 +1,524 @@ +/****************************************************************************** + * + * 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, along with synthesis-generated.c, contains the synthesis +filterbank routines. The operations performed correspond to the +operations described in A2DP Appendix B, Figure 12.3. Several +mathematical optimizations are performed, particularly for the +8-subband case. + +One important optimization is to note that the "matrixing" operation +can be decomposed into the product of a type II discrete cosine kernel +and another, sparse matrix. + +According to Fig 12.3, in the 8-subband case, +@code + N[k][i] = cos((i+0.5)*(k+4)*pi/8), k = 0..15 and i = 0..7 +@endcode + +N can be factored as R * C2, where C2 is an 8-point type II discrete +cosine kernel given by +@code + C2[k][i] = cos((i+0.5)*k*pi/8)), k = 0..7 and i = 0..7 +@endcode + +R turns out to be a sparse 16x8 matrix with the following non-zero +entries: +@code + R[k][k+4] = 1, k = 0..3 + R[k][abs(12-k)] = -1, k = 5..15 +@endcode + +The spec describes computing V[0..15] as N * R. +@code + V[0..15] = N * R = (R * C2) * R = R * (C2 * R) +@endcode + +C2 * R corresponds to computing the discrete cosine transform of R, so +V[0..15] can be computed by taking the DCT of R followed by assignment +and selective negation of the DCT result into V. + + Although this was derived empirically using GNU Octave, it is + formally demonstrated in, e.g., Liu, Chi-Min and Lee, + Wen-Chieh. "A Unified Fast Algorithm for Cosine Modulated + Filter Banks in Current Audio Coding Standards." Journal of + the AES 47 (December 1999): 1061. + +Given the shift operation performed prior to computing V[0..15], it is +clear that V[0..159] represents a rolling history of the 10 most +recent groups of blocks input to the synthesis operation. Interpreting +the matrix N in light of its factorization into C2 and R, R's +sparseness has implications for interpreting the values in V. In +particular, there is considerable redundancy in the values stored in +V. Furthermore, since R[4][0..7] are all zeros, one out of every 16 +values in V will be zero regardless of the input data. Within each +block of 16 values in V, fully half of them are redundant or +irrelevant: + +@code + V[ 0] = DCT[4] + V[ 1] = DCT[5] + V[ 2] = DCT[6] + V[ 3] = DCT[7] + V[ 4] = 0 + V[ 5] = -DCT[7] = -V[3] (redundant) + V[ 6] = -DCT[6] = -V[2] (redundant) + V[ 7] = -DCT[5] = -V[1] (redundant) + V[ 8] = -DCT[4] = -V[0] (redundant) + V[ 9] = -DCT[3] + V[10] = -DCT[2] + V[11] = -DCT[1] + V[12] = -DCT[0] + V[13] = -DCT[1] = V[11] (redundant) + V[14] = -DCT[2] = V[10] (redundant) + V[15] = -DCT[3] = V[ 9] (redundant) +@endcode + +Since the elements of V beyond 15 were originally computed the same +way during a previous run, what holds true for V[x] also holds true +for V[x+16]. Thus, so long as care is taken to maintain the mapping, +we need only actually store the unique values, which correspond to the +output of the DCT, in some cases inverted. In fact, instead of storing +V[0..159], we could store DCT[0..79] which would contain a history of +DCT results. More on this in a bit. + +Going back to figure 12.3 in the spec, it should be clear that the +vector U need not actually be explicitly constructed, but that with +suitable indexing into V during the window operation, the same end can +be accomplished. In the same spirit of the pseudocode shown in the +figure, the following is the construction of W without using U: + +@code + for i=0 to 79 do + W[i] = D[i]*VSIGN(i)*V[remap_V(i)] where remap_V(i) = 32*(int(i/16)) + (i % 16) + (i % 16 >= 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_analysis.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_analysis.c new file mode 100644 index 0000000000..0a548ab8b3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_analysis.c @@ -0,0 +1,1160 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.c new file mode 100644 index 0000000000..a9b8baf776 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.c @@ -0,0 +1,241 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.h new file mode 100644 index 0000000000..78671b357d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_dct_coeffs.c new file mode 100644 index 0000000000..800a722a95 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c new file mode 100644 index 0000000000..06af17a400 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_mono.c @@ -0,0 +1,183 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c new file mode 100644 index 0000000000..5982461332 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_bit_alloc_ste.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_coeffs.c new file mode 100644 index 0000000000..29dee97904 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_enc_func_declare.h new file mode 100644 index 0000000000..1635b81a32 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.c new file mode 100644 index 0000000000..bcfdfa456a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.c @@ -0,0 +1,321 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.h new file mode 100644 index 0000000000..d706c34955 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_encoder.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_packing.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_packing.c new file mode 100644 index 0000000000..36adea7626 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_packing.c @@ -0,0 +1,262 @@ +/****************************************************************************** + * + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_types.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/sbc/enc/sbc_types.h new file mode 100644 index 0000000000..82eed43685 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.c new file mode 100644 index 0000000000..f4b34c022e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.h new file mode 100644 index 0000000000..2d16c052df --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/bas.h @@ -0,0 +1,56 @@ +/* + * 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c new file mode 100644 index 0000000000..4f27a55b01 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c @@ -0,0 +1,293 @@ +/**************************************************************************** +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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h new file mode 100644 index 0000000000..5071ad592d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h @@ -0,0 +1,38 @@ +/**************************************************************************** +FILE NAME + ble_tp_svc.h + +DESCRIPTION +NOTES +*/ +/****************************************************************************/ + +#ifndef _BLE_TP_SVC_H_ +#define _BLE_TP_SVC_H_ + +#include + +// 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.c new file mode 100644 index 0000000000..3e6e990358 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.c @@ -0,0 +1,277 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/dis.h new file mode 100644 index 0000000000..ee123593e3 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.c new file mode 100644 index 0000000000..14f3c9729d --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.c @@ -0,0 +1,212 @@ +/** @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" +#include "log.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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.h new file mode 100644 index 0000000000..c3746aaef2 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/hog.h @@ -0,0 +1,38 @@ +/** @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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad.h new file mode 100644 index 0000000000..dc76e3b76b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad.h @@ -0,0 +1,92 @@ +#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, + OAD_CHECK_HASH256_FAIL, +}; + +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; + + u32_t hosal_offset; +}; + +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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_client.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_client.h new file mode 100644 index 0000000000..54c11810da --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_client.h @@ -0,0 +1,45 @@ +#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c new file mode 100644 index 0000000000..d324eddbb2 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c @@ -0,0 +1,625 @@ +#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" +#if !defined(CONFIG_BL_MCU_SDK) +#include "hal_boot2.h" +#include "bl_flash.h" +#include "bl_sys.h" +#include "hosal_ota.h" +#include "bl702_common.h" +#include "hal_sys.h" +#else +#include "partition.h" +#include "hal_flash.h" +#include "errno.h" +#include "bl702_glb.h" +#include "mbedtls/sha256.h" +#endif +#include "log.h" +#include "bl702.h" +#include "softcrc.h" +#if defined(CONFIG_BL_MCU_SDK) +#define BL_SDK_VER "1.00" +#define BOOT2_PARTITION_ADDR (0x4202DC00) +#endif //CONFIG_BL_MCU_SDK + +#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; + +#if defined(CONFIG_BL_MCU_SDK) +static int hal_boot2_partition_addr_inactive(const char *name, uint32_t *addr, uint32_t *size); +static int hal_boot2_get_active_entries_byname(u8_t *name, pt_table_entry_config *ptEntry_hal); +static int hal_boot2_update_ptable(pt_table_entry_config *ptEntry_hal); +static int fw_check_hash256(void); +#endif +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"); +#if !defined(CONFIG_BL_MCU_SDK) + if (!hosal_ota_finish(1, 0)) +#else + if (!fw_check_hash256()) +#endif + k_delayed_work_submit(&oad_env.upgrd_work, UPGRD_TIMEOUT); + else { + BT_WARN("Hash256 check failed"); + status = OAD_CHECK_HASH256_FAIL; + } + } + + 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 ota_finish(struct k_work *work) +{ + 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 + +#if defined(CONFIG_BL_MCU_SDK) + GLB_SW_System_Reset(); +#else + hal_reboot(); +#endif +} + +static u8_t oad_write_flash(uint32_t filesize, uint32_t offset, u8_t *data, u16_t len) +{ +#if defined(CONFIG_BL_MCU_SDK) + 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) { + BT_WARN("flash erase\r\n"); + 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); + 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); +#else + hosal_ota_update(filesize, offset, data, len); +#endif + + 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 oDataLen = 0; + static u32_t write_count = 0; + + 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)*/ OTA_WRITE_FLASH_SIZE - 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; + + } else { + oDataLen = len - left_size; + memcpy((wData.wdata_buf + wData.index), data, left_size); + wData.index += left_size; + if (wData.index == OTA_WRITE_FLASH_SIZE) { + if (oad_write_flash(oad_env.upgrd_file_size, oad_env.hosal_offset, wData.wdata_buf, OTA_WRITE_FLASH_SIZE)) { + BT_ERR("Failed to write flash\r\n"); + return OAD_ABORT; + } + } else { + BT_ERR("Unexpect result\r\n"); + return OAD_ABORT; + } + + write_count += 1; + oad_env.hosal_offset = write_count * OTA_WRITE_FLASH_SIZE; +#if defined(CONFIG_BT_SETTINGS) + 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), (data + left_size), oDataLen); + wData.index += oDataLen; + } + } + + 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(oad_env.upgrd_file_size, oad_env.hosal_offset, 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: { +#if !defined(CONFIG_BL_MCU_SDK) + bl_flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); +#else + flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); +#endif + } 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 bt_le_conn_param conn_param; + struct oad_image_identity_t *identity = (struct oad_image_identity_t *)(data); + int err = 0; + + 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); +#else + oad_env.new_img_addr = 0; + oad_env.w_img_end_addr = 0; +#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; + } + + conn_param.interval_max = 6; + conn_param.interval_min = 6; + conn_param.latency = 0; + conn_param.timeout = 500; //5s + err = bt_conn_le_param_update(conn, &conn_param); + if (err) + BT_WARN("fail to start conn update\r\n"); + else + BT_WARN("start conn update\r\n"); + + 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"); +#if !defined(CONFIG_BL_MCU_SDK) + hosal_ota_start(oad_env.upgrd_file_size); +#endif + 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; + } +} +#if defined(CONFIG_BL_MCU_SDK) +static struct { + u8_t partition_active_idx; + u8_t pad[3]; + pt_table_stuff_config table; +} boot2_partition_table; + +//#define PARTITION_MAGIC (0x54504642) + +pt_table_id_type active_id = PT_TABLE_ID_INVALID; +pt_table_stuff_config pt_table_stuff[2]; + +static int fw_check_hash256(void) +{ + uint32_t bin_size; + uint32_t hash_addr; + pt_table_entry_config ptEntry; + + if (oad_env.upgrd_file_size <= 32) { + return -1; + } + if (oad_env.w_img_end_addr <= 32) { + return -1; + } + + bin_size = oad_env.upgrd_file_size - 32; + hash_addr = oad_env.w_img_end_addr - 32; + + if (hal_boot2_get_active_entries_byname((uint8_t *)"FW", &ptEntry)) { + BT_WARN("Failed to get active entries by name\r\n"); + return -1; + } + +#define CHECK_IMG_BUF_SIZE 512 + uint8_t sha_check[32] = { 0 }; + uint8_t dst_sha[32] = { 0 }; + uint32_t read_size; + mbedtls_sha256_context sha256_ctx; + int i, offset = 0; + uint8_t r_buf[CHECK_IMG_BUF_SIZE]; + + BT_WARN("[OTA]prepare OTA partition info\r\n"); + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts_ret(&sha256_ctx, 0); + + memset(sha_check, 0, 32); + memset(dst_sha, 0, 32); + offset = 0; + while (offset < bin_size) { + (bin_size - offset >= CHECK_IMG_BUF_SIZE) ? (read_size = CHECK_IMG_BUF_SIZE) : (read_size = bin_size - offset); + if (flash_read(oad_env.new_img_addr + offset, r_buf, read_size)) { + BT_WARN("flash read failed \r\n"); + return -1; + } + mbedtls_sha256_update_ret(&sha256_ctx, (const uint8_t *)r_buf, read_size); + offset += read_size; + } + + mbedtls_sha256_finish_ret(&sha256_ctx, sha_check); + + flash_read(hash_addr, dst_sha, 32); + for (i = 0; i < 32; i++) { + BT_WARN("%02X", dst_sha[i]); + } + puts("\r\nHeader SET SHA256 Checksum:"); + for (i = 0; i < 32; i++) { + BT_WARN("%02X", sha_check[i]); + } + + if (memcmp(sha_check, (const void *)dst_sha, 32) != 0) { + BT_WARN("sha256 check error\r\n"); + return -1; + } + ptEntry.len = bin_size; + BT_WARN("[OTA] Update PARTITION, partition len is %lu\r\n", ptEntry.len); + hal_boot2_update_ptable(&ptEntry); + + return 0; +} + +static int oad_hal_boot2_partition_addr(const char *name, uint32_t *addr0, uint32_t *addr1, uint32_t *size0, uint32_t *size1, int *active) +{ + int i; + + if (BFLB_PT_MAGIC_CODE != boot2_partition_table.table.pt_table.magicCode) { + return -EIO; + } + + /*Get Target partition*/ + for (i = 0; i < boot2_partition_table.table.pt_table.entryCnt; i++) { + if (0 == strcmp((char *)&(boot2_partition_table.table.pt_entries[i].name[0]), name)) { + break; + } + } + if (boot2_partition_table.table.pt_table.entryCnt == i) { + return -ENOENT; + } + *addr0 = boot2_partition_table.table.pt_entries[i].start_address[0]; + *addr1 = boot2_partition_table.table.pt_entries[i].start_address[1]; + *size0 = boot2_partition_table.table.pt_entries[i].max_len[0]; + *size1 = boot2_partition_table.table.pt_entries[i].max_len[1]; + *active = boot2_partition_table.table.pt_entries[i].active_index; + + return 0; +} + +static int hal_boot2_partition_addr_inactive(const char *name, uint32_t *addr, uint32_t *size) +{ + uint32_t addr0, addr1; + uint32_t size0, size1; + int active, ret; + + if ((ret = oad_hal_boot2_partition_addr(name, &addr0, &addr1, &size0, &size1, &active))) { + return ret; + } + *addr = active ? addr0 : addr1; + *size = active ? size0 : size1; + + return 0; +} + +static pt_table_error_type oad_PtTable_Get_Active_Entries_By_Name(pt_table_stuff_config *ptStuff, + u8_t *name, + pt_table_entry_config *ptEntry) +{ + uint32_t i = 0; + uint32_t len = strlen((char *)name); + + if (ptStuff == NULL || ptEntry == NULL) { + return PT_ERROR_PARAMETER; + } + for (i = 0; i < ptStuff->pt_table.entryCnt; i++) { + if (strlen((char *)ptStuff->pt_entries[i].name) == len && + memcmp((char *)ptStuff->pt_entries[i].name, (char *)name, len) == 0) { + ARCH_MemCpy_Fast(ptEntry, &ptStuff->pt_entries[i], sizeof(pt_table_entry_config)); + return PT_ERROR_SUCCESS; + } + } + return PT_ERROR_ENTRY_NOT_FOUND; +} + +static int hal_boot2_update_ptable(pt_table_entry_config *ptEntry_hal) +{ + int ret; + //FIXME force covert + pt_table_entry_config *ptEntry = (pt_table_entry_config *)ptEntry_hal; + + ptEntry->active_index = !ptEntry->active_index; + (ptEntry->age)++; + ret = pt_table_update_entry((pt_table_id_type)(!active_id), &pt_table_stuff[!active_id], ptEntry); + return ret; +} + +static int hal_boot2_get_active_entries_byname(uint8_t *name, pt_table_entry_config *ptEntry_hal) +{ + pt_table_entry_config *ptEntry = (pt_table_entry_config *)ptEntry_hal; + if (oad_PtTable_Get_Active_Entries_By_Name(&boot2_partition_table.table, name, ptEntry)) { + return -1; + } + return 0; +} + +static int oad_get_boot2_partition_table(void) +{ + memcpy(&boot2_partition_table.table, &pt_table_stuff[active_id], sizeof(pt_table_stuff_config)); + boot2_partition_table.partition_active_idx = active_id; + BT_WARN("magicCode: 0x%x\r\n", boot2_partition_table.table.pt_table.magicCode); + return 0; +} +#endif +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); + +#if defined(CONFIG_BL_MCU_SDK) + flash_init(); + pt_table_set_flash_operation(flash_erase, flash_write, flash_read); + active_id = pt_table_get_active_partition_need_lock(pt_table_stuff); + oad_get_boot2_partition_table(); +#endif + k_delayed_work_init(&oad_env.upgrd_work, ota_finish); +} diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.h new file mode 100644 index 0000000000..8bf60af46e --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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 ota_finish(struct k_work *work); +#endif //__OAD_API_H__ \ No newline at end of file diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_service.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_service.h new file mode 100644 index 0000000000..798eac78d4 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.c new file mode 100644 index 0000000000..67d4db3bcd --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/scps.h new file mode 100644 index 0000000000..ede76ec22f --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h new file mode 100644 index 0000000000..948d34eba6 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h @@ -0,0 +1,83 @@ +#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); + +// if 0, success. +// if -1, fail, +int8_t ble_controller_set_scan_filter_table_size(uint8_t size); + +// 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); +void ble_controller_sleep_restore(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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/hci_onchip.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/hci_onchip.h new file mode 100644 index 0000000000..e9f6246b8a --- /dev/null +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/hci_onchip.h @@ -0,0 +1,42 @@ +#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; + +typedef void (*bt_hci_recv_cb)(uint8_t pkt_type, uint16_t src_id, uint8_t *param, uint8_t param_len); + +uint8_t bt_onchiphci_interface_init(bt_hci_recv_cb cb); +int8_t bt_onchiphci_send(uint8_t pkt_type, uint16_t dest_id, hci_pkt_struct *pkt); +uint8_t bt_onchiphci_hanlde_rx_acl(void *param, uint8_t *host_buf_data); + +#endif diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c new file mode 100644 index 0000000000..bdde265394 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -0,0 +1,353 @@ +/**************************************************************************** +FILE NAME + ble_peripheral_tp_server.c + +DESCRIPTION + test profile demo + +NOTES +*/ +/****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bluetooth.h" +#include "conn.h" +#include "gatt.h" +#include "hci_core.h" +#include "uuid.h" +#include "ble_peripheral_tp_server.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); + +static struct bt_conn *ble_tp_conn; +#if !defined(CONFIG_BT_OAD_SERVER) +static struct bt_gatt_exchange_params exchg_mtu; +#endif +static TaskHandle_t ble_tp_task_h; + +static struct k_sem notify_poll_sem; + +static int tx_mtu_size = 20; +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, +}; + +#if !defined(CONFIG_BT_OAD_SERVER) +/************************************************************************* +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", tx_mtu_size); + } else { + BT_WARN("ble tp echange mtu size failure, err: %d", err); + } +} +#endif +/************************************************************************* +NAME + ble_tp_connected +*/ +static void ble_tp_connected(struct bt_conn *conn, u8_t err) +{ + #if !defined(CONFIG_BT_OAD_SERVER) + int tx_octets = 0x00fb; + int tx_time = 0x0848; + int ret = -1; + #endif + + if (err) { + return; + } + + BT_WARN("Tp connected"); + ble_tp_conn = conn; + + #if !defined(CONFIG_BT_OAD_SERVER) + //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"); + } else { + BT_WARN("ble tp set data length failure, err: %d", 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"); + } else { + BT_WARN("ble tp exchange mtu size failure, err: %d", ret); + } + #endif +} + +/************************************************************************* +NAME + ble_tp_disconnected +*/ +static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason) +{ + BT_WARN("Tp disconnected"); + + if (created_tp_task) { + BT_WARN("Delete throughput tx task"); + vTaskDelete(ble_tp_task_h); + created_tp_task = 0; + } + + 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(receive data from client) +*/ +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_WARN("recv data len=%d, offset=%d, flag=%d", len, offset, flags); + BT_WARN("recv data:%s", bt_hex(buf, len)); + + if (flags & BT_GATT_WRITE_FLAG_PREPARE) { + //Don't use prepare write data, execute write will upload data again. + BT_WARN("recv prepare write request"); + return 0; + } + + if (flags & BT_GATT_WRITE_FLAG_CMD) { + //Use write command data. + BT_WARN("recv write command"); + } else { + //Use write request / execute write data. + BT_WARN("recv write request / exce write"); + } + + k_sem_give(¬ify_poll_sem); + return len; +} + +/************************************************************************* +NAME + indicate_rsp /bl_tp_send_indicate +*/ + +static void indicate_rsp(struct bt_conn *conn, const struct bt_gatt_attr *attr, u8_t err) +{ + BT_WARN("receive comfirmation, err:%d", err); +} + +static int bl_tp_send_indicate(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, u16_t len) +{ + 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", err); + } +} + +/************************************************************************* +NAME + ble_tp_notify(send data to client) +*/ +static void ble_tp_notify_task(void *pvParameters) +{ + int err = -1; + u8_t data[244] = { 0x01 }; + k_sem_give(¬ify_poll_sem); + + while (1) { + k_sem_take(¬ify_poll_sem, K_FOREVER); + //send data to client + err = bt_gatt_notify(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX), data, (tx_mtu_size - 3)); + data[0] = data[0] + 1; + BT_WARN("ble tp send notify : %d", err); + } +} + +/************************************************************************* +NAME + ble_tp_not_ccc_changed +*/ +static void ble_tp_notify_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ + BT_WARN("ccc:value=[%d]", value); + + if (value == BT_GATT_CCC_NOTIFY) { + + if (xTaskCreate(ble_tp_notify_task, (char *)"bletp", 512, NULL, 15, &ble_tp_task_h) == pdPASS) { + created_tp_task = 1; + BT_WARN("Create throughput tx task success"); + } else { + created_tp_task = 0; + BT_WARN("Create throughput tx taskfail"); + } + } else { + if (created_tp_task) { + BT_WARN("Delete throughput tx task"); + 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, + 0, + 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, + 0, + NULL, + NULL, + NULL), + + BT_GATT_CCC(ble_tp_notify_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]; +} + +static 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); + k_sem_init(¬ify_poll_sem, 0, 1); + } +} + + + +int ble_start_adv(void) +{ + struct bt_le_adv_param adv_param = { + //options:3, connectable undirected, adv one time + .options = 3, + .interval_min = BT_GAP_ADV_FAST_INT_MIN_3, + .interval_max = BT_GAP_ADV_FAST_INT_MAX_3, + }; + + char *adv_name = "BL_TEST_01"; // This name must be the same as adv_name in ble_central + struct bt_data adv_data[2] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_NO_BREDR | BT_LE_AD_GENERAL)), + BT_DATA(BT_DATA_NAME_COMPLETE, adv_name, strlen(adv_name)), + }; + + return bt_le_adv_start(&adv_param, adv_data, ARRAY_SIZE(adv_data), &adv_data[1], 1); +} + + +void bt_enable_cb(int err) +{ + ble_tp_init(); + + ble_start_adv(); +} + + +void ble_stack_start(void) +{ + MSG("[OS] ble_controller_init...\r\n"); + GLB_Set_EM_Sel(GLB_EM_8KB); + ble_controller_init(configMAX_PRIORITIES - 1); + + // Initialize BLE Host stack + MSG("[OS] hci_driver_init...\r\n"); + hci_driver_init(); + + MSG("[OS] bt_enable...\r\n"); + bt_enable(bt_enable_cb); +} From 736bedc51aed8eadb0a8d276205cbf72e2e0dd37 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 19:49:20 +1100 Subject: [PATCH 002/211] Scratch --- .../ble/ble_stack/host/bl_host_assist.c | 363 ------ .../components/ble/ble_stack/host/conn.c | 2 +- .../components/ble/ble_stack/host/gatt.c | 2 +- .../components/ble/ble_stack/host/iso.c | 1111 ----------------- .../components/ble/ble_stack/host/l2cap.c | 2 +- .../components/ble/ble_stack/host/rfcomm.c | 1 + .../ble/ble_stack/services/ble_tp_svc.c | 1 - .../ble/ble_stack/services/oad/oad_main.c | 625 ---------- source/Core/BSP/Pinecilv2/ble.c | 2 +- .../BSP/Pinecilv2/ble_peripheral_tp_server.c | 352 ++++++ .../BSP/Pinecilv2/ble_peripheral_tp_server.h | 38 + 11 files changed, 395 insertions(+), 2104 deletions(-) delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c create mode 100644 source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c create mode 100644 source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.h diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c deleted file mode 100644 index d62716d4b6..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/bl_host_assist.c +++ /dev/null @@ -1,363 +0,0 @@ -#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c index 86b90b3e00..6644abcd12 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/conn.c @@ -35,7 +35,7 @@ #include "att_internal.h" #include "gatt_internal.h" #if defined(BFLB_BLE) -#include "config.h" +#include "ble_config.h" extern struct k_sem g_poll_sem; #endif diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c index a9c4d9169e..e6ed08ae45 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/gatt.c @@ -30,7 +30,7 @@ #include #include #if defined(BFLB_BLE) -#include "config.h" +#include "ble_config.h" #include #endif diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c deleted file mode 100644 index 7ab175d1a9..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/iso.c +++ /dev/null @@ -1,1111 +0,0 @@ -/* 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c index c40183868b..988365722d 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap.c @@ -26,7 +26,7 @@ #include "conn_internal.h" #include "l2cap_internal.h" -#include "config.h" +#include "ble_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) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c index 67bf469a69..1ce95adac5 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c index 4f27a55b01..1e52e13fe0 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c @@ -14,7 +14,6 @@ NOTES #include #include #include -#include #include "bluetooth.h" #include "conn.h" diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c deleted file mode 100644 index d324eddbb2..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/oad/oad_main.c +++ /dev/null @@ -1,625 +0,0 @@ -#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" -#if !defined(CONFIG_BL_MCU_SDK) -#include "hal_boot2.h" -#include "bl_flash.h" -#include "bl_sys.h" -#include "hosal_ota.h" -#include "bl702_common.h" -#include "hal_sys.h" -#else -#include "partition.h" -#include "hal_flash.h" -#include "errno.h" -#include "bl702_glb.h" -#include "mbedtls/sha256.h" -#endif -#include "log.h" -#include "bl702.h" -#include "softcrc.h" -#if defined(CONFIG_BL_MCU_SDK) -#define BL_SDK_VER "1.00" -#define BOOT2_PARTITION_ADDR (0x4202DC00) -#endif //CONFIG_BL_MCU_SDK - -#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; - -#if defined(CONFIG_BL_MCU_SDK) -static int hal_boot2_partition_addr_inactive(const char *name, uint32_t *addr, uint32_t *size); -static int hal_boot2_get_active_entries_byname(u8_t *name, pt_table_entry_config *ptEntry_hal); -static int hal_boot2_update_ptable(pt_table_entry_config *ptEntry_hal); -static int fw_check_hash256(void); -#endif -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"); -#if !defined(CONFIG_BL_MCU_SDK) - if (!hosal_ota_finish(1, 0)) -#else - if (!fw_check_hash256()) -#endif - k_delayed_work_submit(&oad_env.upgrd_work, UPGRD_TIMEOUT); - else { - BT_WARN("Hash256 check failed"); - status = OAD_CHECK_HASH256_FAIL; - } - } - - 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 ota_finish(struct k_work *work) -{ - 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 - -#if defined(CONFIG_BL_MCU_SDK) - GLB_SW_System_Reset(); -#else - hal_reboot(); -#endif -} - -static u8_t oad_write_flash(uint32_t filesize, uint32_t offset, u8_t *data, u16_t len) -{ -#if defined(CONFIG_BL_MCU_SDK) - 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) { - BT_WARN("flash erase\r\n"); - 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); - 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); -#else - hosal_ota_update(filesize, offset, data, len); -#endif - - 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 oDataLen = 0; - static u32_t write_count = 0; - - 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)*/ OTA_WRITE_FLASH_SIZE - 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; - - } else { - oDataLen = len - left_size; - memcpy((wData.wdata_buf + wData.index), data, left_size); - wData.index += left_size; - if (wData.index == OTA_WRITE_FLASH_SIZE) { - if (oad_write_flash(oad_env.upgrd_file_size, oad_env.hosal_offset, wData.wdata_buf, OTA_WRITE_FLASH_SIZE)) { - BT_ERR("Failed to write flash\r\n"); - return OAD_ABORT; - } - } else { - BT_ERR("Unexpect result\r\n"); - return OAD_ABORT; - } - - write_count += 1; - oad_env.hosal_offset = write_count * OTA_WRITE_FLASH_SIZE; -#if defined(CONFIG_BT_SETTINGS) - 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), (data + left_size), oDataLen); - wData.index += oDataLen; - } - } - - 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(oad_env.upgrd_file_size, oad_env.hosal_offset, 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: { -#if !defined(CONFIG_BL_MCU_SDK) - bl_flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); -#else - flash_erase(oad_env.new_img_addr, oad_env.upgrd_file_size); -#endif - } 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 bt_le_conn_param conn_param; - struct oad_image_identity_t *identity = (struct oad_image_identity_t *)(data); - int err = 0; - - 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); -#else - oad_env.new_img_addr = 0; - oad_env.w_img_end_addr = 0; -#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; - } - - conn_param.interval_max = 6; - conn_param.interval_min = 6; - conn_param.latency = 0; - conn_param.timeout = 500; //5s - err = bt_conn_le_param_update(conn, &conn_param); - if (err) - BT_WARN("fail to start conn update\r\n"); - else - BT_WARN("start conn update\r\n"); - - 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"); -#if !defined(CONFIG_BL_MCU_SDK) - hosal_ota_start(oad_env.upgrd_file_size); -#endif - 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; - } -} -#if defined(CONFIG_BL_MCU_SDK) -static struct { - u8_t partition_active_idx; - u8_t pad[3]; - pt_table_stuff_config table; -} boot2_partition_table; - -//#define PARTITION_MAGIC (0x54504642) - -pt_table_id_type active_id = PT_TABLE_ID_INVALID; -pt_table_stuff_config pt_table_stuff[2]; - -static int fw_check_hash256(void) -{ - uint32_t bin_size; - uint32_t hash_addr; - pt_table_entry_config ptEntry; - - if (oad_env.upgrd_file_size <= 32) { - return -1; - } - if (oad_env.w_img_end_addr <= 32) { - return -1; - } - - bin_size = oad_env.upgrd_file_size - 32; - hash_addr = oad_env.w_img_end_addr - 32; - - if (hal_boot2_get_active_entries_byname((uint8_t *)"FW", &ptEntry)) { - BT_WARN("Failed to get active entries by name\r\n"); - return -1; - } - -#define CHECK_IMG_BUF_SIZE 512 - uint8_t sha_check[32] = { 0 }; - uint8_t dst_sha[32] = { 0 }; - uint32_t read_size; - mbedtls_sha256_context sha256_ctx; - int i, offset = 0; - uint8_t r_buf[CHECK_IMG_BUF_SIZE]; - - BT_WARN("[OTA]prepare OTA partition info\r\n"); - mbedtls_sha256_init(&sha256_ctx); - mbedtls_sha256_starts_ret(&sha256_ctx, 0); - - memset(sha_check, 0, 32); - memset(dst_sha, 0, 32); - offset = 0; - while (offset < bin_size) { - (bin_size - offset >= CHECK_IMG_BUF_SIZE) ? (read_size = CHECK_IMG_BUF_SIZE) : (read_size = bin_size - offset); - if (flash_read(oad_env.new_img_addr + offset, r_buf, read_size)) { - BT_WARN("flash read failed \r\n"); - return -1; - } - mbedtls_sha256_update_ret(&sha256_ctx, (const uint8_t *)r_buf, read_size); - offset += read_size; - } - - mbedtls_sha256_finish_ret(&sha256_ctx, sha_check); - - flash_read(hash_addr, dst_sha, 32); - for (i = 0; i < 32; i++) { - BT_WARN("%02X", dst_sha[i]); - } - puts("\r\nHeader SET SHA256 Checksum:"); - for (i = 0; i < 32; i++) { - BT_WARN("%02X", sha_check[i]); - } - - if (memcmp(sha_check, (const void *)dst_sha, 32) != 0) { - BT_WARN("sha256 check error\r\n"); - return -1; - } - ptEntry.len = bin_size; - BT_WARN("[OTA] Update PARTITION, partition len is %lu\r\n", ptEntry.len); - hal_boot2_update_ptable(&ptEntry); - - return 0; -} - -static int oad_hal_boot2_partition_addr(const char *name, uint32_t *addr0, uint32_t *addr1, uint32_t *size0, uint32_t *size1, int *active) -{ - int i; - - if (BFLB_PT_MAGIC_CODE != boot2_partition_table.table.pt_table.magicCode) { - return -EIO; - } - - /*Get Target partition*/ - for (i = 0; i < boot2_partition_table.table.pt_table.entryCnt; i++) { - if (0 == strcmp((char *)&(boot2_partition_table.table.pt_entries[i].name[0]), name)) { - break; - } - } - if (boot2_partition_table.table.pt_table.entryCnt == i) { - return -ENOENT; - } - *addr0 = boot2_partition_table.table.pt_entries[i].start_address[0]; - *addr1 = boot2_partition_table.table.pt_entries[i].start_address[1]; - *size0 = boot2_partition_table.table.pt_entries[i].max_len[0]; - *size1 = boot2_partition_table.table.pt_entries[i].max_len[1]; - *active = boot2_partition_table.table.pt_entries[i].active_index; - - return 0; -} - -static int hal_boot2_partition_addr_inactive(const char *name, uint32_t *addr, uint32_t *size) -{ - uint32_t addr0, addr1; - uint32_t size0, size1; - int active, ret; - - if ((ret = oad_hal_boot2_partition_addr(name, &addr0, &addr1, &size0, &size1, &active))) { - return ret; - } - *addr = active ? addr0 : addr1; - *size = active ? size0 : size1; - - return 0; -} - -static pt_table_error_type oad_PtTable_Get_Active_Entries_By_Name(pt_table_stuff_config *ptStuff, - u8_t *name, - pt_table_entry_config *ptEntry) -{ - uint32_t i = 0; - uint32_t len = strlen((char *)name); - - if (ptStuff == NULL || ptEntry == NULL) { - return PT_ERROR_PARAMETER; - } - for (i = 0; i < ptStuff->pt_table.entryCnt; i++) { - if (strlen((char *)ptStuff->pt_entries[i].name) == len && - memcmp((char *)ptStuff->pt_entries[i].name, (char *)name, len) == 0) { - ARCH_MemCpy_Fast(ptEntry, &ptStuff->pt_entries[i], sizeof(pt_table_entry_config)); - return PT_ERROR_SUCCESS; - } - } - return PT_ERROR_ENTRY_NOT_FOUND; -} - -static int hal_boot2_update_ptable(pt_table_entry_config *ptEntry_hal) -{ - int ret; - //FIXME force covert - pt_table_entry_config *ptEntry = (pt_table_entry_config *)ptEntry_hal; - - ptEntry->active_index = !ptEntry->active_index; - (ptEntry->age)++; - ret = pt_table_update_entry((pt_table_id_type)(!active_id), &pt_table_stuff[!active_id], ptEntry); - return ret; -} - -static int hal_boot2_get_active_entries_byname(uint8_t *name, pt_table_entry_config *ptEntry_hal) -{ - pt_table_entry_config *ptEntry = (pt_table_entry_config *)ptEntry_hal; - if (oad_PtTable_Get_Active_Entries_By_Name(&boot2_partition_table.table, name, ptEntry)) { - return -1; - } - return 0; -} - -static int oad_get_boot2_partition_table(void) -{ - memcpy(&boot2_partition_table.table, &pt_table_stuff[active_id], sizeof(pt_table_stuff_config)); - boot2_partition_table.partition_active_idx = active_id; - BT_WARN("magicCode: 0x%x\r\n", boot2_partition_table.table.pt_table.magicCode); - return 0; -} -#endif -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); - -#if defined(CONFIG_BL_MCU_SDK) - flash_init(); - pt_table_set_flash_operation(flash_erase, flash_write, flash_read); - active_id = pt_table_get_active_partition_need_lock(pt_table_stuff); - oad_get_boot2_partition_table(); -#endif - k_delayed_work_init(&oad_env.upgrd_work, ota_finish); -} diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c index bdde265394..e64b3f0433 100644 --- a/source/Core/BSP/Pinecilv2/ble.c +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -22,7 +22,7 @@ NOTES #include "uuid.h" #include "ble_peripheral_tp_server.h" #include "log.h" - +#include "bl702_glb.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); diff --git a/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c new file mode 100644 index 0000000000..576617d072 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c @@ -0,0 +1,352 @@ +/**************************************************************************** +FILE NAME + ble_peripheral_tp_server.c + +DESCRIPTION + test profile demo + +NOTES +*/ +/****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bluetooth.h" +#include "conn.h" +#include "gatt.h" +#include "hci_core.h" +#include "uuid.h" +#include "ble_peripheral_tp_server.h" +#include "log.h" +#include "hal_clock.h" + +extern bool pds_start; + +static void ble_tp_connected(struct bt_conn *conn, u8_t err); +static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason); +static void ble_param_updated(struct bt_conn *conn, u16_t interval, u16_t latency, u16_t timeout); + +static struct bt_conn *ble_tp_conn; +#if !defined(CONFIG_BT_OAD_SERVER) +static struct bt_gatt_exchange_params exchg_mtu; +#endif +static TaskHandle_t ble_tp_task_h; + +static struct k_sem notify_poll_sem; + +static int tx_mtu_size = 20; +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, + .le_param_updated = ble_param_updated, +}; + +#if !defined(CONFIG_BT_OAD_SERVER) +/************************************************************************* +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", tx_mtu_size); + } else { + BT_WARN("ble tp echange mtu size failure, err: %d", err); + } +} +#endif +/************************************************************************* +NAME + ble_tp_connected +*/ +static void ble_tp_connected(struct bt_conn *conn, u8_t err) +{ +#if !defined(CONFIG_BT_OAD_SERVER) + int tx_octets = 0x00fb; + int tx_time = 0x0848; + int ret = -1; +#endif + +#if XTAL_32K_TYPE == EXTERNAL_XTAL_32K + struct bt_le_conn_param param; +#endif + + if (err) { + return; + } + + BT_WARN("Tp connected"); + ble_tp_conn = conn; + pds_start = false; + +#if XTAL_32K_TYPE == EXTERNAL_XTAL_32K + param.interval_min = param.interval_max = 0x320; + param.latency = 0; + param.timeout = 0x05dc; + ret = bt_conn_le_param_update(ble_tp_conn, ¶m); + if (ret) { + BT_WARN("conn update failed (err %d)\r\n", ret); + } else { + BT_WARN("conn update initiated\r\n"); + } +#endif + +#if !defined(CONFIG_BT_OAD_SERVER) + //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"); + } else { + BT_WARN("ble tp set data length failure, err: %d", 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"); + } else { + BT_WARN("ble tp exchange mtu size failure, err: %d", ret); + } +#endif +} + +/************************************************************************* +NAME + ble_tp_disconnected +*/ +static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason) +{ + BT_WARN("Tp disconnected"); + + if (created_tp_task) { + BT_WARN("Delete throughput tx task"); + vTaskDelete(ble_tp_task_h); + created_tp_task = 0; + } + + ble_tp_conn = NULL; + extern int ble_start_adv(void); + ble_start_adv(); + pds_start = true; +} + +/************************************************************************* +NAME + ble_param_updated +*/ + +static void ble_param_updated(struct bt_conn *conn, u16_t interval, + u16_t latency, u16_t timeout) +{ + BT_WARN("LE conn param updated: int 0x%04x lat %d to %d \r\n", interval, latency, timeout); +#if XTAL_32K_TYPE == EXTERNAL_XTAL_32K + if (interval > 80) { + pds_start = true; + } else { + pds_start = false; + } +#endif +} + +/************************************************************************* +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(receive data from client) +*/ +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_WARN("recv data len=%d, offset=%d, flag=%d", len, offset, flags); + BT_WARN("recv data:%s", bt_hex(buf, len)); + + if (flags & BT_GATT_WRITE_FLAG_PREPARE) { + //Don't use prepare write data, execute write will upload data again. + BT_WARN("recv prepare write request"); + return 0; + } + + if (flags & BT_GATT_WRITE_FLAG_CMD) { + //Use write command data. + BT_WARN("recv write command"); + } else { + //Use write request / execute write data. + BT_WARN("recv write request / exce write"); + } + + k_sem_give(¬ify_poll_sem); + return len; +} + +/************************************************************************* +NAME + indicate_rsp /bl_tp_send_indicate +*/ + +static void indicate_rsp(struct bt_conn *conn, const struct bt_gatt_attr *attr, u8_t err) +{ + BT_WARN("receive comfirmation, err:%d", err); +} + +static int bl_tp_send_indicate(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, u16_t len) +{ + 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", err); + } +} + +/************************************************************************* +NAME + ble_tp_notify(send data to client) +*/ +static void ble_tp_notify_task(void *pvParameters) +{ + int err = -1; + u8_t data[244] = { 0x01 }; + k_sem_give(¬ify_poll_sem); + + while (1) { + k_sem_take(¬ify_poll_sem, K_FOREVER); + //send data to client + err = bt_gatt_notify(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX), data, (tx_mtu_size - 3)); + data[0] = data[0] + 1; + BT_WARN("ble tp send notify : %d", err); + } +} + +/************************************************************************* +NAME + ble_tp_not_ccc_changed +*/ +static void ble_tp_notify_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) +{ + BT_WARN("ccc:value=[%d]", value); + + if (value == BT_GATT_CCC_NOTIFY) { + if (xTaskCreate(ble_tp_notify_task, (char *)"bletp", 512, NULL, 15, &ble_tp_task_h) == pdPASS) { + created_tp_task = 1; + BT_WARN("Create throughput tx task success"); + } else { + created_tp_task = 0; + BT_WARN("Create throughput tx taskfail"); + } + } else { + if (created_tp_task) { + BT_WARN("Delete throughput tx task"); + 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, + 0, + 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, + 0, + NULL, + NULL, + NULL), + + BT_GATT_CCC(ble_tp_notify_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]; +} + +static 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); + k_sem_init(¬ify_poll_sem, 0, 1); + } +} diff --git a/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.h b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.h new file mode 100644 index 0000000000..235039afd7 --- /dev/null +++ b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.h @@ -0,0 +1,38 @@ +/**************************************************************************** +FILE NAME + ble_peripheral_tp_server.h + +DESCRIPTION +NOTES +*/ +/****************************************************************************/ + +#ifndef _BLE_TP_SVC_H_ +#define _BLE_TP_SVC_H_ + +#include "ble_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 From 69c2d475e31750b6d34278cb1960d36881915acc Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 20:02:33 +1100 Subject: [PATCH 003/211] Update postRTOS.cpp --- source/Core/BSP/Pinecilv2/postRTOS.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/Core/BSP/Pinecilv2/postRTOS.cpp b/source/Core/BSP/Pinecilv2/postRTOS.cpp index 7e10c24e49..44119950f5 100644 --- a/source/Core/BSP/Pinecilv2/postRTOS.cpp +++ b/source/Core/BSP/Pinecilv2/postRTOS.cpp @@ -11,6 +11,9 @@ #include "task.h" bool hall_effect_present = false; +extern "C" { +void ble_stack_start(void); +}; void postRToSInit() { // Any after RTos setup #ifdef HALL_SI7210 @@ -18,6 +21,7 @@ void postRToSInit() { hall_effect_present = Si7210::init(); } #endif + ble_stack_start(); } int16_t getRawHallEffect() { if (hall_effect_present) { From ea3bd86b5205322eb070d4589c862d6e90a325c5 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 20:02:44 +1100 Subject: [PATCH 004/211] Update Makefile --- source/Makefile | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/source/Makefile b/source/Makefile index f2842429ed..d3e9328159 100644 --- a/source/Makefile +++ b/source/Makefile @@ -97,6 +97,24 @@ PINECILV2_COMPONENTS_DIR = $(PINECILV2_SDK_DIR)/components PINECILV2_COMPONENTS_FREERTOS_DIR = $(PINECILV2_COMPONENTS_DIR)/freertos PINECILV2_COMPONENTS_FREERTOS_BL602_DIR = $(PINECILV2_COMPONENTS_FREERTOS_DIR)/portable/gcc/risc-v/bl702 +PINECILV2_COMPONENTS_BLE_STACK_PORT_INCLUDE_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/port/include +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/include +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DRIVERS_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/include/drivers +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DRIVERS_BLUETOOTH_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/include/drivers/bluetooth +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_BLUETOOTH_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/include/bluetooth +PINECILV2_COMPONENTS_BLE_STACK_COMMON_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common +PINECILV2_COMPONENTS_BLE_STACK_COMMON_INCLUDE_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/include +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_MISC_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/include/misc +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_ZEPHYR_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/include/zephyr +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_NET_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/include/net +PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_TOOLCHAIN_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/include/toolchain +PINECILV2_COMPONENTS_BLE_STACK_TINYCRYPT_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/tinycrypt/include +PINECILV2_COMPONENTS_BLE_STACK_TINYCRYPT_INCLUDE_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/common/tinycrypt/include/tinycrypt +PINECILV2_COMPONENTS_BLE_STACK_HOST_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/host +PINECILV2_COMPONENTS_BLE_STACK_BL_HCI_WRAPPER_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/bl_hci_wrapper +PINECILV2_COMPONENTS_BLE_CONTROLLER_BLE_INC_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/blecontroller/ble_inc +PINECILV2_COMPONENTS_BLE_STACK_BC_DEC_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/sbc/dec + PINECILV2_COMPONENTS_NMSIS_DIR = $(PINECILV2_COMPONENTS_DIR)/nmsis PINECILV2_COMPONENTS_NMSIS_CORE_INC_DIR = $(PINECILV2_COMPONENTS_NMSIS_DIR)/core/inc @@ -243,6 +261,24 @@ DEVICE_INCLUDES = -I$(PINECILV2_DIR) \ -I$(PINECILV2_COMPONENTS_DIR) \ -I$(PINECILV2_COMPONENTS_FREERTOS_DIR) \ -I$(PINECILV2_COMPONENTS_FREERTOS_BL602_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DRIVERS_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_DRIVERS_BLUETOOTH_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_PORT_INCLUDE_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_BLUETOOTH_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_COMMON_INCLUDE_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_COMMON_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_MISC_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_ZEPHYR_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_NET_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_TOOLCHAIN_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_INCLUDE_TOOLCHAIN_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_TINYCRYPT_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_TINYCRYPT_INCLUDE_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_HOST_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_BL_HCI_WRAPPER_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_CONTROLLER_BLE_INC_DIR) \ + -I$(PINECILV2_COMPONENTS_BLE_STACK_BC_DEC_DIR) \ -I$(PINECILV2_COMPONENTS_NMSIS_DIR) \ -I$(PINECILV2_COMPONENTS_USB_STACK_DIR) \ -I$(PINECILV2_DRIVERS_DIR) \ @@ -269,11 +305,12 @@ LDSCRIPT=./Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld CPUFLAGS= -march=rv32imafc \ -mabi=ilp32f \ -mcmodel=medany -fsigned-char -fno-builtin -nostartfiles \ - -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DportUSING_MPU_WRAPPERS=0 -DDBFLB_USE_ROM_DRIVER=1 - + -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DportUSING_MPU_WRAPPERS=0 -DBL702 -DBFLB_USE_ROM_DRIVER=1 DEV_LDFLAGS=-nostartfiles DEV_AFLAGS= -DEV_GLOBAL_DEFS= +DEV_GLOBAL_DEFS= -D CONFIG_DISABLE_BT_SMP=1 -DCONFIG_BT_BREDR=1 -DBFLB_BLE -DCFG_BLE_ENABLE -DCFG_BLE -DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER -DCONFIG_MAX_SCO=1 -DCFG_BLE_TX_BUFF_DATA=1 -DCFG_CON=1 -DDCFG_BLE_TX_BUFF_DATA=1 -DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL -DCONFIG_BT_GATT_CLIENT -DCONFIG_BT_CONN -DCONFIG_BT_GATT_DIS_PNP -DCONFIG_BT_GATT_DIS_SERIAL_NUMBER -DCONFIG_BT_GATT_DIS_FW_REV -DCONFIG_BT_GATT_DIS_HW_REV -DCONFIG_BT_GATT_DIS_SW_REV -DCONFIG_BT_ECC -DCONFIG_BT_GATT_DYNAMIC_DB -DCONFIG_BT_GATT_SERVICE_CHANGED -DCONFIG_BT_KEYS_OVERWRITE_OLDEST -DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING -DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS -DCONFIG_BT_BONDABLE -DCONFIG_BT_HCI_VS_EVT_USER -DCONFIG_BT_ASSERT + + #Required to be turned off due to their drivers tripping warnings DEV_CFLAGS= -Wno-error=enum-conversion -Wno-type-limits -Wno-implicit-fallthrough DEV_CXXFLAGS= $(DEV_CFLAGS) From a6bb022997dc8ce49b0e29bad32be1817f51c314 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:10:47 +1100 Subject: [PATCH 005/211] Update hex.c --- .../components/ble/ble_stack/common/hex.c | 80 ------------------- 1 file changed, 80 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c index 6f3c3c42d6..2d2425c067 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/hex.c @@ -9,83 +9,3 @@ #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; -} From 721c7d36f3bb3cb4d7ff7f4347d1bd4ee9cdb5a7 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:10:54 +1100 Subject: [PATCH 006/211] rem dup --- .../ble/ble_stack/services/ble_tp_svc.c | 292 ------------------ .../ble/ble_stack/services/ble_tp_svc.h | 38 --- .../gcc/risc-v/bl702/FreeRTOSConfig.h | 112 ------- 3 files changed, 442 deletions(-) delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/freertos/portable/gcc/risc-v/bl702/FreeRTOSConfig.h diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c deleted file mode 100644 index 1e52e13fe0..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.c +++ /dev/null @@ -1,292 +0,0 @@ -/**************************************************************************** -FILE NAME - ble_tp_svc.c - -DESCRIPTION - test profile demo - -NOTES -*/ -/****************************************************************************/ - -#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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h deleted file mode 100644 index 5071ad592d..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/services/ble_tp_svc.h +++ /dev/null @@ -1,38 +0,0 @@ -/**************************************************************************** -FILE NAME - ble_tp_svc.h - -DESCRIPTION -NOTES -*/ -/****************************************************************************/ - -#ifndef _BLE_TP_SVC_H_ -#define _BLE_TP_SVC_H_ - -#include - -// 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/freertos/portable/gcc/risc-v/bl702/FreeRTOSConfig.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/freertos/portable/gcc/risc-v/bl702/FreeRTOSConfig.h deleted file mode 100644 index 1ad79327b3..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/freertos/portable/gcc/risc-v/bl702/FreeRTOSConfig.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * FreeRTOS Kernel V10.2.1 - * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ -#define configSUPPORT_STATIC_ALLOCATION 1 -#define CLINT_CTRL_ADDR (0x02000000UL) -#define configCLINT_BASE_ADDRESS CLINT_CTRL_ADDR -#define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configCPU_CLOCK_HZ (1000000UL) -#define configTICK_RATE_HZ ((TickType_t)1000) -#define configMAX_PRIORITIES (7) -#define configMINIMAL_STACK_SIZE ((unsigned short)512) /* Only needs to be this high as some demo tasks also use this constant. In production only the idle task would use this. */ -#define configTOTAL_HEAP_SIZE ((size_t)48 * 1024) -#define configMAX_TASK_NAME_LEN (16) -#define configUSE_TRACE_FACILITY 1 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 0 -#define configUSE_MUTEXES 1 -#define configQUEUE_REGISTRY_SIZE 8 -#define configCHECK_FOR_STACK_OVERFLOW 2 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_MALLOC_FAILED_HOOK 1 -#define configUSE_APPLICATION_TASK_TAG 0 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 -#define configUSE_STATS_FORMATTING_FUNCTIONS 2 -#define configUSE_TICKLESS_IDLE 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES (2) - -/* Software timer definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) -#define configTIMER_QUEUE_LENGTH 8 -#define configTIMER_TASK_STACK_DEPTH (160) - -/* Task priorities. Allow these to be overridden. */ -#ifndef uartPRIMARY_PRIORITY -#define uartPRIMARY_PRIORITY (configMAX_PRIORITIES - 3) -#endif - -/* Set the following definitions to 1 to include the API function, or zero -to exclude the API function. */ -#define INCLUDE_vTaskPrioritySet 1 -#define INCLUDE_uxTaskPriorityGet 1 -#define INCLUDE_vTaskDelete 1 -#define INCLUDE_vTaskCleanUpResources 1 -#define INCLUDE_vTaskSuspend 1 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_eTaskGetState 1 -#define INCLUDE_xTimerPendFunctionCall 1 -#define INCLUDE_xTaskAbortDelay 1 -#define INCLUDE_xTaskGetHandle 1 -#define INCLUDE_xSemaphoreGetMutexHolder 1 - -/* Normal assert() semantics without relying on the provision of an assert.h -header file. */ -void vAssertCalled(void); -#define configASSERT(x) \ - if ((x) == 0) \ - vAssertCalled() - -#if (configUSE_TICKLESS_IDLE != 0) -void vApplicationSleep(uint32_t xExpectedIdleTime); -#define portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime) vApplicationSleep(xExpectedIdleTime) -#endif - -#define portUSING_MPU_WRAPPERS 0 -#endif /* FREERTOS_CONFIG_H */ From 8a857ce757197702ca6aec05a8167aea58ac663c Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:11:05 +1100 Subject: [PATCH 007/211] ble linker --- .../bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld index 3fb9b5ec0e..bc52b6c99d 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld @@ -32,7 +32,7 @@ SECTIONS { PROVIDE(__metal_chicken_bit = 0); - .text : + .text : { . = ALIGN(4); __text_code_start__ = .; @@ -73,9 +73,19 @@ SECTIONS *bl702_xip_sflash_ext*.o* \ *bl702_ef_ctrl*.o*) .rodata*) + *(.rodata) + *(.rodata.*) + *(.srodata) *(.srodata.*) + _bt_gatt_service_static_list_start = .; + KEEP(*(SORT_BY_NAME("._bt_gatt_service_static.static.*"))) + _bt_gatt_service_static_list_end = .; + _bt_l2cap_fixed_chan_list_start = .; + KEEP(*(SORT_BY_NAME("._bt_l2cap_fixed_chan.static.*"))) + _bt_l2cap_fixed_chan_list_end = .; + . = ALIGN(4); __text_code_end__ = .; } > xip_memory From 0a215e399676b92cceffdfa97d8f0567831ec2b7 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:11:08 +1100 Subject: [PATCH 008/211] Update system_bl702.c --- .../bl_mcu_sdk/drivers/bl702_driver/startup/system_bl702.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/system_bl702.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/system_bl702.c index 49acc26604..c030a5590b 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/system_bl702.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/system_bl702.c @@ -41,9 +41,6 @@ void USB_DoNothing_IRQHandler(void) { /*---------------------------------------------------------------------------- Vector Table *----------------------------------------------------------------------------*/ -#define VECT_TAB_OFFSET \ - 0x00 /*!< Vector Table base offset field. \ - This value must be a multiple of 0x200. */ /*---------------------------------------------------------------------------- System initialization function From 4fcbb8656cfd9543406094b486604de862ba4b0b Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:11:15 +1100 Subject: [PATCH 009/211] . --- source/Core/BSP/Pinecilv2/ble.c | 323 +++--------------- .../BSP/Pinecilv2/ble_peripheral_tp_server.c | 2 +- 2 files changed, 39 insertions(+), 286 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c index e64b3f0433..ecd9e46ee5 100644 --- a/source/Core/BSP/Pinecilv2/ble.c +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -13,8 +13,9 @@ NOTES #include #include #include +#include "bflb_platform.h" #include - +#include "BSP.h" #include "bluetooth.h" #include "conn.h" #include "gatt.h" @@ -23,291 +24,7 @@ NOTES #include "ble_peripheral_tp_server.h" #include "log.h" #include "bl702_glb.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); - -static struct bt_conn *ble_tp_conn; -#if !defined(CONFIG_BT_OAD_SERVER) -static struct bt_gatt_exchange_params exchg_mtu; -#endif -static TaskHandle_t ble_tp_task_h; - -static struct k_sem notify_poll_sem; - -static int tx_mtu_size = 20; -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, -}; - -#if !defined(CONFIG_BT_OAD_SERVER) -/************************************************************************* -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", tx_mtu_size); - } else { - BT_WARN("ble tp echange mtu size failure, err: %d", err); - } -} -#endif -/************************************************************************* -NAME - ble_tp_connected -*/ -static void ble_tp_connected(struct bt_conn *conn, u8_t err) -{ - #if !defined(CONFIG_BT_OAD_SERVER) - int tx_octets = 0x00fb; - int tx_time = 0x0848; - int ret = -1; - #endif - - if (err) { - return; - } - - BT_WARN("Tp connected"); - ble_tp_conn = conn; - - #if !defined(CONFIG_BT_OAD_SERVER) - //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"); - } else { - BT_WARN("ble tp set data length failure, err: %d", 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"); - } else { - BT_WARN("ble tp exchange mtu size failure, err: %d", ret); - } - #endif -} - -/************************************************************************* -NAME - ble_tp_disconnected -*/ -static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason) -{ - BT_WARN("Tp disconnected"); - - if (created_tp_task) { - BT_WARN("Delete throughput tx task"); - vTaskDelete(ble_tp_task_h); - created_tp_task = 0; - } - - 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(receive data from client) -*/ -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_WARN("recv data len=%d, offset=%d, flag=%d", len, offset, flags); - BT_WARN("recv data:%s", bt_hex(buf, len)); - - if (flags & BT_GATT_WRITE_FLAG_PREPARE) { - //Don't use prepare write data, execute write will upload data again. - BT_WARN("recv prepare write request"); - return 0; - } - - if (flags & BT_GATT_WRITE_FLAG_CMD) { - //Use write command data. - BT_WARN("recv write command"); - } else { - //Use write request / execute write data. - BT_WARN("recv write request / exce write"); - } - - k_sem_give(¬ify_poll_sem); - return len; -} - -/************************************************************************* -NAME - indicate_rsp /bl_tp_send_indicate -*/ - -static void indicate_rsp(struct bt_conn *conn, const struct bt_gatt_attr *attr, u8_t err) -{ - BT_WARN("receive comfirmation, err:%d", err); -} - -static int bl_tp_send_indicate(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *data, u16_t len) -{ - 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", err); - } -} - -/************************************************************************* -NAME - ble_tp_notify(send data to client) -*/ -static void ble_tp_notify_task(void *pvParameters) -{ - int err = -1; - u8_t data[244] = { 0x01 }; - k_sem_give(¬ify_poll_sem); - - while (1) { - k_sem_take(¬ify_poll_sem, K_FOREVER); - //send data to client - err = bt_gatt_notify(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX), data, (tx_mtu_size - 3)); - data[0] = data[0] + 1; - BT_WARN("ble tp send notify : %d", err); - } -} - -/************************************************************************* -NAME - ble_tp_not_ccc_changed -*/ -static void ble_tp_notify_ccc_changed(const struct bt_gatt_attr *attr, u16_t value) -{ - BT_WARN("ccc:value=[%d]", value); - - if (value == BT_GATT_CCC_NOTIFY) { - - if (xTaskCreate(ble_tp_notify_task, (char *)"bletp", 512, NULL, 15, &ble_tp_task_h) == pdPASS) { - created_tp_task = 1; - BT_WARN("Create throughput tx task success"); - } else { - created_tp_task = 0; - BT_WARN("Create throughput tx taskfail"); - } - } else { - if (created_tp_task) { - BT_WARN("Delete throughput tx task"); - 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, - 0, - 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, - 0, - NULL, - NULL, - NULL), - - BT_GATT_CCC(ble_tp_notify_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]; -} - -static 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); - k_sem_init(¬ify_poll_sem, 0, 1); - } -} @@ -351,3 +68,39 @@ void ble_stack_start(void) MSG("[OS] bt_enable...\r\n"); bt_enable(bt_enable_cb); } + + + +/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the +application must provide an implementation of vApplicationGetTimerTaskMemory() +to provide the memory that is used by the Timer service task. */ +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) +{ + /* If the buffers to be provided to the Timer task are declared inside this + function then they must be declared static - otherwise they will be allocated on + the stack and so not exists after this function exits. */ + static StaticTask_t xTimerTaskTCB; + static StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH]; + + /* Pass out a pointer to the StaticTask_t structure in which the Timer + task's state will be stored. */ + *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; + + /* Pass out the array that will be used as the Timer task's stack. */ + *ppxTimerTaskStackBuffer = uxTimerTaskStack; + + /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. + Note that, as the array is necessarily of type StackType_t, + configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ + *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; +} + + + +void vApplicationMallocFailedHook(void) +{ + MSG("vApplicationMallocFailedHook\r\n"); + + while (1) + ; +} \ No newline at end of file diff --git a/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c index 576617d072..4554fa2d45 100644 --- a/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c +++ b/source/Core/BSP/Pinecilv2/ble_peripheral_tp_server.c @@ -24,7 +24,7 @@ NOTES #include "log.h" #include "hal_clock.h" -extern bool pds_start; + bool pds_start; static void ble_tp_connected(struct bt_conn *conn, u8_t err); static void ble_tp_disconnected(struct bt_conn *conn, u8_t reason); From 69bf96601584dcef31392a622a7df226f7206c17 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:11:22 +1100 Subject: [PATCH 010/211] Update FreeRTOSConfig.h --- source/Core/BSP/Pinecilv2/FreeRTOSConfig.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h index 9127e00d8a..e5f099a808 100644 --- a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h +++ b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h @@ -4,7 +4,7 @@ #define portCHAR char #define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 #define CLINT_CTRL_ADDR (0x02000000UL) #define configCLINT_BASE_ADDRESS CLINT_CTRL_ADDR #define configUSE_PREEMPTION 1 @@ -14,7 +14,7 @@ #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (7) #define configMINIMAL_STACK_SIZE ((unsigned short)160) /* Only needs to be this high as some demo tasks also use this constant. In production only the idle task would use this. */ -#define configTOTAL_HEAP_SIZE ((size_t)0) +#define configTOTAL_HEAP_SIZE ((size_t)1024*4) #define configMAX_TASK_NAME_LEN (24) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 @@ -35,7 +35,10 @@ #define configUSE_CO_ROUTINES 0 /* Software timer definitions. */ -#define configUSE_TIMERS 0 +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_QUEUE_LENGTH 8 +#define configTIMER_TASK_STACK_DEPTH (160) /* Task priorities. Allow these to be overridden. */ #ifndef uartPRIMARY_PRIORITY @@ -63,7 +66,7 @@ void vApplicationSleep(uint32_t xExpectedIdleTime); #define INCLUDE_vTaskPrioritySet 0 #define INCLUDE_uxTaskPriorityGet 0 -#define INCLUDE_vTaskDelete 0 +#define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_xResumeFromISR 1 #define INCLUDE_vTaskDelayUntil 1 From 6a2dc1a9d2b12d9869375d4e8e85a335c355123c Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:11:31 +1100 Subject: [PATCH 011/211] Update Makefile --- source/Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/Makefile b/source/Makefile index d3e9328159..4e162543c6 100644 --- a/source/Makefile +++ b/source/Makefile @@ -116,6 +116,11 @@ PINECILV2_COMPONENTS_BLE_CONTROLLER_BLE_INC_DIR = $(PINECILV2_COMPONENTS_DIR)/bl PINECILV2_COMPONENTS_BLE_STACK_BC_DEC_DIR = $(PINECILV2_COMPONENTS_DIR)/ble/ble_stack/sbc/dec +#Binary blobs suck and they should be ashamed +PINECILV2_BLE_CRAPWARE_BLOB_DIR=$(PINECILV2_COMPONENTS_DIR)/ble/blecontroller/lib +PINECILV2_RF_CRAPWARE_BLOB_DIR=$(PINECILV2_COMPONENTS_DIR)/ble/bl702_rf/lib + + PINECILV2_COMPONENTS_NMSIS_DIR = $(PINECILV2_COMPONENTS_DIR)/nmsis PINECILV2_COMPONENTS_NMSIS_CORE_INC_DIR = $(PINECILV2_COMPONENTS_NMSIS_DIR)/core/inc @@ -305,10 +310,10 @@ LDSCRIPT=./Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld CPUFLAGS= -march=rv32imafc \ -mabi=ilp32f \ -mcmodel=medany -fsigned-char -fno-builtin -nostartfiles \ - -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DportUSING_MPU_WRAPPERS=0 -DBL702 -DBFLB_USE_ROM_DRIVER=1 -DEV_LDFLAGS=-nostartfiles + -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DBL702 -DBFLB_USE_ROM_DRIVER=1 +DEV_LDFLAGS=-nostartfiles -L $(PINECILV2_BLE_CRAPWARE_BLOB_DIR) -L $(PINECILV2_RF_CRAPWARE_BLOB_DIR) -l blecontroller_702_std -l bl702_rf DEV_AFLAGS= -DEV_GLOBAL_DEFS= -D CONFIG_DISABLE_BT_SMP=1 -DCONFIG_BT_BREDR=1 -DBFLB_BLE -DCFG_BLE_ENABLE -DCFG_BLE -DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER -DCONFIG_MAX_SCO=1 -DCFG_BLE_TX_BUFF_DATA=1 -DCFG_CON=1 -DDCFG_BLE_TX_BUFF_DATA=1 -DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL -DCONFIG_BT_GATT_CLIENT -DCONFIG_BT_CONN -DCONFIG_BT_GATT_DIS_PNP -DCONFIG_BT_GATT_DIS_SERIAL_NUMBER -DCONFIG_BT_GATT_DIS_FW_REV -DCONFIG_BT_GATT_DIS_HW_REV -DCONFIG_BT_GATT_DIS_SW_REV -DCONFIG_BT_ECC -DCONFIG_BT_GATT_DYNAMIC_DB -DCONFIG_BT_GATT_SERVICE_CHANGED -DCONFIG_BT_KEYS_OVERWRITE_OLDEST -DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING -DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS -DCONFIG_BT_BONDABLE -DCONFIG_BT_HCI_VS_EVT_USER -DCONFIG_BT_ASSERT +DEV_GLOBAL_DEFS= -D CONFIG_DISABLE_BT_SMP=1 -DCONFIG_BT_BREDR=1 -DBL_MCU_SDK -DBFLB_BLE -DCFG_BLE_ENABLE -DCFG_BLE -DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER -DCONFIG_MAX_SCO=1 -DCFG_BLE_TX_BUFF_DATA=1 -DCFG_CON=1 -DDCFG_BLE_TX_BUFF_DATA=1 -DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL -DCONFIG_BT_GATT_CLIENT -DCONFIG_BT_CONN -DCONFIG_BT_GATT_DIS_PNP -DCONFIG_BT_GATT_DIS_SERIAL_NUMBER -DCONFIG_BT_GATT_DIS_FW_REV -DCONFIG_BT_GATT_DIS_HW_REV -DCONFIG_BT_GATT_DIS_SW_REV -DCONFIG_BT_ECC -DCONFIG_BT_GATT_DYNAMIC_DB -DCONFIG_BT_GATT_SERVICE_CHANGED -DCONFIG_BT_KEYS_OVERWRITE_OLDEST -DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING -DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS -DCONFIG_BT_BONDABLE -DCONFIG_BT_HCI_VS_EVT_USER #Required to be turned off due to their drivers tripping warnings From 6c69c2e24188607d6ec441af0052547e8f971cec Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Fri, 21 Oct 2022 23:26:00 +1100 Subject: [PATCH 012/211] Update bl702_flash.ld --- .../Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld index bc52b6c99d..b6b6d96d20 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld @@ -85,6 +85,9 @@ SECTIONS _bt_l2cap_fixed_chan_list_start = .; KEEP(*(SORT_BY_NAME("._bt_l2cap_fixed_chan.static.*"))) _bt_l2cap_fixed_chan_list_end = .; + _bt_l2cap_br_fixed_chan_list_start = .; + KEEP(*(SORT_BY_NAME("._bt_l2cap_br_fixed_chan.static.*"))) + _bt_l2cap_br_fixed_chan_list_end = .; . = ALIGN(4); __text_code_end__ = .; From d5f33814aa255d3778fadc482d05d07718835ce5 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 10:57:56 +1100 Subject: [PATCH 013/211] Update FreeRTOS --- source/Core/BSP/Pinecilv2/FreeRTOSConfig.h | 2 +- source/Core/BSP/Pinecilv2/MemMang/heap_4.c | 494 + .../ble/blecontroller/ble_inc/ble_lib_api.h | 67 +- source/Core/BSP/Pinecilv2/ble.c | 10 +- source/Core/Inc/main.hpp | 4 +- source/Core/Src/FreeRTOSHooks.c | 5 +- source/Makefile | 7 +- .../FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c | 101 - .../Third_Party/FreeRTOS/Source/croutine.c | 536 +- .../FreeRTOS/Source/event_groups.c | 1238 +-- .../FreeRTOS/Source/include/FreeRTOS.h | 976 +- .../FreeRTOS/Source/include/StackMacros.h | 111 +- .../FreeRTOS/Source/include/atomic.h | 265 +- .../FreeRTOS/Source/include/croutine.h | 807 +- .../Source/include/deprecated_definitions.h | 40 + .../FreeRTOS/Source/include/event_groups.h | 1532 +-- .../FreeRTOS/Source/include/list.h | 185 +- .../FreeRTOS/Source/include/message_buffer.h | 516 +- .../FreeRTOS/Source/include/mpu_prototypes.h | 263 + .../FreeRTOS/Source/include/mpu_wrappers.h | 186 + .../FreeRTOS/Source/include/portable.h | 163 +- .../FreeRTOS/Source/include/projdefs.h | 136 +- .../FreeRTOS/Source/include/queue.h | 3371 +++--- .../FreeRTOS/Source/include/semphr.h | 979 +- .../FreeRTOS/Source/include/stack_macros.h | 122 +- .../FreeRTOS/Source/include/stream_buffer.h | 546 +- .../FreeRTOS/Source/include/task.h | 5580 +++++----- .../FreeRTOS/Source/include/timers.h | 2660 ++--- .../Third_Party/FreeRTOS/Source/list.c | 278 +- .../Third_Party/FreeRTOS/Source/queue.c | 4432 ++++---- .../FreeRTOS/Source/stream_buffer.c | 1326 +++ .../Third_Party/FreeRTOS/Source/tasks.c | 9106 +++++++++-------- .../Third_Party/FreeRTOS/Source/timers.c | 1847 ++-- 33 files changed, 20715 insertions(+), 17176 deletions(-) create mode 100644 source/Core/BSP/Pinecilv2/MemMang/heap_4.c create mode 100644 source/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h create mode 100644 source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h create mode 100644 source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h create mode 100644 source/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c diff --git a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h index e5f099a808..166c5f7ee7 100644 --- a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h +++ b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h @@ -14,7 +14,7 @@ #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (7) #define configMINIMAL_STACK_SIZE ((unsigned short)160) /* Only needs to be this high as some demo tasks also use this constant. In production only the idle task would use this. */ -#define configTOTAL_HEAP_SIZE ((size_t)1024*4) +#define configTOTAL_HEAP_SIZE ((size_t)1024 * 16) #define configMAX_TASK_NAME_LEN (24) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 diff --git a/source/Core/BSP/Pinecilv2/MemMang/heap_4.c b/source/Core/BSP/Pinecilv2/MemMang/heap_4.c new file mode 100644 index 0000000000..8008f2706b --- /dev/null +++ b/source/Core/BSP/Pinecilv2/MemMang/heap_4.c @@ -0,0 +1,494 @@ +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that combines + * (coalescences) adjacent memory blocks as they are freed, and in so doing + * limits memory fragmentation. + * + * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the + * memory management pages of https://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) + #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 +#endif + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) ) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ( ( size_t ) 8 ) + +/* Allocate the memory for the heap. */ +#if ( configAPPLICATION_ALLOCATED_HEAP == 1 ) + +/* The application writer has already defined the array used for the RTOS +* heap - probably so it can be placed in a special segment or address. */ + extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#else + PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#endif /* configAPPLICATION_ALLOCATED_HEAP */ + +/* Define the linked list structure. This is used to link free blocks in order + * of their memory address. */ +typedef struct A_BLOCK_LINK +{ + struct A_BLOCK_LINK * pxNextFreeBlock; /*<< The next free block in the list. */ + size_t xBlockSize; /*<< The size of the free block. */ +} BlockLink_t; + +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION; + +/* + * Called automatically to setup the required heap structures the first time + * pvPortMalloc() is called. + */ +static void prvHeapInit( void ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory + * block must by correctly byte aligned. */ +static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); + +/* Create a couple of list links to mark the start and end of the list. */ +PRIVILEGED_DATA static BlockLink_t xStart, * pxEnd = NULL; + +/* Keeps track of the number of calls to allocate and free memory as well as the + * number of free bytes remaining, but says nothing about fragmentation. */ +PRIVILEGED_DATA static size_t xFreeBytesRemaining = 0U; +PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = 0U; +PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = 0; +PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = 0; + +/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize + * member of an BlockLink_t structure is set then the block belongs to the + * application. When the bit is free the block is still part of the free heap + * space. */ +PRIVILEGED_DATA static size_t xBlockAllocatedBit = 0; + +/*-----------------------------------------------------------*/ + +void * pvPortMalloc( size_t xWantedSize ) +{ + BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink; + void * pvReturn = NULL; + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + * initialisation to setup the list of free blocks. */ + if( pxEnd == NULL ) + { + prvHeapInit(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Check the requested block size is not so large that the top bit is + * set. The top bit of the block size member of the BlockLink_t structure + * is used to determine who owns the block - the application or the + * kernel, so it must be free. */ + if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) + { + /* The wanted size is increased so it can contain a BlockLink_t + * structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += xHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + * of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) + { + /* Traverse the list from the start (lowest address) block until + * one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If the end marker was reached then a block of adequate size + * was not found. */ + if( pxBlock != pxEnd ) + { + /* Return the memory space pointed to - jumping over the + * BlockLink_t structure at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); + + /* This block is being returned for use so must be taken out + * of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + * two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new + * block following the number of bytes requested. The void + * cast is used to prevent byte alignment warnings from the + * compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); + configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); + + /* Calculate the sizes of two blocks split from the + * single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( pxNewBlockLink ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) + { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The block is being returned - it is allocated and owned + * by the application and has no "next" block. */ + pxBlock->xBlockSize |= xBlockAllocatedBit; + pxBlock->pxNextFreeBlock = NULL; + xNumberOfSuccessfulAllocations++; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if ( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */ + + configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void * pv ) +{ + uint8_t * puc = ( uint8_t * ) pv; + BlockLink_t * pxLink; + + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + * before it. */ + puc -= xHeapStructSize; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + /* Check the block is actually allocated. */ + configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); + configASSERT( pxLink->pxNextFreeBlock == NULL ); + + if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) + { + if( pxLink->pxNextFreeBlock == NULL ) + { + /* The block is being returned to the heap - it is no longer + * allocated. */ + pxLink->xBlockSize &= ~xBlockAllocatedBit; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE( pv, pxLink->xBlockSize ); + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + xNumberOfSuccessfulFrees++; + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetMinimumEverFreeHeapSize( void ) +{ + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */ +{ + BlockLink_t * pxFirstFreeBlock; + uint8_t * pucAlignedHeap; + size_t uxAddress; + size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; + + /* Ensure the heap starts on a correctly aligned boundary. */ + uxAddress = ( size_t ) ucHeap; + + if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + uxAddress += ( portBYTE_ALIGNMENT - 1 ); + uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); + xTotalHeapSize -= uxAddress - ( size_t ) ucHeap; + } + + pucAlignedHeap = ( uint8_t * ) uxAddress; + + /* xStart is used to hold a pointer to the first item in the list of free + * blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; + xStart.xBlockSize = ( size_t ) 0; + + /* pxEnd is used to mark the end of the list of free blocks and is inserted + * at the end of the heap space. */ + uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize; + uxAddress -= xHeapStructSize; + uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); + pxEnd = ( void * ) uxAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block that is sized to take up the + * entire heap space, minus the space taken by pxEnd. */ + pxFirstFreeBlock = ( void * ) pucAlignedHeap; + pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock; + pxFirstFreeBlock->pxNextFreeBlock = pxEnd; + + /* Only one block exists - and it covers the entire usable heap space. */ + xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */ +{ + BlockLink_t * pxIterator; + uint8_t * puc; + + /* Iterate through the list until a block is found that has a higher address + * than the block being inserted. */ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) + { + /* Nothing to do here, just iterate to the right position. */ + } + + /* Do the block being inserted, and the block it is being inserted after + * make a contiguous block of memory? */ + puc = ( uint8_t * ) pxIterator; + + if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) + { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Do the block being inserted, and the block it is being inserted before + * make a contiguous block of memory? */ + puc = ( uint8_t * ) pxBlockToInsert; + + if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) + { + if( pxIterator->pxNextFreeBlock != pxEnd ) + { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gab, so was merged with the block + * before and the block after, then it's pxNextFreeBlock pointer will have + * already been set, and should not be set here as that would make it point + * to itself. */ + if( pxIterator != pxBlockToInsert ) + { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +void vPortGetHeapStats( HeapStats_t * pxHeapStats ) +{ + BlockLink_t * pxBlock; + size_t xBlocks = 0, xMaxSize = 0, xMinSize = portMAX_DELAY; /* portMAX_DELAY used as a portable way of getting the maximum value. */ + + vTaskSuspendAll(); + { + pxBlock = xStart.pxNextFreeBlock; + + /* pxBlock will be NULL if the heap has not been initialised. The heap + * is initialised automatically when the first allocation is made. */ + if( pxBlock != NULL ) + { + do + { + /* Increment the number of blocks and record the largest block seen + * so far. */ + xBlocks++; + + if( pxBlock->xBlockSize > xMaxSize ) + { + xMaxSize = pxBlock->xBlockSize; + } + + if( pxBlock->xBlockSize < xMinSize ) + { + xMinSize = pxBlock->xBlockSize; + } + + /* Move to the next block in the chain until the last block is + * reached. */ + pxBlock = pxBlock->pxNextFreeBlock; + } while( pxBlock != pxEnd ); + } + } + ( void ) xTaskResumeAll(); + + pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize; + pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize; + pxHeapStats->xNumberOfFreeBlocks = xBlocks; + + taskENTER_CRITICAL(); + { + pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining; + pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations; + pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees; + pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining; + } + taskEXIT_CRITICAL(); +} diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h index 948d34eba6..2cf94f98f7 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/ble_inc/ble_lib_api.h @@ -19,60 +19,55 @@ int8_t ble_controller_set_scan_filter_table_size(uint8_t size); // 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); -void ble_controller_sleep_restore(void); -bool ble_controller_sleep_is_ongoing(void); +void ble_controller_sleep_restore(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 -}; +enum { BLE_TEST_TX = 0x00, BLE_TEST_RX, BLE_TEST_RXTX, BLE_TEST_END }; -///HCI LE Receiver Test Command parameters structure +/// HCI LE Receiver Test Command parameters structure struct le_rx_test_cmd { - ///RX frequency for Rx test - uint8_t rx_freq; + /// RX frequency for Rx test + uint8_t rx_freq; }; -///HCI LE Transmitter Test Command parameters structure +/// 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; + /// 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; + /// 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 +/// 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; + /// 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); +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) diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c index ecd9e46ee5..9a17425536 100644 --- a/source/Core/BSP/Pinecilv2/ble.c +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -61,12 +61,12 @@ void ble_stack_start(void) GLB_Set_EM_Sel(GLB_EM_8KB); ble_controller_init(configMAX_PRIORITIES - 1); - // Initialize BLE Host stack - MSG("[OS] hci_driver_init...\r\n"); - hci_driver_init(); + // // Initialize BLE Host stack + // MSG("[OS] hci_driver_init...\r\n"); + // hci_driver_init(); - MSG("[OS] bt_enable...\r\n"); - bt_enable(bt_enable_cb); + // MSG("[OS] bt_enable...\r\n"); + // bt_enable(bt_enable_cb); } diff --git a/source/Core/Inc/main.hpp b/source/Core/Inc/main.hpp index a3bc02b62c..b4f476ade8 100644 --- a/source/Core/Inc/main.hpp +++ b/source/Core/Inc/main.hpp @@ -1,8 +1,8 @@ #ifndef __MAIN_H #define __MAIN_H -#include #include "OLED.hpp" #include "Setup.h" +#include extern volatile uint32_t currentTempTargetDegC; extern bool settingsWereReset; extern bool usb_pd_available; @@ -10,7 +10,7 @@ extern bool usb_pd_available; extern "C" { #endif -void vApplicationStackOverflowHook(TaskHandle_t *pxTask, signed portCHAR *pcTaskName); +void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName); // Threads void startGUITask(void const *argument); diff --git a/source/Core/Src/FreeRTOSHooks.c b/source/Core/Src/FreeRTOSHooks.c index a4fec3816b..dce6771c1c 100644 --- a/source/Core/Src/FreeRTOSHooks.c +++ b/source/Core/Src/FreeRTOSHooks.c @@ -20,8 +20,9 @@ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackTyp /* place for user code */ } -void vApplicationStackOverflowHook(TaskHandle_t *pxTask, signed char *pcTaskName) { - (void)pxTask; +void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ) { + (void)xTask; (void)pcTaskName; // We dont have a good way to handle a stack overflow at this point in time diff --git a/source/Makefile b/source/Makefile index 4e162543c6..5e4cb27406 100644 --- a/source/Makefile +++ b/source/Makefile @@ -405,11 +405,7 @@ LINKER_FLAGS= -Wl,--gc-sections \ # compiler flags --------------------------------------------------------------- -CHECKOPTIONS= -Wall \ - -Wextra \ - -Wunused \ - -Wcomment \ - -Wtrigraphs \ +CHECKOPTIONS=-Wtrigraphs \ -Wuninitialized \ -Wmissing-braces \ -Wfloat-equal \ @@ -428,7 +424,6 @@ CHECKOPTIONS= -Wall \ -Waddress \ -Waggregate-return \ -Wmissing-field-initializers \ - -Winline \ -Wshadow \ -Wno-unused-parameter \ -Wdouble-promotion diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c b/source/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c index 552f583c2c..c261427b54 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c @@ -548,107 +548,6 @@ osStatus osTimerDelete(osTimerId timer_id) return result; } -/*************************** Signal Management ********************************/ -/** -* @brief Set the specified Signal Flags of an active thread. -* @param thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. -* @param signals specifies the signal flags of the thread that should be set. -* @retval previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. -* @note MUST REMAIN UNCHANGED: \b osSignalSet shall be consistent in every CMSIS-RTOS. -*/ -int32_t osSignalSet(osThreadId thread_id, int32_t signal) -{ -#if (configUSE_TASK_NOTIFICATIONS == 1) - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - uint32_t ulPreviousNotificationValue = 0; - - if (inHandlerMode()) - { - if (xTaskGenericNotifyFromISR(thread_id, (uint32_t)signal, eSetBits, &ulPreviousNotificationValue, &xHigherPriorityTaskWoken) != pdPASS) - return 0x80000000; - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } - else if (xTaskGenericNotify(thread_id, (uint32_t)signal, eSetBits, &ulPreviousNotificationValue) != pdPASS) - return 0x80000000; - - return ulPreviousNotificationValue; -#else - (void)thread_id; - (void)signal; - - return 0x80000000; /* Task Notification not supported */ -#endif -} - -/** -* @brief Clear the specified Signal Flags of an active thread. -* @param thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. -* @param signals specifies the signal flags of the thread that shall be cleared. -* @retval previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. -* @note MUST REMAIN UNCHANGED: \b osSignalClear shall be consistent in every CMSIS-RTOS. -*/ -int32_t osSignalClear(osThreadId thread_id, int32_t signal); - -/** -* @brief Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread. -* @param signals wait until all specified signal flags set or 0 for any single signal flag. -* @param millisec timeout value or 0 in case of no time-out. -* @retval event flag information or error code. -* @note MUST REMAIN UNCHANGED: \b osSignalWait shall be consistent in every CMSIS-RTOS. -*/ -osEvent osSignalWait(int32_t signals, uint32_t millisec) -{ - osEvent ret; - -#if (configUSE_TASK_NOTIFICATIONS == 1) - - TickType_t ticks; - - ret.value.signals = 0; - ticks = 0; - if (millisec == osWaitForever) - { - ticks = portMAX_DELAY; - } - else if (millisec != 0) - { - ticks = millisec / portTICK_PERIOD_MS; - if (ticks == 0) - { - ticks = 1; - } - } - - if (inHandlerMode()) - { - ret.status = osErrorISR; /*Not allowed in ISR*/ - } - else - { - if (xTaskNotifyWait(0, (uint32_t)signals, (uint32_t *)&ret.value.signals, ticks) != pdTRUE) - { - if (ticks == 0) - ret.status = osOK; - else - ret.status = osEventTimeout; - } - else if (ret.value.signals < 0) - { - ret.status = osErrorValue; - } - else - ret.status = osEventSignal; - } -#else - (void)signals; - (void)millisec; - - ret.status = osErrorOS; /* Task Notification not supported */ -#endif - - return ret; -} /**************************** Mutex Management ********************************/ /** diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/croutine.c b/source/Middlewares/Third_Party/FreeRTOS/Source/croutine.c index 507e217902..6959fac3c7 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/croutine.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/croutine.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #include "FreeRTOS.h" @@ -30,32 +29,32 @@ #include "croutine.h" /* Remove the whole file is co-routines are not being used. */ -#if( configUSE_CO_ROUTINES != 0 ) +#if ( configUSE_CO_ROUTINES != 0 ) /* * Some kernel aware debuggers require data to be viewed to be global, rather * than file scope. */ -#ifdef portREMOVE_STATIC_QUALIFIER - #define static -#endif + #ifdef portREMOVE_STATIC_QUALIFIER + #define static + #endif /* Lists for ready and blocked co-routines. --------------------*/ -static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ -static List_t xDelayedCoRoutineList1; /*< Delayed co-routines. */ -static List_t xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ -static List_t * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */ -static List_t * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ -static List_t xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ + static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ + static List_t xDelayedCoRoutineList1; /*< Delayed co-routines. */ + static List_t xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ + static List_t * pxDelayedCoRoutineList = NULL; /*< Points to the delayed co-routine list currently being used. */ + static List_t * pxOverflowDelayedCoRoutineList = NULL; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ + static List_t xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ /* Other file private variables. --------------------------------*/ -CRCB_t * pxCurrentCoRoutine = NULL; -static UBaseType_t uxTopCoRoutineReadyPriority = 0; -static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; + CRCB_t * pxCurrentCoRoutine = NULL; + static UBaseType_t uxTopCoRoutineReadyPriority = 0; + static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; /* The initial state of the co-routine when it is created. */ -#define corINITIAL_STATE ( 0 ) + #define corINITIAL_STATE ( 0 ) /* * Place the co-routine represented by pxCRCB into the appropriate ready queue @@ -64,20 +63,20 @@ static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; * This macro accesses the co-routine ready lists and therefore must not be * used from within an ISR. */ -#define prvAddCoRoutineToReadyQueue( pxCRCB ) \ -{ \ - if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ - { \ - uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ - } \ - vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ -} + #define prvAddCoRoutineToReadyQueue( pxCRCB ) \ + { \ + if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ + { \ + uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ + } \ + vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ + } /* * Utility to ready all the lists used by the scheduler. This is called * automatically upon the creation of the first co-routine. */ -static void prvInitialiseCoRoutineLists( void ); + static void prvInitialiseCoRoutineLists( void ); /* * Co-routines that are readied by an interrupt cannot be placed directly into @@ -85,7 +84,7 @@ static void prvInitialiseCoRoutineLists( void ); * in the pending ready list in order that they can later be moved to the ready * list by the co-routine scheduler. */ -static void prvCheckPendingReadyList( void ); + static void prvCheckPendingReadyList( void ); /* * Macro that looks at the list of co-routines that are currently delayed to @@ -95,259 +94,268 @@ static void prvCheckPendingReadyList( void ); * meaning once one co-routine has been found whose timer has not expired * we need not look any further down the list. */ -static void prvCheckDelayedList( void ); + static void prvCheckDelayedList( void ); /*-----------------------------------------------------------*/ -BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex ) -{ -BaseType_t xReturn; -CRCB_t *pxCoRoutine; - - /* Allocate the memory that will store the co-routine control block. */ - pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) ); - if( pxCoRoutine ) - { - /* If pxCurrentCoRoutine is NULL then this is the first co-routine to - be created and the co-routine data structures need initialising. */ - if( pxCurrentCoRoutine == NULL ) - { - pxCurrentCoRoutine = pxCoRoutine; - prvInitialiseCoRoutineLists(); - } - - /* Check the priority is within limits. */ - if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) - { - uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; - } - - /* Fill out the co-routine control block from the function parameters. */ - pxCoRoutine->uxState = corINITIAL_STATE; - pxCoRoutine->uxPriority = uxPriority; - pxCoRoutine->uxIndex = uxIndex; - pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; - - /* Initialise all the other co-routine control block parameters. */ - vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); - vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); - - /* Set the co-routine control block as a link back from the ListItem_t. - This is so we can get back to the containing CRCB from a generic item - in a list. */ - listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); - listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); - - /* Event lists are always in priority order. */ - listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) ); - - /* Now the co-routine has been initialised it can be added to the ready - list at the correct priority. */ - prvAddCoRoutineToReadyQueue( pxCoRoutine ); - - xReturn = pdPASS; - } - else - { - xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - } - - return xReturn; -} + BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, + UBaseType_t uxPriority, + UBaseType_t uxIndex ) + { + BaseType_t xReturn; + CRCB_t * pxCoRoutine; + + /* Allocate the memory that will store the co-routine control block. */ + pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) ); + + if( pxCoRoutine ) + { + /* If pxCurrentCoRoutine is NULL then this is the first co-routine to + * be created and the co-routine data structures need initialising. */ + if( pxCurrentCoRoutine == NULL ) + { + pxCurrentCoRoutine = pxCoRoutine; + prvInitialiseCoRoutineLists(); + } + + /* Check the priority is within limits. */ + if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) + { + uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; + } + + /* Fill out the co-routine control block from the function parameters. */ + pxCoRoutine->uxState = corINITIAL_STATE; + pxCoRoutine->uxPriority = uxPriority; + pxCoRoutine->uxIndex = uxIndex; + pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; + + /* Initialise all the other co-routine control block parameters. */ + vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); + vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); + + /* Set the co-routine control block as a link back from the ListItem_t. + * This is so we can get back to the containing CRCB from a generic item + * in a list. */ + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) ); + + /* Now the co-routine has been initialised it can be added to the ready + * list at the correct priority. */ + prvAddCoRoutineToReadyQueue( pxCoRoutine ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; + } /*-----------------------------------------------------------*/ -void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList ) -{ -TickType_t xTimeToWake; - - /* Calculate the time to wake - this may overflow but this is - not a problem. */ - xTimeToWake = xCoRoutineTickCount + xTicksToDelay; - - /* We must remove ourselves from the ready list before adding - ourselves to the blocked list as the same list item is used for - both lists. */ - ( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); - - /* The list item will be inserted in wake time order. */ - listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); - - if( xTimeToWake < xCoRoutineTickCount ) - { - /* Wake time has overflowed. Place this item in the - overflow list. */ - vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); - } - else - { - /* The wake time has not overflowed, so we can use the - current block list. */ - vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); - } - - if( pxEventList ) - { - /* Also add the co-routine to an event list. If this is done then the - function must be called with interrupts disabled. */ - vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); - } -} + void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, + List_t * pxEventList ) + { + TickType_t xTimeToWake; + + /* Calculate the time to wake - this may overflow but this is + * not a problem. */ + xTimeToWake = xCoRoutineTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + * ourselves to the blocked list as the same list item is used for + * both lists. */ + ( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xCoRoutineTickCount ) + { + /* Wake time has overflowed. Place this item in the + * overflow list. */ + vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + * current block list. */ + vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + + if( pxEventList ) + { + /* Also add the co-routine to an event list. If this is done then the + * function must be called with interrupts disabled. */ + vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); + } + } /*-----------------------------------------------------------*/ -static void prvCheckPendingReadyList( void ) -{ - /* Are there any co-routines waiting to get moved to the ready list? These - are co-routines that have been readied by an ISR. The ISR cannot access - the ready lists itself. */ - while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE ) - { - CRCB_t *pxUnblockedCRCB; - - /* The pending ready list can be accessed by an ISR. */ - portDISABLE_INTERRUPTS(); - { - pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) ); - ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); - } - portENABLE_INTERRUPTS(); - - ( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); - prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); - } -} + static void prvCheckPendingReadyList( void ) + { + /* Are there any co-routines waiting to get moved to the ready list? These + * are co-routines that have been readied by an ISR. The ISR cannot access + * the ready lists itself. */ + while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE ) + { + CRCB_t * pxUnblockedCRCB; + + /* The pending ready list can be accessed by an ISR. */ + portDISABLE_INTERRUPTS(); + { + pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyCoRoutineList ) ); + ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + } + portENABLE_INTERRUPTS(); + + ( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); + prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); + } + } /*-----------------------------------------------------------*/ -static void prvCheckDelayedList( void ) -{ -CRCB_t *pxCRCB; - - xPassedTicks = xTaskGetTickCount() - xLastTickCount; - while( xPassedTicks ) - { - xCoRoutineTickCount++; - xPassedTicks--; - - /* If the tick count has overflowed we need to swap the ready lists. */ - if( xCoRoutineTickCount == 0 ) - { - List_t * pxTemp; - - /* Tick count has overflowed so we need to swap the delay lists. If there are - any items in pxDelayedCoRoutineList here then there is an error! */ - pxTemp = pxDelayedCoRoutineList; - pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; - pxOverflowDelayedCoRoutineList = pxTemp; - } - - /* See if this tick has made a timeout expire. */ - while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE ) - { - pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ); - - if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) - { - /* Timeout not yet expired. */ - break; - } - - portDISABLE_INTERRUPTS(); - { - /* The event could have occurred just before this critical - section. If this is the case then the generic list item will - have been moved to the pending ready list and the following - line is still valid. Also the pvContainer parameter will have - been set to NULL so the following lines are also valid. */ - ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) ); - - /* Is the co-routine waiting on an event also? */ - if( pxCRCB->xEventListItem.pxContainer ) - { - ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); - } - } - portENABLE_INTERRUPTS(); - - prvAddCoRoutineToReadyQueue( pxCRCB ); - } - } - - xLastTickCount = xCoRoutineTickCount; -} + static void prvCheckDelayedList( void ) + { + CRCB_t * pxCRCB; + + xPassedTicks = xTaskGetTickCount() - xLastTickCount; + + while( xPassedTicks ) + { + xCoRoutineTickCount++; + xPassedTicks--; + + /* If the tick count has overflowed we need to swap the ready lists. */ + if( xCoRoutineTickCount == 0 ) + { + List_t * pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. If there are + * any items in pxDelayedCoRoutineList here then there is an error! */ + pxTemp = pxDelayedCoRoutineList; + pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; + pxOverflowDelayedCoRoutineList = pxTemp; + } + + /* See if this tick has made a timeout expire. */ + while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE ) + { + pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ); + + if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) + { + /* Timeout not yet expired. */ + break; + } + + portDISABLE_INTERRUPTS(); + { + /* The event could have occurred just before this critical + * section. If this is the case then the generic list item will + * have been moved to the pending ready list and the following + * line is still valid. Also the pvContainer parameter will have + * been set to NULL so the following lines are also valid. */ + ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) ); + + /* Is the co-routine waiting on an event also? */ + if( pxCRCB->xEventListItem.pxContainer ) + { + ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); + } + } + portENABLE_INTERRUPTS(); + + prvAddCoRoutineToReadyQueue( pxCRCB ); + } + } + + xLastTickCount = xCoRoutineTickCount; + } /*-----------------------------------------------------------*/ -void vCoRoutineSchedule( void ) -{ - /* See if any co-routines readied by events need moving to the ready lists. */ - prvCheckPendingReadyList(); - - /* See if any delayed co-routines have timed out. */ - prvCheckDelayedList(); - - /* Find the highest priority queue that contains ready co-routines. */ - while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) - { - if( uxTopCoRoutineReadyPriority == 0 ) - { - /* No more co-routines to check. */ - return; - } - --uxTopCoRoutineReadyPriority; - } - - /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines - of the same priority get an equal share of the processor time. */ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); - - /* Call the co-routine. */ - ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); - - return; -} + void vCoRoutineSchedule( void ) + { + /* Only run a co-routine after prvInitialiseCoRoutineLists() has been + * called. prvInitialiseCoRoutineLists() is called automatically when a + * co-routine is created. */ + if( pxDelayedCoRoutineList != NULL ) + { + /* See if any co-routines readied by events need moving to the ready lists. */ + prvCheckPendingReadyList(); + + /* See if any delayed co-routines have timed out. */ + prvCheckDelayedList(); + + /* Find the highest priority queue that contains ready co-routines. */ + while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) + { + if( uxTopCoRoutineReadyPriority == 0 ) + { + /* No more co-routines to check. */ + return; + } + + --uxTopCoRoutineReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines + * of the same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); + + /* Call the co-routine. */ + ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); + } + } /*-----------------------------------------------------------*/ -static void prvInitialiseCoRoutineLists( void ) -{ -UBaseType_t uxPriority; + static void prvInitialiseCoRoutineLists( void ) + { + UBaseType_t uxPriority; - for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) - { - vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); - } + for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); + } - vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 ); - vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 ); - vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList ); + vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 ); + vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 ); + vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList ); - /* Start with pxDelayedCoRoutineList using list1 and the - pxOverflowDelayedCoRoutineList using list2. */ - pxDelayedCoRoutineList = &xDelayedCoRoutineList1; - pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; -} + /* Start with pxDelayedCoRoutineList using list1 and the + * pxOverflowDelayedCoRoutineList using list2. */ + pxDelayedCoRoutineList = &xDelayedCoRoutineList1; + pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; + } /*-----------------------------------------------------------*/ -BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList ) -{ -CRCB_t *pxUnblockedCRCB; -BaseType_t xReturn; - - /* This function is called from within an interrupt. It can only access - event lists and the pending ready list. This function assumes that a - check has already been made to ensure pxEventList is not empty. */ - pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); - ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); - vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) ); - - if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) - { - xReturn = pdTRUE; - } - else - { - xReturn = pdFALSE; - } - - return xReturn; -} + BaseType_t xCoRoutineRemoveFromEventList( const List_t * pxEventList ) + { + CRCB_t * pxUnblockedCRCB; + BaseType_t xReturn; + + /* This function is called from within an interrupt. It can only access + * event lists and the pending ready list. This function assumes that a + * check has already been made to ensure pxEventList is not empty. */ + pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) ); + + if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; + } #endif /* configUSE_CO_ROUTINES == 0 */ - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c b/source/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c index 0bf3b9661a..c46013ab3b 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/event_groups.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,18 +19,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /* Standard includes. */ #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining -all the API functions to use the MPU wrappers. That should only be done when -task.h is included from an application file. */ + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE /* FreeRTOS includes. */ @@ -40,38 +39,38 @@ task.h is included from an application file. */ #include "event_groups.h" /* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified -because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined -for the header files above, but not in this file, in order to generate the -correct privileged Vs unprivileged linkage and placement. */ + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */ /* The following bit fields convey control information in a task's event list -item value. It is important they don't clash with the -taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */ + * item value. It is important they don't clash with the + * taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */ #if configUSE_16_BIT_TICKS == 1 - #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x0100U - #define eventUNBLOCKED_DUE_TO_BIT_SET 0x0200U - #define eventWAIT_FOR_ALL_BITS 0x0400U - #define eventEVENT_BITS_CONTROL_BYTES 0xff00U + #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x0100U + #define eventUNBLOCKED_DUE_TO_BIT_SET 0x0200U + #define eventWAIT_FOR_ALL_BITS 0x0400U + #define eventEVENT_BITS_CONTROL_BYTES 0xff00U #else - #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL - #define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL - #define eventWAIT_FOR_ALL_BITS 0x04000000UL - #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL + #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL + #define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL + #define eventWAIT_FOR_ALL_BITS 0x04000000UL + #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL #endif typedef struct EventGroupDef_t { - EventBits_t uxEventBits; - List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ + EventBits_t uxEventBits; + List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ - #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxEventGroupNumber; - #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxEventGroupNumber; + #endif - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */ - #endif + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */ + #endif } EventGroup_t; /*-----------------------------------------------------------*/ @@ -84,670 +83,689 @@ typedef struct EventGroupDef_t * wait condition is met if any of the bits set in uxBitsToWait for are also set * in uxCurrentEventBits. */ -static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION; +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - - EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) - { - EventGroup_t *pxEventBits; - - /* A StaticEventGroup_t object must be provided. */ - configASSERT( pxEventGroupBuffer ); - - #if( configASSERT_DEFINED == 1 ) - { - /* Sanity check that the size of the structure used to declare a - variable of type StaticEventGroup_t equals the size of the real - event group structure. */ - volatile size_t xSize = sizeof( StaticEventGroup_t ); - configASSERT( xSize == sizeof( EventGroup_t ) ); - } /*lint !e529 xSize is referenced if configASSERT() is defined. */ - #endif /* configASSERT_DEFINED */ - - /* The user has provided a statically allocated event group - use it. */ - pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ - - if( pxEventBits != NULL ) - { - pxEventBits->uxEventBits = 0; - vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); - - #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - /* Both static and dynamic allocation can be used, so note that - this event group was created statically in case the event group - is later deleted. */ - pxEventBits->ucStaticallyAllocated = pdTRUE; - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - - traceEVENT_GROUP_CREATE( pxEventBits ); - } - else - { - /* xEventGroupCreateStatic should only ever be called with - pxEventGroupBuffer pointing to a pre-allocated (compile time - allocated) StaticEventGroup_t variable. */ - traceEVENT_GROUP_CREATE_FAILED(); - } - - return pxEventBits; - } +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) + { + EventGroup_t * pxEventBits; + + /* A StaticEventGroup_t object must be provided. */ + configASSERT( pxEventGroupBuffer ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticEventGroup_t equals the size of the real + * event group structure. */ + volatile size_t xSize = sizeof( StaticEventGroup_t ); + configASSERT( xSize == sizeof( EventGroup_t ) ); + } /*lint !e529 xSize is referenced if configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + + /* The user has provided a statically allocated event group - use it. */ + pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note that + * this event group was created statically in case the event group + * is later deleted. */ + pxEventBits->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + /* xEventGroupCreateStatic should only ever be called with + * pxEventGroupBuffer pointing to a pre-allocated (compile time + * allocated) StaticEventGroup_t variable. */ + traceEVENT_GROUP_CREATE_FAILED(); + } + + return pxEventBits; + } #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - - EventGroupHandle_t xEventGroupCreate( void ) - { - EventGroup_t *pxEventBits; - - /* Allocate the event group. Justification for MISRA deviation as - follows: pvPortMalloc() always ensures returned memory blocks are - aligned per the requirements of the MCU stack. In this case - pvPortMalloc() must return a pointer that is guaranteed to meet the - alignment requirements of the EventGroup_t structure - which (if you - follow it through) is the alignment requirements of the TickType_t type - (EventBits_t being of TickType_t itself). Therefore, whenever the - stack alignment requirements are greater than or equal to the - TickType_t alignment requirements the cast is safe. In other cases, - where the natural word size of the architecture is less than - sizeof( TickType_t ), the TickType_t variables will be accessed in two - or more reads operations, and the alignment requirements is only that - of each individual read. */ - pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */ - - if( pxEventBits != NULL ) - { - pxEventBits->uxEventBits = 0; - vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); - - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - /* Both static and dynamic allocation can be used, so note this - event group was allocated statically in case the event group is - later deleted. */ - pxEventBits->ucStaticallyAllocated = pdFALSE; - } - #endif /* configSUPPORT_STATIC_ALLOCATION */ - - traceEVENT_GROUP_CREATE( pxEventBits ); - } - else - { - traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */ - } - - return pxEventBits; - } +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + EventGroupHandle_t xEventGroupCreate( void ) + { + EventGroup_t * pxEventBits; + + /* Allocate the event group. Justification for MISRA deviation as + * follows: pvPortMalloc() always ensures returned memory blocks are + * aligned per the requirements of the MCU stack. In this case + * pvPortMalloc() must return a pointer that is guaranteed to meet the + * alignment requirements of the EventGroup_t structure - which (if you + * follow it through) is the alignment requirements of the TickType_t type + * (EventBits_t being of TickType_t itself). Therefore, whenever the + * stack alignment requirements are greater than or equal to the + * TickType_t alignment requirements the cast is safe. In other cases, + * where the natural word size of the architecture is less than + * sizeof( TickType_t ), the TickType_t variables will be accessed in two + * or more reads operations, and the alignment requirements is only that + * of each individual read. */ + pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */ + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note this + * event group was allocated statically in case the event group is + * later deleted. */ + pxEventBits->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */ + } + + return pxEventBits; + } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ -EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + const EventBits_t uxBitsToWaitFor, + TickType_t xTicksToWait ) { -EventBits_t uxOriginalBitValue, uxReturn; -EventGroup_t *pxEventBits = xEventGroup; -BaseType_t xAlreadyYielded; -BaseType_t xTimeoutOccurred = pdFALSE; - - configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); - configASSERT( uxBitsToWaitFor != 0 ); - #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) - { - configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); - } - #endif - - vTaskSuspendAll(); - { - uxOriginalBitValue = pxEventBits->uxEventBits; - - ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet ); - - if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor ) - { - /* All the rendezvous bits are now set - no need to block. */ - uxReturn = ( uxOriginalBitValue | uxBitsToSet ); - - /* Rendezvous always clear the bits. They will have been cleared - already unless this is the only task in the rendezvous. */ - pxEventBits->uxEventBits &= ~uxBitsToWaitFor; - - xTicksToWait = 0; - } - else - { - if( xTicksToWait != ( TickType_t ) 0 ) - { - traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ); - - /* Store the bits that the calling task is waiting for in the - task's event list item so the kernel knows when a match is - found. Then enter the blocked state. */ - vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait ); - - /* This assignment is obsolete as uxReturn will get set after - the task unblocks, but some compilers mistakenly generate a - warning about uxReturn being returned without being set if the - assignment is omitted. */ - uxReturn = 0; - } - else - { - /* The rendezvous bits were not set, but no block time was - specified - just return the current event bit value. */ - uxReturn = pxEventBits->uxEventBits; - xTimeoutOccurred = pdTRUE; - } - } - } - xAlreadyYielded = xTaskResumeAll(); - - if( xTicksToWait != ( TickType_t ) 0 ) - { - if( xAlreadyYielded == pdFALSE ) - { - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* The task blocked to wait for its required bits to be set - at this - point either the required bits were set or the block time expired. If - the required bits were set they will have been stored in the task's - event list item, and they should now be retrieved then cleared. */ - uxReturn = uxTaskResetEventItemValue(); - - if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) - { - /* The task timed out, just return the current event bit value. */ - taskENTER_CRITICAL(); - { - uxReturn = pxEventBits->uxEventBits; - - /* Although the task got here because it timed out before the - bits it was waiting for were set, it is possible that since it - unblocked another task has set the bits. If this is the case - then it needs to clear the bits before exiting. */ - if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor ) - { - pxEventBits->uxEventBits &= ~uxBitsToWaitFor; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - xTimeoutOccurred = pdTRUE; - } - else - { - /* The task unblocked because the bits were set. */ - } - - /* Control bits might be set as the task had blocked should not be - returned. */ - uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; - } - - traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); - - /* Prevent compiler warnings when trace macros are not used. */ - ( void ) xTimeoutOccurred; - - return uxReturn; + EventBits_t uxOriginalBitValue, uxReturn; + EventGroup_t * pxEventBits = xEventGroup; + BaseType_t xAlreadyYielded; + BaseType_t xTimeoutOccurred = pdFALSE; + + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + uxOriginalBitValue = pxEventBits->uxEventBits; + + ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet ); + + if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + /* All the rendezvous bits are now set - no need to block. */ + uxReturn = ( uxOriginalBitValue | uxBitsToSet ); + + /* Rendezvous always clear the bits. They will have been cleared + * already unless this is the only task in the rendezvous. */ + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + + xTicksToWait = 0; + } + else + { + if( xTicksToWait != ( TickType_t ) 0 ) + { + traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ); + + /* Store the bits that the calling task is waiting for in the + * task's event list item so the kernel knows when a match is + * found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait ); + + /* This assignment is obsolete as uxReturn will get set after + * the task unblocks, but some compilers mistakenly generate a + * warning about uxReturn being returned without being set if the + * assignment is omitted. */ + uxReturn = 0; + } + else + { + /* The rendezvous bits were not set, but no block time was + * specified - just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + xTimeoutOccurred = pdTRUE; + } + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + * point either the required bits were set or the block time expired. If + * the required bits were set they will have been stored in the task's + * event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + /* The task timed out, just return the current event bit value. */ + taskENTER_CRITICAL(); + { + uxReturn = pxEventBits->uxEventBits; + + /* Although the task got here because it timed out before the + * bits it was waiting for were set, it is possible that since it + * unblocked another task has set the bits. If this is the case + * then it needs to clear the bits before exiting. */ + if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + xTimeoutOccurred = pdTRUE; + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* Control bits might be set as the task had blocked should not be + * returned. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + + traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); + + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + + return uxReturn; } /*-----------------------------------------------------------*/ -EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait ) { -EventGroup_t *pxEventBits = xEventGroup; -EventBits_t uxReturn, uxControlBits = 0; -BaseType_t xWaitConditionMet, xAlreadyYielded; -BaseType_t xTimeoutOccurred = pdFALSE; - - /* Check the user is not attempting to wait on the bits used by the kernel - itself, and that at least one bit is being requested. */ - configASSERT( xEventGroup ); - configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); - configASSERT( uxBitsToWaitFor != 0 ); - #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) - { - configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); - } - #endif - - vTaskSuspendAll(); - { - const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits; - - /* Check to see if the wait condition is already met or not. */ - xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits ); - - if( xWaitConditionMet != pdFALSE ) - { - /* The wait condition has already been met so there is no need to - block. */ - uxReturn = uxCurrentEventBits; - xTicksToWait = ( TickType_t ) 0; - - /* Clear the wait bits if requested to do so. */ - if( xClearOnExit != pdFALSE ) - { - pxEventBits->uxEventBits &= ~uxBitsToWaitFor; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else if( xTicksToWait == ( TickType_t ) 0 ) - { - /* The wait condition has not been met, but no block time was - specified, so just return the current value. */ - uxReturn = uxCurrentEventBits; - xTimeoutOccurred = pdTRUE; - } - else - { - /* The task is going to block to wait for its required bits to be - set. uxControlBits are used to remember the specified behaviour of - this call to xEventGroupWaitBits() - for use when the event bits - unblock the task. */ - if( xClearOnExit != pdFALSE ) - { - uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - if( xWaitForAllBits != pdFALSE ) - { - uxControlBits |= eventWAIT_FOR_ALL_BITS; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Store the bits that the calling task is waiting for in the - task's event list item so the kernel knows when a match is - found. Then enter the blocked state. */ - vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait ); - - /* This is obsolete as it will get set after the task unblocks, but - some compilers mistakenly generate a warning about the variable - being returned without being set if it is not done. */ - uxReturn = 0; - - traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); - } - } - xAlreadyYielded = xTaskResumeAll(); - - if( xTicksToWait != ( TickType_t ) 0 ) - { - if( xAlreadyYielded == pdFALSE ) - { - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* The task blocked to wait for its required bits to be set - at this - point either the required bits were set or the block time expired. If - the required bits were set they will have been stored in the task's - event list item, and they should now be retrieved then cleared. */ - uxReturn = uxTaskResetEventItemValue(); - - if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) - { - taskENTER_CRITICAL(); - { - /* The task timed out, just return the current event bit value. */ - uxReturn = pxEventBits->uxEventBits; - - /* It is possible that the event bits were updated between this - task leaving the Blocked state and running again. */ - if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ) - { - if( xClearOnExit != pdFALSE ) - { - pxEventBits->uxEventBits &= ~uxBitsToWaitFor; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - xTimeoutOccurred = pdTRUE; - } - taskEXIT_CRITICAL(); - } - else - { - /* The task unblocked because the bits were set. */ - } - - /* The task blocked so control bits may have been set. */ - uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; - } - traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); - - /* Prevent compiler warnings when trace macros are not used. */ - ( void ) xTimeoutOccurred; - - return uxReturn; + EventGroup_t * pxEventBits = xEventGroup; + EventBits_t uxReturn, uxControlBits = 0; + BaseType_t xWaitConditionMet, xAlreadyYielded; + BaseType_t xTimeoutOccurred = pdFALSE; + + /* Check the user is not attempting to wait on the bits used by the kernel + * itself, and that at least one bit is being requested. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits; + + /* Check to see if the wait condition is already met or not. */ + xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits ); + + if( xWaitConditionMet != pdFALSE ) + { + /* The wait condition has already been met so there is no need to + * block. */ + uxReturn = uxCurrentEventBits; + xTicksToWait = ( TickType_t ) 0; + + /* Clear the wait bits if requested to do so. */ + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The wait condition has not been met, but no block time was + * specified, so just return the current value. */ + uxReturn = uxCurrentEventBits; + xTimeoutOccurred = pdTRUE; + } + else + { + /* The task is going to block to wait for its required bits to be + * set. uxControlBits are used to remember the specified behaviour of + * this call to xEventGroupWaitBits() - for use when the event bits + * unblock the task. */ + if( xClearOnExit != pdFALSE ) + { + uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xWaitForAllBits != pdFALSE ) + { + uxControlBits |= eventWAIT_FOR_ALL_BITS; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the bits that the calling task is waiting for in the + * task's event list item so the kernel knows when a match is + * found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait ); + + /* This is obsolete as it will get set after the task unblocks, but + * some compilers mistakenly generate a warning about the variable + * being returned without being set if it is not done. */ + uxReturn = 0; + + traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + * point either the required bits were set or the block time expired. If + * the required bits were set they will have been stored in the task's + * event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + taskENTER_CRITICAL(); + { + /* The task timed out, just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + + /* It is possible that the event bits were updated between this + * task leaving the Blocked state and running again. */ + if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ) + { + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xTimeoutOccurred = pdTRUE; + } + taskEXIT_CRITICAL(); + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* The task blocked so control bits may have been set. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + + traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); + + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + + return uxReturn; } /*-----------------------------------------------------------*/ -EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) { -EventGroup_t *pxEventBits = xEventGroup; -EventBits_t uxReturn; + EventGroup_t * pxEventBits = xEventGroup; + EventBits_t uxReturn; - /* Check the user is not attempting to clear the bits used by the kernel - itself. */ - configASSERT( xEventGroup ); - configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + /* Check the user is not attempting to clear the bits used by the kernel + * itself. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); - taskENTER_CRITICAL(); - { - traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); + taskENTER_CRITICAL(); + { + traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); - /* The value returned is the event group value prior to the bits being - cleared. */ - uxReturn = pxEventBits->uxEventBits; + /* The value returned is the event group value prior to the bits being + * cleared. */ + uxReturn = pxEventBits->uxEventBits; - /* Clear the bits. */ - pxEventBits->uxEventBits &= ~uxBitsToClear; - } - taskEXIT_CRITICAL(); + /* Clear the bits. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + } + taskEXIT_CRITICAL(); - return uxReturn; + return uxReturn; } /*-----------------------------------------------------------*/ #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) - BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) - { - BaseType_t xReturn; + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) + { + BaseType_t xReturn; - traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ + traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ - return xReturn; - } + return xReturn; + } -#endif +#endif /* if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) { -UBaseType_t uxSavedInterruptStatus; -EventGroup_t const * const pxEventBits = xEventGroup; -EventBits_t uxReturn; + UBaseType_t uxSavedInterruptStatus; + EventGroup_t const * const pxEventBits = xEventGroup; + EventBits_t uxReturn; - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - uxReturn = pxEventBits->uxEventBits; - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + uxReturn = pxEventBits->uxEventBits; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - return uxReturn; + return uxReturn; } /*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */ /*-----------------------------------------------------------*/ -EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet ) { -ListItem_t *pxListItem, *pxNext; -ListItem_t const *pxListEnd; -List_t const * pxList; -EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits; -EventGroup_t *pxEventBits = xEventGroup; -BaseType_t xMatchFound = pdFALSE; - - /* Check the user is not attempting to set the bits used by the kernel - itself. */ - configASSERT( xEventGroup ); - configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); - - pxList = &( pxEventBits->xTasksWaitingForBits ); - pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - vTaskSuspendAll(); - { - traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); - - pxListItem = listGET_HEAD_ENTRY( pxList ); - - /* Set the bits. */ - pxEventBits->uxEventBits |= uxBitsToSet; - - /* See if the new bit value should unblock any tasks. */ - while( pxListItem != pxListEnd ) - { - pxNext = listGET_NEXT( pxListItem ); - uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem ); - xMatchFound = pdFALSE; - - /* Split the bits waited for from the control bits. */ - uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES; - uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES; - - if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 ) - { - /* Just looking for single bit being set. */ - if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 ) - { - xMatchFound = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor ) - { - /* All bits are set. */ - xMatchFound = pdTRUE; - } - else - { - /* Need all bits to be set, but not all the bits were set. */ - } - - if( xMatchFound != pdFALSE ) - { - /* The bits match. Should the bits be cleared on exit? */ - if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ) - { - uxBitsToClear |= uxBitsWaitedFor; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Store the actual event flag value in the task's event list - item before removing the task from the event list. The - eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows - that is was unblocked due to its required bits matching, rather - than because it timed out. */ - vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); - } - - /* Move onto the next list item. Note pxListItem->pxNext is not - used here as the list item may have been removed from the event list - and inserted into the ready/pending reading list. */ - pxListItem = pxNext; - } - - /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT - bit was set in the control word. */ - pxEventBits->uxEventBits &= ~uxBitsToClear; - } - ( void ) xTaskResumeAll(); - - return pxEventBits->uxEventBits; + ListItem_t * pxListItem, * pxNext; + ListItem_t const * pxListEnd; + List_t const * pxList; + EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits; + EventGroup_t * pxEventBits = xEventGroup; + BaseType_t xMatchFound = pdFALSE; + + /* Check the user is not attempting to set the bits used by the kernel + * itself. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + + pxList = &( pxEventBits->xTasksWaitingForBits ); + pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + vTaskSuspendAll(); + { + traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); + + pxListItem = listGET_HEAD_ENTRY( pxList ); + + /* Set the bits. */ + pxEventBits->uxEventBits |= uxBitsToSet; + + /* See if the new bit value should unblock any tasks. */ + while( pxListItem != pxListEnd ) + { + pxNext = listGET_NEXT( pxListItem ); + uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem ); + xMatchFound = pdFALSE; + + /* Split the bits waited for from the control bits. */ + uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES; + uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES; + + if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 ) + { + /* Just looking for single bit being set. */ + if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 ) + { + xMatchFound = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor ) + { + /* All bits are set. */ + xMatchFound = pdTRUE; + } + else + { + /* Need all bits to be set, but not all the bits were set. */ + } + + if( xMatchFound != pdFALSE ) + { + /* The bits match. Should the bits be cleared on exit? */ + if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ) + { + uxBitsToClear |= uxBitsWaitedFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the actual event flag value in the task's event list + * item before removing the task from the event list. The + * eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows + * that is was unblocked due to its required bits matching, rather + * than because it timed out. */ + vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); + } + + /* Move onto the next list item. Note pxListItem->pxNext is not + * used here as the list item may have been removed from the event list + * and inserted into the ready/pending reading list. */ + pxListItem = pxNext; + } + + /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT + * bit was set in the control word. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + } + ( void ) xTaskResumeAll(); + + return pxEventBits->uxEventBits; } /*-----------------------------------------------------------*/ void vEventGroupDelete( EventGroupHandle_t xEventGroup ) { -EventGroup_t *pxEventBits = xEventGroup; -const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); - - vTaskSuspendAll(); - { - traceEVENT_GROUP_DELETE( xEventGroup ); - - while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) - { - /* Unblock the task, returning 0 as the event list is being deleted - and cannot therefore have any bits set. */ - configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); - vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); - } - - #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) - { - /* The event group can only have been allocated dynamically - free - it again. */ - vPortFree( pxEventBits ); - } - #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - { - /* The event group could have been allocated statically or - dynamically, so check before attempting to free the memory. */ - if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) - { - vPortFree( pxEventBits ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - } - ( void ) xTaskResumeAll(); + EventGroup_t * pxEventBits = xEventGroup; + const List_t * pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); + + vTaskSuspendAll(); + { + traceEVENT_GROUP_DELETE( xEventGroup ); + + while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) + { + /* Unblock the task, returning 0 as the event list is being deleted + * and cannot therefore have any bits set. */ + configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); + vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); + } + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The event group can only have been allocated dynamically - free + * it again. */ + vPortFree( pxEventBits ); + } + #elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The event group could have been allocated statically or + * dynamically, so check before attempting to free the memory. */ + if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxEventBits ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } + ( void ) xTaskResumeAll(); } /*-----------------------------------------------------------*/ /* For internal use only - execute a 'set bits' command that was pended from -an interrupt. */ -void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) + * an interrupt. */ +void vEventGroupSetBitsCallback( void * pvEventGroup, + const uint32_t ulBitsToSet ) { - ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ + ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ /* For internal use only - execute a 'clear bits' command that was pended from -an interrupt. */ -void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) + * an interrupt. */ +void vEventGroupClearBitsCallback( void * pvEventGroup, + const uint32_t ulBitsToClear ) { - ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ + ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */ } /*-----------------------------------------------------------*/ -static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xWaitForAllBits ) { -BaseType_t xWaitConditionMet = pdFALSE; - - if( xWaitForAllBits == pdFALSE ) - { - /* Task only has to wait for one bit within uxBitsToWaitFor to be - set. Is one already set? */ - if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 ) - { - xWaitConditionMet = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - /* Task has to wait for all the bits in uxBitsToWaitFor to be set. - Are they set already? */ - if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor ) - { - xWaitConditionMet = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - return xWaitConditionMet; + BaseType_t xWaitConditionMet = pdFALSE; + + if( xWaitForAllBits == pdFALSE ) + { + /* Task only has to wait for one bit within uxBitsToWaitFor to be + * set. Is one already set? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Task has to wait for all the bits in uxBitsToWaitFor to be set. + * Are they set already? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return xWaitConditionMet; } /*-----------------------------------------------------------*/ #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) - BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) - { - BaseType_t xReturn; + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + BaseType_t * pxHigherPriorityTaskWoken ) + { + BaseType_t xReturn; - traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); - xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ + traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */ - return xReturn; - } + return xReturn; + } -#endif +#endif /* if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ -#if (configUSE_TRACE_FACILITY == 1) +#if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) - { - UBaseType_t xReturn; - EventGroup_t const *pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ + UBaseType_t uxEventGroupGetNumber( void * xEventGroup ) + { + UBaseType_t xReturn; + EventGroup_t const * pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ - if( xEventGroup == NULL ) - { - xReturn = 0; - } - else - { - xReturn = pxEventBits->uxEventGroupNumber; - } + if( xEventGroup == NULL ) + { + xReturn = 0; + } + else + { + xReturn = pxEventBits->uxEventGroupNumber; + } - return xReturn; - } + return xReturn; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) - void vEventGroupSetNumber( void * xEventGroup, UBaseType_t uxEventGroupNumber ) - { - ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ - } + void vEventGroupSetNumber( void * xEventGroup, + UBaseType_t uxEventGroupNumber ) + { + ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */ + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ - - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h index 5a1a497800..96cf880a40 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #ifndef INC_FREERTOS_H @@ -46,11 +45,13 @@ * contains the typedefs required to build FreeRTOS. Read the instructions * in FreeRTOS/source/stdint.readme for more information. */ -#include /* READ COMMENT ABOVE. */ +#include /* READ COMMENT ABOVE. */ +/* *INDENT-OFF* */ #ifdef __cplusplus -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /* Application specific configuration options. */ #include "FreeRTOSConfig.h" @@ -63,13 +64,14 @@ extern "C" { /* Must be defaulted before configUSE_NEWLIB_REENTRANT is used below. */ #ifndef configUSE_NEWLIB_REENTRANT - #define configUSE_NEWLIB_REENTRANT 0 + #define configUSE_NEWLIB_REENTRANT 0 #endif /* Required if struct _reent is used. */ #if ( configUSE_NEWLIB_REENTRANT == 1 ) - #include + #include #endif + /* * Check all the required application specific macros have been defined. * These macros are application specific and (as downloaded) are defined @@ -77,495 +79,509 @@ extern "C" { */ #ifndef configMINIMAL_STACK_SIZE - #error Missing definition: configMINIMAL_STACK_SIZE must be defined in FreeRTOSConfig.h. configMINIMAL_STACK_SIZE defines the size (in words) of the stack allocated to the idle task. Refer to the demo project provided for your port for a suitable value. + #error Missing definition: configMINIMAL_STACK_SIZE must be defined in FreeRTOSConfig.h. configMINIMAL_STACK_SIZE defines the size (in words) of the stack allocated to the idle task. Refer to the demo project provided for your port for a suitable value. #endif #ifndef configMAX_PRIORITIES - #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. + #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. #endif #if configMAX_PRIORITIES < 1 - #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. #endif #ifndef configUSE_PREEMPTION - #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. + #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif #ifndef configUSE_IDLE_HOOK - #error Missing definition: configUSE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. + #error Missing definition: configUSE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif #ifndef configUSE_TICK_HOOK - #error Missing definition: configUSE_TICK_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. + #error Missing definition: configUSE_TICK_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif #ifndef configUSE_16_BIT_TICKS - #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. + #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. #endif #ifndef configUSE_CO_ROUTINES - #define configUSE_CO_ROUTINES 0 + #define configUSE_CO_ROUTINES 0 #endif #ifndef INCLUDE_vTaskPrioritySet - #define INCLUDE_vTaskPrioritySet 0 + #define INCLUDE_vTaskPrioritySet 0 #endif #ifndef INCLUDE_uxTaskPriorityGet - #define INCLUDE_uxTaskPriorityGet 0 + #define INCLUDE_uxTaskPriorityGet 0 #endif #ifndef INCLUDE_vTaskDelete - #define INCLUDE_vTaskDelete 0 + #define INCLUDE_vTaskDelete 0 #endif #ifndef INCLUDE_vTaskSuspend - #define INCLUDE_vTaskSuspend 0 + #define INCLUDE_vTaskSuspend 0 #endif #ifndef INCLUDE_vTaskDelayUntil - #define INCLUDE_vTaskDelayUntil 0 + #define INCLUDE_vTaskDelayUntil 0 #endif #ifndef INCLUDE_vTaskDelay - #define INCLUDE_vTaskDelay 0 + #define INCLUDE_vTaskDelay 0 #endif #ifndef INCLUDE_xTaskGetIdleTaskHandle - #define INCLUDE_xTaskGetIdleTaskHandle 0 + #define INCLUDE_xTaskGetIdleTaskHandle 0 #endif #ifndef INCLUDE_xTaskAbortDelay - #define INCLUDE_xTaskAbortDelay 0 + #define INCLUDE_xTaskAbortDelay 0 #endif #ifndef INCLUDE_xQueueGetMutexHolder - #define INCLUDE_xQueueGetMutexHolder 0 + #define INCLUDE_xQueueGetMutexHolder 0 #endif #ifndef INCLUDE_xSemaphoreGetMutexHolder - #define INCLUDE_xSemaphoreGetMutexHolder INCLUDE_xQueueGetMutexHolder + #define INCLUDE_xSemaphoreGetMutexHolder INCLUDE_xQueueGetMutexHolder #endif #ifndef INCLUDE_xTaskGetHandle - #define INCLUDE_xTaskGetHandle 0 + #define INCLUDE_xTaskGetHandle 0 #endif #ifndef INCLUDE_uxTaskGetStackHighWaterMark - #define INCLUDE_uxTaskGetStackHighWaterMark 0 + #define INCLUDE_uxTaskGetStackHighWaterMark 0 #endif #ifndef INCLUDE_uxTaskGetStackHighWaterMark2 - #define INCLUDE_uxTaskGetStackHighWaterMark2 0 + #define INCLUDE_uxTaskGetStackHighWaterMark2 0 #endif #ifndef INCLUDE_eTaskGetState - #define INCLUDE_eTaskGetState 0 + #define INCLUDE_eTaskGetState 0 #endif #ifndef INCLUDE_xTaskResumeFromISR - #define INCLUDE_xTaskResumeFromISR 1 + #define INCLUDE_xTaskResumeFromISR 1 #endif #ifndef INCLUDE_xTimerPendFunctionCall - #define INCLUDE_xTimerPendFunctionCall 0 + #define INCLUDE_xTimerPendFunctionCall 0 #endif #ifndef INCLUDE_xTaskGetSchedulerState - #define INCLUDE_xTaskGetSchedulerState 0 + #define INCLUDE_xTaskGetSchedulerState 0 #endif #ifndef INCLUDE_xTaskGetCurrentTaskHandle - #define INCLUDE_xTaskGetCurrentTaskHandle 0 + #define INCLUDE_xTaskGetCurrentTaskHandle 0 #endif #if configUSE_CO_ROUTINES != 0 - #ifndef configMAX_CO_ROUTINE_PRIORITIES - #error configMAX_CO_ROUTINE_PRIORITIES must be greater than or equal to 1. - #endif + #ifndef configMAX_CO_ROUTINE_PRIORITIES + #error configMAX_CO_ROUTINE_PRIORITIES must be greater than or equal to 1. + #endif #endif #ifndef configUSE_DAEMON_TASK_STARTUP_HOOK - #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 #endif #ifndef configUSE_APPLICATION_TASK_TAG - #define configUSE_APPLICATION_TASK_TAG 0 + #define configUSE_APPLICATION_TASK_TAG 0 #endif #ifndef configNUM_THREAD_LOCAL_STORAGE_POINTERS - #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 + #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 #endif #ifndef configUSE_RECURSIVE_MUTEXES - #define configUSE_RECURSIVE_MUTEXES 0 + #define configUSE_RECURSIVE_MUTEXES 0 #endif #ifndef configUSE_MUTEXES - #define configUSE_MUTEXES 0 + #define configUSE_MUTEXES 0 #endif #ifndef configUSE_TIMERS - #define configUSE_TIMERS 0 + #define configUSE_TIMERS 0 #endif #ifndef configUSE_COUNTING_SEMAPHORES - #define configUSE_COUNTING_SEMAPHORES 0 + #define configUSE_COUNTING_SEMAPHORES 0 #endif #ifndef configUSE_ALTERNATIVE_API - #define configUSE_ALTERNATIVE_API 0 + #define configUSE_ALTERNATIVE_API 0 #endif #ifndef portCRITICAL_NESTING_IN_TCB - #define portCRITICAL_NESTING_IN_TCB 0 + #define portCRITICAL_NESTING_IN_TCB 0 #endif #ifndef configMAX_TASK_NAME_LEN - #define configMAX_TASK_NAME_LEN 16 + #define configMAX_TASK_NAME_LEN 16 #endif #ifndef configIDLE_SHOULD_YIELD - #define configIDLE_SHOULD_YIELD 1 + #define configIDLE_SHOULD_YIELD 1 #endif #if configMAX_TASK_NAME_LEN < 1 - #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h + #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h #endif #ifndef configASSERT - #define configASSERT( x ) - #define configASSERT_DEFINED 0 + #define configASSERT( x ) + #define configASSERT_DEFINED 0 #else - #define configASSERT_DEFINED 1 + #define configASSERT_DEFINED 1 #endif /* configPRECONDITION should be defined as configASSERT. -The CBMC proofs need a way to track assumptions and assertions. -A configPRECONDITION statement should express an implicit invariant or -assumption made. A configASSERT statement should express an invariant that must -hold explicit before calling the code. */ + * The CBMC proofs need a way to track assumptions and assertions. + * A configPRECONDITION statement should express an implicit invariant or + * assumption made. A configASSERT statement should express an invariant that must + * hold explicit before calling the code. */ #ifndef configPRECONDITION - #define configPRECONDITION( X ) configASSERT(X) - #define configPRECONDITION_DEFINED 0 + #define configPRECONDITION( X ) configASSERT( X ) + #define configPRECONDITION_DEFINED 0 #else - #define configPRECONDITION_DEFINED 1 + #define configPRECONDITION_DEFINED 1 #endif #ifndef portMEMORY_BARRIER - #define portMEMORY_BARRIER() + #define portMEMORY_BARRIER() #endif #ifndef portSOFTWARE_BARRIER - #define portSOFTWARE_BARRIER() + #define portSOFTWARE_BARRIER() #endif /* The timers module relies on xTaskGetSchedulerState(). */ #if configUSE_TIMERS == 1 - #ifndef configTIMER_TASK_PRIORITY - #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. - #endif /* configTIMER_TASK_PRIORITY */ + #ifndef configTIMER_TASK_PRIORITY + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. + #endif /* configTIMER_TASK_PRIORITY */ - #ifndef configTIMER_QUEUE_LENGTH - #error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. - #endif /* configTIMER_QUEUE_LENGTH */ + #ifndef configTIMER_QUEUE_LENGTH + #error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. + #endif /* configTIMER_QUEUE_LENGTH */ - #ifndef configTIMER_TASK_STACK_DEPTH - #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. - #endif /* configTIMER_TASK_STACK_DEPTH */ + #ifndef configTIMER_TASK_STACK_DEPTH + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. + #endif /* configTIMER_TASK_STACK_DEPTH */ #endif /* configUSE_TIMERS */ #ifndef portSET_INTERRUPT_MASK_FROM_ISR - #define portSET_INTERRUPT_MASK_FROM_ISR() 0 + #define portSET_INTERRUPT_MASK_FROM_ISR() 0 #endif #ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR - #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue #endif #ifndef portCLEAN_UP_TCB - #define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB + #define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB #endif #ifndef portPRE_TASK_DELETE_HOOK - #define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxYieldPending ) + #define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxYieldPending ) #endif #ifndef portSETUP_TCB - #define portSETUP_TCB( pxTCB ) ( void ) pxTCB + #define portSETUP_TCB( pxTCB ) ( void ) pxTCB #endif #ifndef configQUEUE_REGISTRY_SIZE - #define configQUEUE_REGISTRY_SIZE 0U + #define configQUEUE_REGISTRY_SIZE 0U #endif #if ( configQUEUE_REGISTRY_SIZE < 1 ) - #define vQueueAddToRegistry( xQueue, pcName ) - #define vQueueUnregisterQueue( xQueue ) - #define pcQueueGetName( xQueue ) + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) + #define pcQueueGetName( xQueue ) #endif #ifndef portPOINTER_SIZE_TYPE - #define portPOINTER_SIZE_TYPE uint32_t + #define portPOINTER_SIZE_TYPE uint32_t #endif /* Remove any unused trace macros. */ #ifndef traceSTART - /* Used to perform any necessary initialisation - for example, open a file - into which trace is to be written. */ - #define traceSTART() + +/* Used to perform any necessary initialisation - for example, open a file + * into which trace is to be written. */ + #define traceSTART() #endif #ifndef traceEND - /* Use to close a trace, for example close a file into which trace has been - written. */ - #define traceEND() + +/* Use to close a trace, for example close a file into which trace has been + * written. */ + #define traceEND() #endif #ifndef traceTASK_SWITCHED_IN - /* Called after a task has been selected to run. pxCurrentTCB holds a pointer - to the task control block of the selected task. */ - #define traceTASK_SWITCHED_IN() + +/* Called after a task has been selected to run. pxCurrentTCB holds a pointer + * to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() #endif #ifndef traceINCREASE_TICK_COUNT - /* Called before stepping the tick count after waking from tickless idle - sleep. */ - #define traceINCREASE_TICK_COUNT( x ) + +/* Called before stepping the tick count after waking from tickless idle + * sleep. */ + #define traceINCREASE_TICK_COUNT( x ) #endif #ifndef traceLOW_POWER_IDLE_BEGIN - /* Called immediately before entering tickless idle. */ - #define traceLOW_POWER_IDLE_BEGIN() + /* Called immediately before entering tickless idle. */ + #define traceLOW_POWER_IDLE_BEGIN() #endif -#ifndef traceLOW_POWER_IDLE_END - /* Called when returning to the Idle task after a tickless idle. */ - #define traceLOW_POWER_IDLE_END() +#ifndef traceLOW_POWER_IDLE_END + /* Called when returning to the Idle task after a tickless idle. */ + #define traceLOW_POWER_IDLE_END() #endif #ifndef traceTASK_SWITCHED_OUT - /* Called before a task has been selected to run. pxCurrentTCB holds a pointer - to the task control block of the task being switched out. */ - #define traceTASK_SWITCHED_OUT() + +/* Called before a task has been selected to run. pxCurrentTCB holds a pointer + * to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() #endif #ifndef traceTASK_PRIORITY_INHERIT - /* Called when a task attempts to take a mutex that is already held by a - lower priority task. pxTCBOfMutexHolder is a pointer to the TCB of the task - that holds the mutex. uxInheritedPriority is the priority the mutex holder - will inherit (the priority of the task that is attempting to obtain the - muted. */ - #define traceTASK_PRIORITY_INHERIT( pxTCBOfMutexHolder, uxInheritedPriority ) + +/* Called when a task attempts to take a mutex that is already held by a + * lower priority task. pxTCBOfMutexHolder is a pointer to the TCB of the task + * that holds the mutex. uxInheritedPriority is the priority the mutex holder + * will inherit (the priority of the task that is attempting to obtain the + * muted. */ + #define traceTASK_PRIORITY_INHERIT( pxTCBOfMutexHolder, uxInheritedPriority ) #endif #ifndef traceTASK_PRIORITY_DISINHERIT - /* Called when a task releases a mutex, the holding of which had resulted in - the task inheriting the priority of a higher priority task. - pxTCBOfMutexHolder is a pointer to the TCB of the task that is releasing the - mutex. uxOriginalPriority is the task's configured (base) priority. */ - #define traceTASK_PRIORITY_DISINHERIT( pxTCBOfMutexHolder, uxOriginalPriority ) + +/* Called when a task releases a mutex, the holding of which had resulted in + * the task inheriting the priority of a higher priority task. + * pxTCBOfMutexHolder is a pointer to the TCB of the task that is releasing the + * mutex. uxOriginalPriority is the task's configured (base) priority. */ + #define traceTASK_PRIORITY_DISINHERIT( pxTCBOfMutexHolder, uxOriginalPriority ) #endif #ifndef traceBLOCKING_ON_QUEUE_RECEIVE - /* Task is about to block because it cannot read from a - queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore - upon which the read was attempted. pxCurrentTCB points to the TCB of the - task that attempted the read. */ - #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) + +/* Task is about to block because it cannot read from a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the read was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) #endif #ifndef traceBLOCKING_ON_QUEUE_PEEK - /* Task is about to block because it cannot read from a - queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore - upon which the read was attempted. pxCurrentTCB points to the TCB of the - task that attempted the read. */ - #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) + +/* Task is about to block because it cannot read from a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the read was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) #endif #ifndef traceBLOCKING_ON_QUEUE_SEND - /* Task is about to block because it cannot write to a - queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore - upon which the write was attempted. pxCurrentTCB points to the TCB of the - task that attempted the write. */ - #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) + +/* Task is about to block because it cannot write to a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the write was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) #endif #ifndef configCHECK_FOR_STACK_OVERFLOW - #define configCHECK_FOR_STACK_OVERFLOW 0 + #define configCHECK_FOR_STACK_OVERFLOW 0 #endif #ifndef configRECORD_STACK_HIGH_ADDRESS - #define configRECORD_STACK_HIGH_ADDRESS 0 + #define configRECORD_STACK_HIGH_ADDRESS 0 #endif #ifndef configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H - #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 + #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 #endif /* The following event macros are embedded in the kernel API calls. */ #ifndef traceMOVED_TASK_TO_READY_STATE - #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) + #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) #endif #ifndef tracePOST_MOVED_TASK_TO_READY_STATE - #define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) + #define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) #endif #ifndef traceQUEUE_CREATE - #define traceQUEUE_CREATE( pxNewQueue ) + #define traceQUEUE_CREATE( pxNewQueue ) #endif #ifndef traceQUEUE_CREATE_FAILED - #define traceQUEUE_CREATE_FAILED( ucQueueType ) + #define traceQUEUE_CREATE_FAILED( ucQueueType ) #endif #ifndef traceCREATE_MUTEX - #define traceCREATE_MUTEX( pxNewQueue ) + #define traceCREATE_MUTEX( pxNewQueue ) #endif #ifndef traceCREATE_MUTEX_FAILED - #define traceCREATE_MUTEX_FAILED() + #define traceCREATE_MUTEX_FAILED() #endif #ifndef traceGIVE_MUTEX_RECURSIVE - #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) #endif #ifndef traceGIVE_MUTEX_RECURSIVE_FAILED - #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) #endif #ifndef traceTAKE_MUTEX_RECURSIVE - #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) #endif #ifndef traceTAKE_MUTEX_RECURSIVE_FAILED - #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) + #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) #endif #ifndef traceCREATE_COUNTING_SEMAPHORE - #define traceCREATE_COUNTING_SEMAPHORE() + #define traceCREATE_COUNTING_SEMAPHORE() #endif #ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED - #define traceCREATE_COUNTING_SEMAPHORE_FAILED() + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SET_SEND + #define traceQUEUE_SET_SEND traceQUEUE_SEND #endif #ifndef traceQUEUE_SEND - #define traceQUEUE_SEND( pxQueue ) + #define traceQUEUE_SEND( pxQueue ) #endif #ifndef traceQUEUE_SEND_FAILED - #define traceQUEUE_SEND_FAILED( pxQueue ) + #define traceQUEUE_SEND_FAILED( pxQueue ) #endif #ifndef traceQUEUE_RECEIVE - #define traceQUEUE_RECEIVE( pxQueue ) + #define traceQUEUE_RECEIVE( pxQueue ) #endif #ifndef traceQUEUE_PEEK - #define traceQUEUE_PEEK( pxQueue ) + #define traceQUEUE_PEEK( pxQueue ) #endif #ifndef traceQUEUE_PEEK_FAILED - #define traceQUEUE_PEEK_FAILED( pxQueue ) + #define traceQUEUE_PEEK_FAILED( pxQueue ) #endif #ifndef traceQUEUE_PEEK_FROM_ISR - #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) + #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) #endif #ifndef traceQUEUE_RECEIVE_FAILED - #define traceQUEUE_RECEIVE_FAILED( pxQueue ) + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) #endif #ifndef traceQUEUE_SEND_FROM_ISR - #define traceQUEUE_SEND_FROM_ISR( pxQueue ) + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) #endif #ifndef traceQUEUE_SEND_FROM_ISR_FAILED - #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) #endif #ifndef traceQUEUE_RECEIVE_FROM_ISR - #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) #endif #ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED - #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) #endif #ifndef traceQUEUE_PEEK_FROM_ISR_FAILED - #define traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ) + #define traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ) #endif #ifndef traceQUEUE_DELETE - #define traceQUEUE_DELETE( pxQueue ) + #define traceQUEUE_DELETE( pxQueue ) #endif #ifndef traceTASK_CREATE - #define traceTASK_CREATE( pxNewTCB ) + #define traceTASK_CREATE( pxNewTCB ) #endif #ifndef traceTASK_CREATE_FAILED - #define traceTASK_CREATE_FAILED() + #define traceTASK_CREATE_FAILED() #endif #ifndef traceTASK_DELETE - #define traceTASK_DELETE( pxTaskToDelete ) + #define traceTASK_DELETE( pxTaskToDelete ) #endif #ifndef traceTASK_DELAY_UNTIL - #define traceTASK_DELAY_UNTIL( x ) + #define traceTASK_DELAY_UNTIL( x ) #endif #ifndef traceTASK_DELAY - #define traceTASK_DELAY() + #define traceTASK_DELAY() #endif #ifndef traceTASK_PRIORITY_SET - #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) #endif #ifndef traceTASK_SUSPEND - #define traceTASK_SUSPEND( pxTaskToSuspend ) + #define traceTASK_SUSPEND( pxTaskToSuspend ) #endif #ifndef traceTASK_RESUME - #define traceTASK_RESUME( pxTaskToResume ) + #define traceTASK_RESUME( pxTaskToResume ) #endif #ifndef traceTASK_RESUME_FROM_ISR - #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) #endif #ifndef traceTASK_INCREMENT_TICK - #define traceTASK_INCREMENT_TICK( xTickCount ) + #define traceTASK_INCREMENT_TICK( xTickCount ) #endif #ifndef traceTIMER_CREATE - #define traceTIMER_CREATE( pxNewTimer ) + #define traceTIMER_CREATE( pxNewTimer ) #endif #ifndef traceTIMER_CREATE_FAILED - #define traceTIMER_CREATE_FAILED() + #define traceTIMER_CREATE_FAILED() #endif #ifndef traceTIMER_COMMAND_SEND - #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) + #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) #endif #ifndef traceTIMER_EXPIRED - #define traceTIMER_EXPIRED( pxTimer ) + #define traceTIMER_EXPIRED( pxTimer ) #endif #ifndef traceTIMER_COMMAND_RECEIVED - #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) + #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) #endif #ifndef traceMALLOC @@ -577,430 +593,445 @@ hold explicit before calling the code. */ #endif #ifndef traceEVENT_GROUP_CREATE - #define traceEVENT_GROUP_CREATE( xEventGroup ) + #define traceEVENT_GROUP_CREATE( xEventGroup ) #endif #ifndef traceEVENT_GROUP_CREATE_FAILED - #define traceEVENT_GROUP_CREATE_FAILED() + #define traceEVENT_GROUP_CREATE_FAILED() #endif #ifndef traceEVENT_GROUP_SYNC_BLOCK - #define traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ) + #define traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ) #endif #ifndef traceEVENT_GROUP_SYNC_END - #define traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred + #define traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred #endif #ifndef traceEVENT_GROUP_WAIT_BITS_BLOCK - #define traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ) + #define traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ) #endif #ifndef traceEVENT_GROUP_WAIT_BITS_END - #define traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred + #define traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred #endif #ifndef traceEVENT_GROUP_CLEAR_BITS - #define traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ) + #define traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ) #endif #ifndef traceEVENT_GROUP_CLEAR_BITS_FROM_ISR - #define traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ) + #define traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ) #endif #ifndef traceEVENT_GROUP_SET_BITS - #define traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ) + #define traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ) #endif #ifndef traceEVENT_GROUP_SET_BITS_FROM_ISR - #define traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ) + #define traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ) #endif #ifndef traceEVENT_GROUP_DELETE - #define traceEVENT_GROUP_DELETE( xEventGroup ) + #define traceEVENT_GROUP_DELETE( xEventGroup ) #endif #ifndef tracePEND_FUNC_CALL - #define tracePEND_FUNC_CALL(xFunctionToPend, pvParameter1, ulParameter2, ret) + #define tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, ret ) #endif #ifndef tracePEND_FUNC_CALL_FROM_ISR - #define tracePEND_FUNC_CALL_FROM_ISR(xFunctionToPend, pvParameter1, ulParameter2, ret) + #define tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, ret ) #endif #ifndef traceQUEUE_REGISTRY_ADD - #define traceQUEUE_REGISTRY_ADD(xQueue, pcQueueName) + #define traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ) #endif #ifndef traceTASK_NOTIFY_TAKE_BLOCK - #define traceTASK_NOTIFY_TAKE_BLOCK() + #define traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWait ) #endif #ifndef traceTASK_NOTIFY_TAKE - #define traceTASK_NOTIFY_TAKE() + #define traceTASK_NOTIFY_TAKE( uxIndexToWait ) #endif #ifndef traceTASK_NOTIFY_WAIT_BLOCK - #define traceTASK_NOTIFY_WAIT_BLOCK() + #define traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait ) #endif #ifndef traceTASK_NOTIFY_WAIT - #define traceTASK_NOTIFY_WAIT() + #define traceTASK_NOTIFY_WAIT( uxIndexToWait ) #endif #ifndef traceTASK_NOTIFY - #define traceTASK_NOTIFY() + #define traceTASK_NOTIFY( uxIndexToNotify ) #endif #ifndef traceTASK_NOTIFY_FROM_ISR - #define traceTASK_NOTIFY_FROM_ISR() + #define traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ) #endif #ifndef traceTASK_NOTIFY_GIVE_FROM_ISR - #define traceTASK_NOTIFY_GIVE_FROM_ISR() + #define traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ) #endif #ifndef traceSTREAM_BUFFER_CREATE_FAILED - #define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ) + #define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ) #endif #ifndef traceSTREAM_BUFFER_CREATE_STATIC_FAILED - #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ) + #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ) #endif #ifndef traceSTREAM_BUFFER_CREATE - #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ) + #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ) #endif #ifndef traceSTREAM_BUFFER_DELETE - #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) + #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) #endif #ifndef traceSTREAM_BUFFER_RESET - #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) + #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) #endif #ifndef traceBLOCKING_ON_STREAM_BUFFER_SEND - #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) + #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) #endif #ifndef traceSTREAM_BUFFER_SEND - #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) + #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) #endif #ifndef traceSTREAM_BUFFER_SEND_FAILED - #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) + #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) #endif #ifndef traceSTREAM_BUFFER_SEND_FROM_ISR - #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) + #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) #endif #ifndef traceBLOCKING_ON_STREAM_BUFFER_RECEIVE - #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) + #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) #endif #ifndef traceSTREAM_BUFFER_RECEIVE - #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) + #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) #endif #ifndef traceSTREAM_BUFFER_RECEIVE_FAILED - #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) + #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) #endif #ifndef traceSTREAM_BUFFER_RECEIVE_FROM_ISR - #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) + #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) #endif #ifndef configGENERATE_RUN_TIME_STATS - #define configGENERATE_RUN_TIME_STATS 0 + #define configGENERATE_RUN_TIME_STATS 0 #endif #if ( configGENERATE_RUN_TIME_STATS == 1 ) - #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS - #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. - #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ - #ifndef portGET_RUN_TIME_COUNTER_VALUE - #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE - #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. - #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ - #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. + #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ #endif /* configGENERATE_RUN_TIME_STATS */ #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS - #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() #endif #ifndef configUSE_MALLOC_FAILED_HOOK - #define configUSE_MALLOC_FAILED_HOOK 0 + #define configUSE_MALLOC_FAILED_HOOK 0 #endif #ifndef portPRIVILEGE_BIT - #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) + #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) #endif #ifndef portYIELD_WITHIN_API - #define portYIELD_WITHIN_API portYIELD + #define portYIELD_WITHIN_API portYIELD #endif #ifndef portSUPPRESS_TICKS_AND_SLEEP - #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) #endif #ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP - #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 + #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 #endif #if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2 - #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2 + #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2 #endif #ifndef configUSE_TICKLESS_IDLE - #define configUSE_TICKLESS_IDLE 0 + #define configUSE_TICKLESS_IDLE 0 #endif #ifndef configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING - #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) + #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) #endif #ifndef configPRE_SLEEP_PROCESSING - #define configPRE_SLEEP_PROCESSING( x ) + #define configPRE_SLEEP_PROCESSING( x ) #endif #ifndef configPOST_SLEEP_PROCESSING - #define configPOST_SLEEP_PROCESSING( x ) + #define configPOST_SLEEP_PROCESSING( x ) #endif #ifndef configUSE_QUEUE_SETS - #define configUSE_QUEUE_SETS 0 + #define configUSE_QUEUE_SETS 0 #endif #ifndef portTASK_USES_FLOATING_POINT - #define portTASK_USES_FLOATING_POINT() + #define portTASK_USES_FLOATING_POINT() #endif #ifndef portALLOCATE_SECURE_CONTEXT - #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) + #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) #endif #ifndef portDONT_DISCARD - #define portDONT_DISCARD + #define portDONT_DISCARD #endif #ifndef configUSE_TIME_SLICING - #define configUSE_TIME_SLICING 1 + #define configUSE_TIME_SLICING 1 #endif #ifndef configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS - #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 + #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 #endif #ifndef configUSE_STATS_FORMATTING_FUNCTIONS - #define configUSE_STATS_FORMATTING_FUNCTIONS 0 + #define configUSE_STATS_FORMATTING_FUNCTIONS 0 #endif #ifndef portASSERT_IF_INTERRUPT_PRIORITY_INVALID - #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() #endif #ifndef configUSE_TRACE_FACILITY - #define configUSE_TRACE_FACILITY 0 + #define configUSE_TRACE_FACILITY 0 #endif #ifndef mtCOVERAGE_TEST_MARKER - #define mtCOVERAGE_TEST_MARKER() + #define mtCOVERAGE_TEST_MARKER() #endif #ifndef mtCOVERAGE_TEST_DELAY - #define mtCOVERAGE_TEST_DELAY() + #define mtCOVERAGE_TEST_DELAY() #endif #ifndef portASSERT_IF_IN_ISR - #define portASSERT_IF_IN_ISR() + #define portASSERT_IF_IN_ISR() #endif #ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION - #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #endif #ifndef configAPPLICATION_ALLOCATED_HEAP - #define configAPPLICATION_ALLOCATED_HEAP 0 + #define configAPPLICATION_ALLOCATED_HEAP 0 #endif #ifndef configUSE_TASK_NOTIFICATIONS - #define configUSE_TASK_NOTIFICATIONS 1 + #define configUSE_TASK_NOTIFICATIONS 1 +#endif + +#ifndef configTASK_NOTIFICATION_ARRAY_ENTRIES + #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 +#endif + +#if configTASK_NOTIFICATION_ARRAY_ENTRIES < 1 + #error configTASK_NOTIFICATION_ARRAY_ENTRIES must be at least 1 #endif #ifndef configUSE_POSIX_ERRNO - #define configUSE_POSIX_ERRNO 0 + #define configUSE_POSIX_ERRNO 0 #endif #ifndef portTICK_TYPE_IS_ATOMIC - #define portTICK_TYPE_IS_ATOMIC 0 + #define portTICK_TYPE_IS_ATOMIC 0 #endif #ifndef configSUPPORT_STATIC_ALLOCATION - /* Defaults to 0 for backward compatibility. */ - #define configSUPPORT_STATIC_ALLOCATION 0 + /* Defaults to 0 for backward compatibility. */ + #define configSUPPORT_STATIC_ALLOCATION 0 #endif #ifndef configSUPPORT_DYNAMIC_ALLOCATION - /* Defaults to 1 for backward compatibility. */ - #define configSUPPORT_DYNAMIC_ALLOCATION 1 + /* Defaults to 1 for backward compatibility. */ + #define configSUPPORT_DYNAMIC_ALLOCATION 1 #endif #ifndef configSTACK_DEPTH_TYPE - /* Defaults to uint16_t for backward compatibility, but can be overridden - in FreeRTOSConfig.h if uint16_t is too restrictive. */ - #define configSTACK_DEPTH_TYPE uint16_t + +/* Defaults to uint16_t for backward compatibility, but can be overridden + * in FreeRTOSConfig.h if uint16_t is too restrictive. */ + #define configSTACK_DEPTH_TYPE uint16_t #endif #ifndef configMESSAGE_BUFFER_LENGTH_TYPE - /* Defaults to size_t for backward compatibility, but can be overridden - in FreeRTOSConfig.h if lengths will always be less than the number of bytes - in a size_t. */ - #define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Defaults to size_t for backward compatibility, but can be overridden + * in FreeRTOSConfig.h if lengths will always be less than the number of bytes + * in a size_t. */ + #define configMESSAGE_BUFFER_LENGTH_TYPE size_t #endif /* Sanity check the configuration. */ -#if( configUSE_TICKLESS_IDLE != 0 ) - #if( INCLUDE_vTaskSuspend != 1 ) - #error INCLUDE_vTaskSuspend must be set to 1 if configUSE_TICKLESS_IDLE is not set to 0 - #endif /* INCLUDE_vTaskSuspend */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + #if ( INCLUDE_vTaskSuspend != 1 ) + #error INCLUDE_vTaskSuspend must be set to 1 if configUSE_TICKLESS_IDLE is not set to 0 + #endif /* INCLUDE_vTaskSuspend */ #endif /* configUSE_TICKLESS_IDLE */ -#if( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) - #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. +#if ( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) + #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. #endif -#if( ( configUSE_RECURSIVE_MUTEXES == 1 ) && ( configUSE_MUTEXES != 1 ) ) - #error configUSE_MUTEXES must be set to 1 to use recursive mutexes +#if ( ( configUSE_RECURSIVE_MUTEXES == 1 ) && ( configUSE_MUTEXES != 1 ) ) + #error configUSE_MUTEXES must be set to 1 to use recursive mutexes #endif #ifndef configINITIAL_TICK_COUNT - #define configINITIAL_TICK_COUNT 0 + #define configINITIAL_TICK_COUNT 0 #endif -#if( portTICK_TYPE_IS_ATOMIC == 0 ) - /* Either variables of tick type cannot be read atomically, or - portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when - the tick count is returned to the standard critical section macros. */ - #define portTICK_TYPE_ENTER_CRITICAL() portENTER_CRITICAL() - #define portTICK_TYPE_EXIT_CRITICAL() portEXIT_CRITICAL() - #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() - #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) ) +#if ( portTICK_TYPE_IS_ATOMIC == 0 ) + +/* Either variables of tick type cannot be read atomically, or + * portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when + * the tick count is returned to the standard critical section macros. */ + #define portTICK_TYPE_ENTER_CRITICAL() portENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() portEXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) ) #else - /* The tick type can be read atomically, so critical sections used when the - tick count is returned can be defined away. */ - #define portTICK_TYPE_ENTER_CRITICAL() - #define portTICK_TYPE_EXIT_CRITICAL() - #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() 0 - #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) ( void ) x -#endif + +/* The tick type can be read atomically, so critical sections used when the + * tick count is returned can be defined away. */ + #define portTICK_TYPE_ENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() 0 + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) ( void ) x +#endif /* if ( portTICK_TYPE_IS_ATOMIC == 0 ) */ /* Definitions to allow backward compatibility with FreeRTOS versions prior to -V8 if desired. */ + * V8 if desired. */ #ifndef configENABLE_BACKWARD_COMPATIBILITY - #define configENABLE_BACKWARD_COMPATIBILITY 1 + #define configENABLE_BACKWARD_COMPATIBILITY 1 #endif #ifndef configPRINTF - /* configPRINTF() was not defined, so define it away to nothing. To use - configPRINTF() then define it as follows (where MyPrintFunction() is - provided by the application writer): - - void MyPrintFunction(const char *pcFormat, ... ); - #define configPRINTF( X ) MyPrintFunction X - Then call like a standard printf() function, but placing brackets around - all parameters so they are passed as a single parameter. For example: - configPRINTF( ("Value = %d", MyVariable) ); */ - #define configPRINTF( X ) +/* configPRINTF() was not defined, so define it away to nothing. To use + * configPRINTF() then define it as follows (where MyPrintFunction() is + * provided by the application writer): + * + * void MyPrintFunction(const char *pcFormat, ... ); + #define configPRINTF( X ) MyPrintFunction X + * + * Then call like a standard printf() function, but placing brackets around + * all parameters so they are passed as a single parameter. For example: + * configPRINTF( ("Value = %d", MyVariable) ); */ + #define configPRINTF( X ) #endif #ifndef configMAX - /* The application writer has not provided their own MAX macro, so define - the following generic implementation. */ - #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) + +/* The application writer has not provided their own MAX macro, so define + * the following generic implementation. */ + #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) #endif #ifndef configMIN - /* The application writer has not provided their own MAX macro, so define - the following generic implementation. */ - #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) + +/* The application writer has not provided their own MAX macro, so define + * the following generic implementation. */ + #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) #endif #if configENABLE_BACKWARD_COMPATIBILITY == 1 - #define eTaskStateGet eTaskGetState - #define portTickType TickType_t - #define xTaskHandle TaskHandle_t - #define xQueueHandle QueueHandle_t - #define xSemaphoreHandle SemaphoreHandle_t - #define xQueueSetHandle QueueSetHandle_t - #define xQueueSetMemberHandle QueueSetMemberHandle_t - #define xTimeOutType TimeOut_t - #define xMemoryRegion MemoryRegion_t - #define xTaskParameters TaskParameters_t - #define xTaskStatusType TaskStatus_t - #define xTimerHandle TimerHandle_t - #define xCoRoutineHandle CoRoutineHandle_t - #define pdTASK_HOOK_CODE TaskHookFunction_t - #define portTICK_RATE_MS portTICK_PERIOD_MS - #define pcTaskGetTaskName pcTaskGetName - #define pcTimerGetTimerName pcTimerGetName - #define pcQueueGetQueueName pcQueueGetName - #define vTaskGetTaskInfo vTaskGetInfo - #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter - - /* Backward compatibility within the scheduler code only - these definitions - are not really required but are included for completeness. */ - #define tmrTIMER_CALLBACK TimerCallbackFunction_t - #define pdTASK_CODE TaskFunction_t - #define xListItem ListItem_t - #define xList List_t - - /* For libraries that break the list data hiding, and access list structure - members directly (which is not supposed to be done). */ - #define pxContainer pvContainer + #define eTaskStateGet eTaskGetState + #define portTickType TickType_t + #define xTaskHandle TaskHandle_t + #define xQueueHandle QueueHandle_t + #define xSemaphoreHandle SemaphoreHandle_t + #define xQueueSetHandle QueueSetHandle_t + #define xQueueSetMemberHandle QueueSetMemberHandle_t + #define xTimeOutType TimeOut_t + #define xMemoryRegion MemoryRegion_t + #define xTaskParameters TaskParameters_t + #define xTaskStatusType TaskStatus_t + #define xTimerHandle TimerHandle_t + #define xCoRoutineHandle CoRoutineHandle_t + #define pdTASK_HOOK_CODE TaskHookFunction_t + #define portTICK_RATE_MS portTICK_PERIOD_MS + #define pcTaskGetTaskName pcTaskGetName + #define pcTimerGetTimerName pcTimerGetName + #define pcQueueGetQueueName pcQueueGetName + #define vTaskGetTaskInfo vTaskGetInfo + #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + +/* Backward compatibility within the scheduler code only - these definitions + * are not really required but are included for completeness. */ + #define tmrTIMER_CALLBACK TimerCallbackFunction_t + #define pdTASK_CODE TaskFunction_t + #define xListItem ListItem_t + #define xList List_t + +/* For libraries that break the list data hiding, and access list structure + * members directly (which is not supposed to be done). */ + #define pxContainer pvContainer #endif /* configENABLE_BACKWARD_COMPATIBILITY */ -#if( configUSE_ALTERNATIVE_API != 0 ) - #error The alternative API was deprecated some time ago, and was removed in FreeRTOS V9.0 0 +#if ( configUSE_ALTERNATIVE_API != 0 ) + #error The alternative API was deprecated some time ago, and was removed in FreeRTOS V9.0 0 #endif /* Set configUSE_TASK_FPU_SUPPORT to 0 to omit floating point support even -if floating point hardware is otherwise supported by the FreeRTOS port in use. -This constant is not supported by all FreeRTOS ports that include floating -point support. */ + * if floating point hardware is otherwise supported by the FreeRTOS port in use. + * This constant is not supported by all FreeRTOS ports that include floating + * point support. */ #ifndef configUSE_TASK_FPU_SUPPORT - #define configUSE_TASK_FPU_SUPPORT 1 + #define configUSE_TASK_FPU_SUPPORT 1 #endif /* Set configENABLE_MPU to 1 to enable MPU support and 0 to disable it. This is -currently used in ARMv8M ports. */ + * currently used in ARMv8M ports. */ #ifndef configENABLE_MPU - #define configENABLE_MPU 0 + #define configENABLE_MPU 0 #endif /* Set configENABLE_FPU to 1 to enable FPU support and 0 to disable it. This is -currently used in ARMv8M ports. */ + * currently used in ARMv8M ports. */ #ifndef configENABLE_FPU - #define configENABLE_FPU 1 + #define configENABLE_FPU 1 #endif /* Set configENABLE_TRUSTZONE to 1 enable TrustZone support and 0 to disable it. -This is currently used in ARMv8M ports. */ + * This is currently used in ARMv8M ports. */ #ifndef configENABLE_TRUSTZONE - #define configENABLE_TRUSTZONE 1 + #define configENABLE_TRUSTZONE 1 #endif /* Set configRUN_FREERTOS_SECURE_ONLY to 1 to run the FreeRTOS ARMv8M port on -the Secure Side only. */ + * the Secure Side only. */ #ifndef configRUN_FREERTOS_SECURE_ONLY - #define configRUN_FREERTOS_SECURE_ONLY 0 + #define configRUN_FREERTOS_SECURE_ONLY 0 #endif /* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using @@ -1045,8 +1076,9 @@ the Secure Side only. */ * | | | | xTaskCreateRestrictedStatic | | | | * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ */ -#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ - ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE \ + ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ + ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) /* * In line with software engineering best practice, FreeRTOS implements a strict @@ -1060,40 +1092,40 @@ the Secure Side only. */ */ struct xSTATIC_LIST_ITEM { - #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) - TickType_t xDummy1; - #endif - TickType_t xDummy2; - void *pvDummy3[ 4 ]; - #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) - TickType_t xDummy4; - #endif + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void * pvDummy3[ 4 ]; + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy4; + #endif }; typedef struct xSTATIC_LIST_ITEM StaticListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ struct xSTATIC_MINI_LIST_ITEM { - #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) - TickType_t xDummy1; - #endif - TickType_t xDummy2; - void *pvDummy3[ 2 ]; + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void * pvDummy3[ 2 ]; }; typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ typedef struct xSTATIC_LIST { - #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) - TickType_t xDummy1; - #endif - UBaseType_t uxDummy2; - void *pvDummy3; - StaticMiniListItem_t xDummy4; - #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) - TickType_t xDummy5; - #endif + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + UBaseType_t uxDummy2; + void * pvDummy3; + StaticMiniListItem_t xDummy4; + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy5; + #endif } StaticList_t; /* @@ -1111,52 +1143,52 @@ typedef struct xSTATIC_LIST */ typedef struct xSTATIC_TCB { - void *pxDummy1; - #if ( portUSING_MPU_WRAPPERS == 1 ) - xMPU_SETTINGS xDummy2; - #endif - StaticListItem_t xDummy3[ 2 ]; - UBaseType_t uxDummy5; - void *pxDummy6; - uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; - #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) - void *pxDummy8; - #endif - #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - UBaseType_t uxDummy9; - #endif - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy10[ 2 ]; - #endif - #if ( configUSE_MUTEXES == 1 ) - UBaseType_t uxDummy12[ 2 ]; - #endif - #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - void *pxDummy14; - #endif - #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - void *pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; - #endif - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - uint32_t ulDummy16; - #endif - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - struct _reent xDummy17; - #endif - #if ( configUSE_TASK_NOTIFICATIONS == 1 ) - uint32_t ulDummy18; - uint8_t ucDummy19; - #endif - #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) - uint8_t uxDummy20; - #endif - - #if( INCLUDE_xTaskAbortDelay == 1 ) - uint8_t ucDummy21; - #endif - #if ( configUSE_POSIX_ERRNO == 1 ) - int iDummy22; - #endif + void * pxDummy1; + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xDummy2; + #endif + StaticListItem_t xDummy3[ 2 ]; + UBaseType_t uxDummy5; + void * pxDummy6; + uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + void * pxDummy8; + #endif + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxDummy9; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy10[ 2 ]; + #endif + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxDummy12[ 2 ]; + #endif + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void * pxDummy14; + #endif + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void * pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulDummy16; + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + struct _reent xDummy17; + #endif + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + uint32_t ulDummy18[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + uint8_t ucDummy19[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + #endif + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t uxDummy20; + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDummy21; + #endif + #if ( configUSE_POSIX_ERRNO == 1 ) + int iDummy22; + #endif } StaticTask_t; /* @@ -1175,31 +1207,30 @@ typedef struct xSTATIC_TCB */ typedef struct xSTATIC_QUEUE { - void *pvDummy1[ 3 ]; - - union - { - void *pvDummy2; - UBaseType_t uxDummy2; - } u; - - StaticList_t xDummy3[ 2 ]; - UBaseType_t uxDummy4[ 3 ]; - uint8_t ucDummy5[ 2 ]; - - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucDummy6; - #endif - - #if ( configUSE_QUEUE_SETS == 1 ) - void *pvDummy7; - #endif - - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy8; - uint8_t ucDummy9; - #endif - + void * pvDummy1[ 3 ]; + + union + { + void * pvDummy2; + UBaseType_t uxDummy2; + } u; + + StaticList_t xDummy3[ 2 ]; + UBaseType_t uxDummy4[ 3 ]; + uint8_t ucDummy5[ 2 ]; + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy6; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + void * pvDummy7; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy8; + uint8_t ucDummy9; + #endif } StaticQueue_t; typedef StaticQueue_t StaticSemaphore_t; @@ -1219,17 +1250,16 @@ typedef StaticQueue_t StaticSemaphore_t; */ typedef struct xSTATIC_EVENT_GROUP { - TickType_t xDummy1; - StaticList_t xDummy2; - - #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy3; - #endif + TickType_t xDummy1; + StaticList_t xDummy2; - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - uint8_t ucDummy4; - #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy3; + #endif + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy4; + #endif } StaticEventGroup_t; /* @@ -1248,48 +1278,48 @@ typedef struct xSTATIC_EVENT_GROUP */ typedef struct xSTATIC_TIMER { - void *pvDummy1; - StaticListItem_t xDummy2; - TickType_t xDummy3; - void *pvDummy5; - TaskFunction_t pvDummy6; - #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy7; - #endif - uint8_t ucDummy8; - + void * pvDummy1; + StaticListItem_t xDummy2; + TickType_t xDummy3; + void * pvDummy5; + TaskFunction_t pvDummy6; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy7; + #endif + uint8_t ucDummy8; } StaticTimer_t; /* -* In line with software engineering best practice, especially when supplying a -* library that is likely to change in future versions, FreeRTOS implements a -* strict data hiding policy. This means the stream buffer structure used -* internally by FreeRTOS is not accessible to application code. However, if -* the application writer wants to statically allocate the memory required to -* create a stream buffer then the size of the stream buffer object needs to be -* know. The StaticStreamBuffer_t structure below is provided for this purpose. -* Its size and alignment requirements are guaranteed to match those of the -* genuine structure, no matter which architecture is being used, and no matter -* how the values in FreeRTOSConfig.h are set. Its contents are somewhat -* obfuscated in the hope users will recognise that it would be unwise to make -* direct use of the structure members. -*/ + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the stream buffer structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create a stream buffer then the size of the stream buffer object needs to be + * know. The StaticStreamBuffer_t structure below is provided for this purpose. + * Its size and alignment requirements are guaranteed to match those of the + * genuine structure, no matter which architecture is being used, and no matter + * how the values in FreeRTOSConfig.h are set. Its contents are somewhat + * obfuscated in the hope users will recognise that it would be unwise to make + * direct use of the structure members. + */ typedef struct xSTATIC_STREAM_BUFFER { - size_t uxDummy1[ 4 ]; - void * pvDummy2[ 3 ]; - uint8_t ucDummy3; - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxDummy4; - #endif + size_t uxDummy1[ 4 ]; + void * pvDummy2[ 3 ]; + uint8_t ucDummy3; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy4; + #endif } StaticStreamBuffer_t; /* Message buffers are built on stream buffers. */ typedef StaticStreamBuffer_t StaticMessageBuffer_t; +/* *INDENT-OFF* */ #ifdef __cplusplus -} + } #endif +/* *INDENT-ON* */ #endif /* INC_FREERTOS_H */ - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h index ac946118e2..0b7e99d488 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,115 +19,14 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ -#ifndef STACK_MACROS_H -#define STACK_MACROS_H #ifndef _MSC_VER /* Visual Studio doesn't support #warning. */ - #warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in future released. + #warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in future released. #endif -/* - * Call the stack overflow hook function if the stack of the task being swapped - * out is currently overflowed, or looks like it might have overflowed in the - * past. - * - * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check - * the current stack state only - comparing the current top of stack value to - * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 - * will also cause the last few stack bytes to be checked to ensure the value - * to which the bytes were set when the task was created have not been - * overwritten. Note this second test does not guarantee that an overflowed - * stack will always be recognised. - */ - -/*-----------------------------------------------------------*/ - -#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) - - /* Only the current stack state is to be checked. */ - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - /* Is the currently saved stack pointer within the stack limit? */ \ - if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } - -#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ -/*-----------------------------------------------------------*/ - -#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) - - /* Only the current stack state is to be checked. */ - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - \ - /* Is the currently saved stack pointer within the stack limit? */ \ - if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } - -#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ -/*-----------------------------------------------------------*/ - -#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) - - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ - const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ - \ - if( ( pulStack[ 0 ] != ulCheckValue ) || \ - ( pulStack[ 1 ] != ulCheckValue ) || \ - ( pulStack[ 2 ] != ulCheckValue ) || \ - ( pulStack[ 3 ] != ulCheckValue ) ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } - -#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ -/*-----------------------------------------------------------*/ - -#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) - - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ - static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ - \ - \ - pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ - \ - /* Has the extremity of the task stack ever been written over? */ \ - if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } - -#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ -/*-----------------------------------------------------------*/ - -/* Remove stack overflow macro if not being used. */ -#ifndef taskCHECK_FOR_STACK_OVERFLOW - #define taskCHECK_FOR_STACK_OVERFLOW() -#endif - - - -#endif /* STACK_MACROS_H */ - +#include "stack_macros.h" diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h index 795d80122b..093530739b 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /** @@ -38,15 +37,17 @@ #define ATOMIC_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include atomic.h" + #error "include FreeRTOS.h must appear in source files before include atomic.h" #endif /* Standard includes. */ #include +/* *INDENT-OFF* */ #ifdef __cplusplus -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /* * Port specific definitions -- entering/exiting critical section. @@ -58,18 +59,18 @@ extern "C" { */ #if defined( portSET_INTERRUPT_MASK_FROM_ISR ) - /* Nested interrupt scheme is supported in this port. */ - #define ATOMIC_ENTER_CRITICAL() \ - UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR() +/* Nested interrupt scheme is supported in this port. */ + #define ATOMIC_ENTER_CRITICAL() \ + UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR() - #define ATOMIC_EXIT_CRITICAL() \ - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType ) + #define ATOMIC_EXIT_CRITICAL() \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType ) #else - /* Nested interrupt scheme is NOT supported in this port. */ - #define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL() - #define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL() +/* Nested interrupt scheme is NOT supported in this port. */ + #define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL() + #define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL() #endif /* portSET_INTERRUPT_MASK_FROM_ISR() */ @@ -81,11 +82,11 @@ extern "C" { * instead of resulting error, simply define it away. */ #ifndef portFORCE_INLINE - #define portFORCE_INLINE + #define portFORCE_INLINE #endif -#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */ -#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */ +#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */ +#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */ /*----------------------------- Swap && CAS ------------------------------*/ @@ -105,26 +106,26 @@ extern "C" { * *pulDestination value equals ulComparand. */ static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination, - uint32_t ulExchange, - uint32_t ulComparand ) + uint32_t ulExchange, + uint32_t ulComparand ) { -uint32_t ulReturnValue; - - ATOMIC_ENTER_CRITICAL(); - { - if( *pulDestination == ulComparand ) - { - *pulDestination = ulExchange; - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; - } - else - { - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; - } - } - ATOMIC_EXIT_CRITICAL(); - - return ulReturnValue; + uint32_t ulReturnValue; + + ATOMIC_ENTER_CRITICAL(); + { + if( *pulDestination == ulComparand ) + { + *pulDestination = ulExchange; + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; + } + else + { + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; + } + } + ATOMIC_EXIT_CRITICAL(); + + return ulReturnValue; } /*-----------------------------------------------------------*/ @@ -141,18 +142,18 @@ uint32_t ulReturnValue; * @return The initial value of *ppvDestination. */ static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination, - void * pvExchange ) + void * pvExchange ) { -void * pReturnValue; + void * pReturnValue; - ATOMIC_ENTER_CRITICAL(); - { - pReturnValue = *ppvDestination; - *ppvDestination = pvExchange; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + pReturnValue = *ppvDestination; + *ppvDestination = pvExchange; + } + ATOMIC_EXIT_CRITICAL(); - return pReturnValue; + return pReturnValue; } /*-----------------------------------------------------------*/ @@ -173,22 +174,22 @@ void * pReturnValue; * *ppvDestination value equals pvComparand. */ static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination, - void * pvExchange, - void * pvComparand ) + void * pvExchange, + void * pvComparand ) { -uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; - - ATOMIC_ENTER_CRITICAL(); - { - if( *ppvDestination == pvComparand ) - { - *ppvDestination = pvExchange; - ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; - } - } - ATOMIC_EXIT_CRITICAL(); - - return ulReturnValue; + uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; + + ATOMIC_ENTER_CRITICAL(); + { + if( *ppvDestination == pvComparand ) + { + *ppvDestination = pvExchange; + ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS; + } + } + ATOMIC_EXIT_CRITICAL(); + + return ulReturnValue; } @@ -206,18 +207,18 @@ uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE; * @return previous *pulAddend value. */ static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend, - uint32_t ulCount ) + uint32_t ulCount ) { - uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend += ulCount; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend += ulCount; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -234,18 +235,18 @@ static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend, * @return previous *pulAddend value. */ static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend, - uint32_t ulCount ) + uint32_t ulCount ) { - uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend -= ulCount; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend -= ulCount; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -261,16 +262,16 @@ static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAdd */ static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend += 1; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend += 1; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -286,16 +287,16 @@ uint32_t ulCurrent; */ static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulAddend; - *pulAddend -= 1; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulAddend; + *pulAddend -= 1; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*----------------------------- Bitwise Logical ------------------------------*/ @@ -312,18 +313,18 @@ uint32_t ulCurrent; * @return The original value of *pulDestination. */ static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) + uint32_t ulValue ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination |= ulValue; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination |= ulValue; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -339,18 +340,18 @@ uint32_t ulCurrent; * @return The original value of *pulDestination. */ static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) + uint32_t ulValue ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination &= ulValue; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination &= ulValue; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -366,18 +367,18 @@ uint32_t ulCurrent; * @return The original value of *pulDestination. */ static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) + uint32_t ulValue ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination = ~( ulCurrent & ulValue ); - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination = ~( ulCurrent & ulValue ); + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } /*-----------------------------------------------------------*/ @@ -393,22 +394,24 @@ uint32_t ulCurrent; * @return The original value of *pulDestination. */ static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination, - uint32_t ulValue ) + uint32_t ulValue ) { -uint32_t ulCurrent; + uint32_t ulCurrent; - ATOMIC_ENTER_CRITICAL(); - { - ulCurrent = *pulDestination; - *pulDestination ^= ulValue; - } - ATOMIC_EXIT_CRITICAL(); + ATOMIC_ENTER_CRITICAL(); + { + ulCurrent = *pulDestination; + *pulDestination ^= ulValue; + } + ATOMIC_EXIT_CRITICAL(); - return ulCurrent; + return ulCurrent; } +/* *INDENT-OFF* */ #ifdef __cplusplus -} + } #endif +/* *INDENT-ON* */ #endif /* ATOMIC_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h index ed2c161ddb..bf51e6f50e 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,51 +19,54 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #ifndef CO_ROUTINE_H #define CO_ROUTINE_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include croutine.h" + #error "include FreeRTOS.h must appear in source files before include croutine.h" #endif #include "list.h" +/* *INDENT-OFF* */ #ifdef __cplusplus -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /* Used to hide the implementation of the co-routine control block. The -control block structure however has to be included in the header due to -the macro implementation of the co-routine functionality. */ + * control block structure however has to be included in the header due to + * the macro implementation of the co-routine functionality. */ typedef void * CoRoutineHandle_t; /* Defines the prototype to which co-routine functions must conform. */ -typedef void (*crCOROUTINE_CODE)( CoRoutineHandle_t, UBaseType_t ); +typedef void (* crCOROUTINE_CODE)( CoRoutineHandle_t, + UBaseType_t ); typedef struct corCoRoutineControlBlock { - crCOROUTINE_CODE pxCoRoutineFunction; - ListItem_t xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ - ListItem_t xEventListItem; /*< List item used to place the CRCB in event lists. */ - UBaseType_t uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ - UBaseType_t uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ - uint16_t uxState; /*< Used internally by the co-routine implementation. */ -} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */ + crCOROUTINE_CODE pxCoRoutineFunction; + ListItem_t xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ + ListItem_t xEventListItem; /*< List item used to place the CRCB in event lists. */ + UBaseType_t uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ + UBaseType_t uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ + uint16_t uxState; /*< Used internally by the co-routine implementation. */ +} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */ /** * croutine. h - *
- BaseType_t xCoRoutineCreate(
-                                 crCOROUTINE_CODE pxCoRoutineCode,
-                                 UBaseType_t uxPriority,
-                                 UBaseType_t uxIndex
-                               );
+ *
+ * BaseType_t xCoRoutineCreate(
+ *                               crCOROUTINE_CODE pxCoRoutineCode,
+ *                               UBaseType_t uxPriority,
+ *                               UBaseType_t uxIndex
+ *                             ); 
+ * 
* * Create a new co-routine and add it to the list of co-routines that are * ready to run. @@ -83,58 +86,61 @@ typedef struct corCoRoutineControlBlock * list, otherwise an error code defined with ProjDefs.h. * * Example usage: -
- // Co-routine to be created.
- void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- // This may not be necessary for const variables.
- static const char cLedToFlash[ 2 ] = { 5, 6 };
- static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
-
-     // Must start every co-routine with a call to crSTART();
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-         // This co-routine just delays for a fixed period, then toggles
-         // an LED.  Two co-routines are created using this function, so
-         // the uxIndex parameter is used to tell the co-routine which
-         // LED to flash and how int32_t to delay.  This assumes xQueue has
-         // already been created.
-         vParTestToggleLED( cLedToFlash[ uxIndex ] );
-         crDELAY( xHandle, uxFlashRates[ uxIndex ] );
-     }
-
-     // Must end every co-routine with a call to crEND();
-     crEND();
- }
-
- // Function that creates two co-routines.
- void vOtherFunction( void )
- {
- uint8_t ucParameterToPass;
- TaskHandle_t xHandle;
-
-     // Create two co-routines at priority 0.  The first is given index 0
-     // so (from the code above) toggles LED 5 every 200 ticks.  The second
-     // is given index 1 so toggles LED 6 every 400 ticks.
-     for( uxIndex = 0; uxIndex < 2; uxIndex++ )
-     {
-         xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
-     }
- }
-   
+ *
+ * // Co-routine to be created.
+ * void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * // This may not be necessary for const variables.
+ * static const char cLedToFlash[ 2 ] = { 5, 6 };
+ * static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
+ *
+ *   // Must start every co-routine with a call to crSTART();
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *       // This co-routine just delays for a fixed period, then toggles
+ *       // an LED.  Two co-routines are created using this function, so
+ *       // the uxIndex parameter is used to tell the co-routine which
+ *       // LED to flash and how int32_t to delay.  This assumes xQueue has
+ *       // already been created.
+ *       vParTestToggleLED( cLedToFlash[ uxIndex ] );
+ *       crDELAY( xHandle, uxFlashRates[ uxIndex ] );
+ *   }
+ *
+ *   // Must end every co-routine with a call to crEND();
+ *   crEND();
+ * }
+ *
+ * // Function that creates two co-routines.
+ * void vOtherFunction( void )
+ * {
+ * uint8_t ucParameterToPass;
+ * TaskHandle_t xHandle;
+ *
+ *   // Create two co-routines at priority 0.  The first is given index 0
+ *   // so (from the code above) toggles LED 5 every 200 ticks.  The second
+ *   // is given index 1 so toggles LED 6 every 400 ticks.
+ *   for( uxIndex = 0; uxIndex < 2; uxIndex++ )
+ *   {
+ *       xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
+ *   }
+ * }
+ * 
* \defgroup xCoRoutineCreate xCoRoutineCreate * \ingroup Tasks */ -BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex ); +BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, + UBaseType_t uxPriority, + UBaseType_t uxIndex ); /** * croutine. h - *
- void vCoRoutineSchedule( void );
+ *
+ * void vCoRoutineSchedule( void );
+ * 
* * Run a co-routine. * @@ -148,25 +154,25 @@ BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPri * hook). * * Example usage: -
- // This idle task hook will schedule a co-routine each time it is called.
- // The rest of the idle task will execute between co-routine calls.
- void vApplicationIdleHook( void )
- {
-	vCoRoutineSchedule();
- }
-
- // Alternatively, if you do not require any other part of the idle task to
- // execute, the idle task hook can call vCoRoutineSchedule() within an
- // infinite loop.
- void vApplicationIdleHook( void )
- {
-    for( ;; )
-    {
-        vCoRoutineSchedule();
-    }
- }
- 
+ *
+ * // This idle task hook will schedule a co-routine each time it is called.
+ * // The rest of the idle task will execute between co-routine calls.
+ * void vApplicationIdleHook( void )
+ * {
+ *  vCoRoutineSchedule();
+ * }
+ *
+ * // Alternatively, if you do not require any other part of the idle task to
+ * // execute, the idle task hook can call vCoRoutineSchedule() within an
+ * // infinite loop.
+ * void vApplicationIdleHook( void )
+ * {
+ *  for( ;; )
+ *  {
+ *      vCoRoutineSchedule();
+ *  }
+ * }
+ * 
* \defgroup vCoRoutineSchedule vCoRoutineSchedule * \ingroup Tasks */ @@ -175,76 +181,87 @@ void vCoRoutineSchedule( void ); /** * croutine. h *
- crSTART( CoRoutineHandle_t xHandle );
+ * crSTART( CoRoutineHandle_t xHandle ); + * * * This macro MUST always be called at the start of a co-routine function. * * Example usage: -
- // Co-routine to be created.
- void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- static int32_t ulAVariable;
-
-     // Must start every co-routine with a call to crSTART();
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-          // Co-routine functionality goes here.
-     }
-
-     // Must end every co-routine with a call to crEND();
-     crEND();
- }
+ *
+ * // Co-routine to be created.
+ * void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * static int32_t ulAVariable;
+ *
+ *   // Must start every co-routine with a call to crSTART();
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *        // Co-routine functionality goes here.
+ *   }
+ *
+ *   // Must end every co-routine with a call to crEND();
+ *   crEND();
+ * }
+ * 
* \defgroup crSTART crSTART * \ingroup Tasks */ -#define crSTART( pxCRCB ) switch( ( ( CRCB_t * )( pxCRCB ) )->uxState ) { case 0: +#define crSTART( pxCRCB ) \ + switch( ( ( CRCB_t * ) ( pxCRCB ) )->uxState ) { \ + case 0: /** * croutine. h *
- crEND();
+ * crEND(); + * * * This macro MUST always be called at the end of a co-routine function. * * Example usage: -
- // Co-routine to be created.
- void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- static int32_t ulAVariable;
-
-     // Must start every co-routine with a call to crSTART();
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-          // Co-routine functionality goes here.
-     }
-
-     // Must end every co-routine with a call to crEND();
-     crEND();
- }
+ *
+ * // Co-routine to be created.
+ * void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * static int32_t ulAVariable;
+ *
+ *   // Must start every co-routine with a call to crSTART();
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *        // Co-routine functionality goes here.
+ *   }
+ *
+ *   // Must end every co-routine with a call to crEND();
+ *   crEND();
+ * }
+ * 
* \defgroup crSTART crSTART * \ingroup Tasks */ -#define crEND() } +#define crEND() } /* * These macros are intended for internal use by the co-routine implementation * only. The macros should not be used directly by application writers. */ -#define crSET_STATE0( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2): -#define crSET_STATE1( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1): +#define crSET_STATE0( xHandle ) \ + ( ( CRCB_t * ) ( xHandle ) )->uxState = ( __LINE__ * 2 ); return; \ + case ( __LINE__ * 2 ): +#define crSET_STATE1( xHandle ) \ + ( ( CRCB_t * ) ( xHandle ) )->uxState = ( ( __LINE__ * 2 ) + 1 ); return; \ + case ( ( __LINE__ * 2 ) + 1 ): /** * croutine. h - *
- crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
+ *
+ * crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
+ * 
* * Delay a co-routine for a fixed period of time. * @@ -261,48 +278,50 @@ void vCoRoutineSchedule( void ); * can be used to convert ticks to milliseconds. * * Example usage: -
- // Co-routine to be created.
- void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- // This may not be necessary for const variables.
- // We are to delay for 200ms.
- static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
-
-     // Must start every co-routine with a call to crSTART();
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-        // Delay for 200ms.
-        crDELAY( xHandle, xDelayTime );
-
-        // Do something here.
-     }
-
-     // Must end every co-routine with a call to crEND();
-     crEND();
- }
+ *
+ * // Co-routine to be created.
+ * void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * // This may not be necessary for const variables.
+ * // We are to delay for 200ms.
+ * static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
+ *
+ *   // Must start every co-routine with a call to crSTART();
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *      // Delay for 200ms.
+ *      crDELAY( xHandle, xDelayTime );
+ *
+ *      // Do something here.
+ *   }
+ *
+ *   // Must end every co-routine with a call to crEND();
+ *   crEND();
+ * }
+ * 
* \defgroup crDELAY crDELAY * \ingroup Tasks */ -#define crDELAY( xHandle, xTicksToDelay ) \ - if( ( xTicksToDelay ) > 0 ) \ - { \ - vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \ - } \ - crSET_STATE0( ( xHandle ) ); +#define crDELAY( xHandle, xTicksToDelay ) \ + if( ( xTicksToDelay ) > 0 ) \ + { \ + vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \ + } \ + crSET_STATE0( ( xHandle ) ); /** *
- crQUEUE_SEND(
-                  CoRoutineHandle_t xHandle,
-                  QueueHandle_t pxQueue,
-                  void *pvItemToQueue,
-                  TickType_t xTicksToWait,
-                  BaseType_t *pxResult
-             )
+ * crQUEUE_SEND( + * CoRoutineHandle_t xHandle, + * QueueHandle_t pxQueue, + * void *pvItemToQueue, + * TickType_t xTicksToWait, + * BaseType_t *pxResult + * ) + * * * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. @@ -342,66 +361,68 @@ void vCoRoutineSchedule( void ); * error defined within ProjDefs.h. * * Example usage: -
- // Co-routine function that blocks for a fixed period then posts a number onto
- // a queue.
- static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- static BaseType_t xNumberToPost = 0;
- static BaseType_t xResult;
-
-    // Co-routines must begin with a call to crSTART().
-    crSTART( xHandle );
-
-    for( ;; )
-    {
-        // This assumes the queue has already been created.
-        crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
-
-        if( xResult != pdPASS )
-        {
-            // The message was not posted!
-        }
-
-        // Increment the number to be posted onto the queue.
-        xNumberToPost++;
-
-        // Delay for 100 ticks.
-        crDELAY( xHandle, 100 );
-    }
-
-    // Co-routines must end with a call to crEND().
-    crEND();
- }
+ *
+ * // Co-routine function that blocks for a fixed period then posts a number onto
+ * // a queue.
+ * static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * static BaseType_t xNumberToPost = 0;
+ * static BaseType_t xResult;
+ *
+ *  // Co-routines must begin with a call to crSTART().
+ *  crSTART( xHandle );
+ *
+ *  for( ;; )
+ *  {
+ *      // This assumes the queue has already been created.
+ *      crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
+ *
+ *      if( xResult != pdPASS )
+ *      {
+ *          // The message was not posted!
+ *      }
+ *
+ *      // Increment the number to be posted onto the queue.
+ *      xNumberToPost++;
+ *
+ *      // Delay for 100 ticks.
+ *      crDELAY( xHandle, 100 );
+ *  }
+ *
+ *  // Co-routines must end with a call to crEND().
+ *  crEND();
+ * }
+ * 
* \defgroup crQUEUE_SEND crQUEUE_SEND * \ingroup Tasks */ -#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ -{ \ - *( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) ); \ - if( *( pxResult ) == errQUEUE_BLOCKED ) \ - { \ - crSET_STATE0( ( xHandle ) ); \ - *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \ - } \ - if( *pxResult == errQUEUE_YIELD ) \ - { \ - crSET_STATE1( ( xHandle ) ); \ - *pxResult = pdPASS; \ - } \ -} +#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ + { \ + *( pxResult ) = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *pxResult = pdPASS; \ + } \ + } /** * croutine. h *
-  crQUEUE_RECEIVE(
-                     CoRoutineHandle_t xHandle,
-                     QueueHandle_t pxQueue,
-                     void *pvBuffer,
-                     TickType_t xTicksToWait,
-                     BaseType_t *pxResult
-                 )
+ * crQUEUE_RECEIVE( + * CoRoutineHandle_t xHandle, + * QueueHandle_t pxQueue, + * void *pvBuffer, + * TickType_t xTicksToWait, + * BaseType_t *pxResult + * ) + * * * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. @@ -440,58 +461,60 @@ void vCoRoutineSchedule( void ); * an error code as defined within ProjDefs.h. * * Example usage: -
- // A co-routine receives the number of an LED to flash from a queue.  It
- // blocks on the queue until the number is received.
- static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // Variables in co-routines must be declared static if they must maintain value across a blocking call.
- static BaseType_t xResult;
- static UBaseType_t uxLEDToFlash;
-
-    // All co-routines must start with a call to crSTART().
-    crSTART( xHandle );
-
-    for( ;; )
-    {
-        // Wait for data to become available on the queue.
-        crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
-
-        if( xResult == pdPASS )
-        {
-            // We received the LED to flash - flash it!
-            vParTestToggleLED( uxLEDToFlash );
-        }
-    }
-
-    crEND();
- }
+ *
+ * // A co-routine receives the number of an LED to flash from a queue.  It
+ * // blocks on the queue until the number is received.
+ * static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ * static BaseType_t xResult;
+ * static UBaseType_t uxLEDToFlash;
+ *
+ *  // All co-routines must start with a call to crSTART().
+ *  crSTART( xHandle );
+ *
+ *  for( ;; )
+ *  {
+ *      // Wait for data to become available on the queue.
+ *      crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+ *
+ *      if( xResult == pdPASS )
+ *      {
+ *          // We received the LED to flash - flash it!
+ *          vParTestToggleLED( uxLEDToFlash );
+ *      }
+ *  }
+ *
+ *  crEND();
+ * }
+ * 
* \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE * \ingroup Tasks */ -#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ -{ \ - *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), ( xTicksToWait ) ); \ - if( *( pxResult ) == errQUEUE_BLOCKED ) \ - { \ - crSET_STATE0( ( xHandle ) ); \ - *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), 0 ); \ - } \ - if( *( pxResult ) == errQUEUE_YIELD ) \ - { \ - crSET_STATE1( ( xHandle ) ); \ - *( pxResult ) = pdPASS; \ - } \ -} +#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ + { \ + *( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), 0 ); \ + } \ + if( *( pxResult ) == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *( pxResult ) = pdPASS; \ + } \ + } /** * croutine. h *
-  crQUEUE_SEND_FROM_ISR(
-                            QueueHandle_t pxQueue,
-                            void *pvItemToQueue,
-                            BaseType_t xCoRoutinePreviouslyWoken
-                       )
+ * crQUEUE_SEND_FROM_ISR( + * QueueHandle_t pxQueue, + * void *pvItemToQueue, + * BaseType_t xCoRoutinePreviouslyWoken + * ) + * * * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() @@ -526,69 +549,72 @@ void vCoRoutineSchedule( void ); * the ISR. * * Example usage: -
- // A co-routine that blocks on a queue waiting for characters to be received.
- static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- char cRxedChar;
- BaseType_t xResult;
-
-     // All co-routines must start with a call to crSTART().
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-         // Wait for data to become available on the queue.  This assumes the
-         // queue xCommsRxQueue has already been created!
-         crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
-
-         // Was a character received?
-         if( xResult == pdPASS )
-         {
-             // Process the character here.
-         }
-     }
-
-     // All co-routines must end with a call to crEND().
-     crEND();
- }
-
- // An ISR that uses a queue to send characters received on a serial port to
- // a co-routine.
- void vUART_ISR( void )
- {
- char cRxedChar;
- BaseType_t xCRWokenByPost = pdFALSE;
-
-     // We loop around reading characters until there are none left in the UART.
-     while( UART_RX_REG_NOT_EMPTY() )
-     {
-         // Obtain the character from the UART.
-         cRxedChar = UART_RX_REG;
-
-         // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
-         // the first time around the loop.  If the post causes a co-routine
-         // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
-         // In this manner we can ensure that if more than one co-routine is
-         // blocked on the queue only one is woken by this ISR no matter how
-         // many characters are posted to the queue.
-         xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
-     }
- }
+ *
+ * // A co-routine that blocks on a queue waiting for characters to be received.
+ * static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * char cRxedChar;
+ * BaseType_t xResult;
+ *
+ *   // All co-routines must start with a call to crSTART().
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *       // Wait for data to become available on the queue.  This assumes the
+ *       // queue xCommsRxQueue has already been created!
+ *       crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+ *
+ *       // Was a character received?
+ *       if( xResult == pdPASS )
+ *       {
+ *           // Process the character here.
+ *       }
+ *   }
+ *
+ *   // All co-routines must end with a call to crEND().
+ *   crEND();
+ * }
+ *
+ * // An ISR that uses a queue to send characters received on a serial port to
+ * // a co-routine.
+ * void vUART_ISR( void )
+ * {
+ * char cRxedChar;
+ * BaseType_t xCRWokenByPost = pdFALSE;
+ *
+ *   // We loop around reading characters until there are none left in the UART.
+ *   while( UART_RX_REG_NOT_EMPTY() )
+ *   {
+ *       // Obtain the character from the UART.
+ *       cRxedChar = UART_RX_REG;
+ *
+ *       // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
+ *       // the first time around the loop.  If the post causes a co-routine
+ *       // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
+ *       // In this manner we can ensure that if more than one co-routine is
+ *       // blocked on the queue only one is woken by this ISR no matter how
+ *       // many characters are posted to the queue.
+ *       xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
+ *   }
+ * }
+ * 
* \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR * \ingroup Tasks */ -#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) ) +#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) \ + xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) ) /** * croutine. h *
-  crQUEUE_SEND_FROM_ISR(
-                            QueueHandle_t pxQueue,
-                            void *pvBuffer,
-                            BaseType_t * pxCoRoutineWoken
-                       )
+ * crQUEUE_SEND_FROM_ISR( + * QueueHandle_t pxQueue, + * void *pvBuffer, + * BaseType_t * pxCoRoutineWoken + * ) + * * * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() @@ -623,75 +649,77 @@ void vCoRoutineSchedule( void ); * pdFALSE. * * Example usage: -
- // A co-routine that posts a character to a queue then blocks for a fixed
- // period.  The character is incremented each time.
- static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
- {
- // cChar holds its value while this co-routine is blocked and must therefore
- // be declared static.
- static char cCharToTx = 'a';
- BaseType_t xResult;
-
-     // All co-routines must start with a call to crSTART().
-     crSTART( xHandle );
-
-     for( ;; )
-     {
-         // Send the next character to the queue.
-         crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
-
-         if( xResult == pdPASS )
-         {
-             // The character was successfully posted to the queue.
-         }
-		 else
-		 {
-			// Could not post the character to the queue.
-		 }
-
-         // Enable the UART Tx interrupt to cause an interrupt in this
-		 // hypothetical UART.  The interrupt will obtain the character
-		 // from the queue and send it.
-		 ENABLE_RX_INTERRUPT();
-
-		 // Increment to the next character then block for a fixed period.
-		 // cCharToTx will maintain its value across the delay as it is
-		 // declared static.
-		 cCharToTx++;
-		 if( cCharToTx > 'x' )
-		 {
-			cCharToTx = 'a';
-		 }
-		 crDELAY( 100 );
-     }
-
-     // All co-routines must end with a call to crEND().
-     crEND();
- }
-
- // An ISR that uses a queue to receive characters to send on a UART.
- void vUART_ISR( void )
- {
- char cCharToTx;
- BaseType_t xCRWokenByPost = pdFALSE;
-
-     while( UART_TX_REG_EMPTY() )
-     {
-         // Are there any characters in the queue waiting to be sent?
-		 // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
-		 // is woken by the post - ensuring that only a single co-routine is
-		 // woken no matter how many times we go around this loop.
-         if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
-		 {
-			 SEND_CHARACTER( cCharToTx );
-		 }
-     }
- }
+ *
+ * // A co-routine that posts a character to a queue then blocks for a fixed
+ * // period.  The character is incremented each time.
+ * static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ * {
+ * // cChar holds its value while this co-routine is blocked and must therefore
+ * // be declared static.
+ * static char cCharToTx = 'a';
+ * BaseType_t xResult;
+ *
+ *   // All co-routines must start with a call to crSTART().
+ *   crSTART( xHandle );
+ *
+ *   for( ;; )
+ *   {
+ *       // Send the next character to the queue.
+ *       crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
+ *
+ *       if( xResult == pdPASS )
+ *       {
+ *           // The character was successfully posted to the queue.
+ *       }
+ *       else
+ *       {
+ *          // Could not post the character to the queue.
+ *       }
+ *
+ *       // Enable the UART Tx interrupt to cause an interrupt in this
+ *       // hypothetical UART.  The interrupt will obtain the character
+ *       // from the queue and send it.
+ *       ENABLE_RX_INTERRUPT();
+ *
+ *       // Increment to the next character then block for a fixed period.
+ *       // cCharToTx will maintain its value across the delay as it is
+ *       // declared static.
+ *       cCharToTx++;
+ *       if( cCharToTx > 'x' )
+ *       {
+ *          cCharToTx = 'a';
+ *       }
+ *       crDELAY( 100 );
+ *   }
+ *
+ *   // All co-routines must end with a call to crEND().
+ *   crEND();
+ * }
+ *
+ * // An ISR that uses a queue to receive characters to send on a UART.
+ * void vUART_ISR( void )
+ * {
+ * char cCharToTx;
+ * BaseType_t xCRWokenByPost = pdFALSE;
+ *
+ *   while( UART_TX_REG_EMPTY() )
+ *   {
+ *       // Are there any characters in the queue waiting to be sent?
+ *       // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
+ *       // is woken by the post - ensuring that only a single co-routine is
+ *       // woken no matter how many times we go around this loop.
+ *       if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
+ *       {
+ *           SEND_CHARACTER( cCharToTx );
+ *       }
+ *   }
+ * }
+ * 
* \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR * \ingroup Tasks */ -#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) ) +#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) \ + xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) ) /* * This function is intended for internal use by the co-routine macros only. @@ -702,7 +730,8 @@ void vCoRoutineSchedule( void ); * Removes the current co-routine from its ready list and places it in the * appropriate delayed list. */ -void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList ); +void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, + List_t * pxEventList ); /* * This function is intended for internal use by the queue implementation only. @@ -711,10 +740,12 @@ void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList ) * Removes the highest priority co-routine from the event list and places it in * the pending ready list. */ -BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList ); +BaseType_t xCoRoutineRemoveFromEventList( const List_t * pxEventList ); +/* *INDENT-OFF* */ #ifdef __cplusplus -} + } #endif +/* *INDENT-ON* */ #endif /* CO_ROUTINE_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h new file mode 100644 index 0000000000..4ca7148753 --- /dev/null +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h @@ -0,0 +1,40 @@ +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +#ifndef DEPRECATED_DEFINITIONS_H +#define DEPRECATED_DEFINITIONS_H + +/* Each FreeRTOS port has a unique portmacro.h header file. Originally a + * pre-processor definition was used to ensure the pre-processor found the correct + * portmacro.h file for the port being used. That scheme was deprecated in favour + * of setting the compiler's include path such that it found the correct + * portmacro.h file - removing the need for the constant and allowing the + * portmacro.h file to be located anywhere in relation to the port being used. The + * definitions below remain in the code for backward compatibility only. New + * projects should not use them. */ +#include "portmacro.h" + +#endif /* DEPRECATED_DEFINITIONS_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h index bf8a985b6b..e03b70dc84 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h @@ -1,757 +1,775 @@ -/* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - -#ifndef EVENT_GROUPS_H -#define EVENT_GROUPS_H - -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h" must appear in source files before "include event_groups.h" -#endif - -/* FreeRTOS includes. */ -#include "timers.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * An event group is a collection of bits to which an application can assign a - * meaning. For example, an application may create an event group to convey - * the status of various CAN bus related events in which bit 0 might mean "A CAN - * message has been received and is ready for processing", bit 1 might mean "The - * application has queued a message that is ready for sending onto the CAN - * network", and bit 2 might mean "It is time to send a SYNC message onto the - * CAN network" etc. A task can then test the bit values to see which events - * are active, and optionally enter the Blocked state to wait for a specified - * bit or a group of specified bits to be active. To continue the CAN bus - * example, a CAN controlling task can enter the Blocked state (and therefore - * not consume any processing time) until either bit 0, bit 1 or bit 2 are - * active, at which time the bit that was actually active would inform the task - * which action it had to take (process a received message, send a message, or - * send a SYNC). - * - * The event groups implementation contains intelligence to avoid race - * conditions that would otherwise occur were an application to use a simple - * variable for the same purpose. This is particularly important with respect - * to when a bit within an event group is to be cleared, and when bits have to - * be set and then tested atomically - as is the case where event groups are - * used to create a synchronisation point between multiple tasks (a - * 'rendezvous'). - * - * \defgroup EventGroup - */ - - - -/** - * event_groups.h - * - * Type by which event groups are referenced. For example, a call to - * xEventGroupCreate() returns an EventGroupHandle_t variable that can then - * be used as a parameter to other event group functions. - * - * \defgroup EventGroupHandle_t EventGroupHandle_t - * \ingroup EventGroup - */ -struct EventGroupDef_t; -typedef struct EventGroupDef_t * EventGroupHandle_t; - -/* - * The type that holds event bits always matches TickType_t - therefore the - * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1, - * 32 bits if set to 0. - * - * \defgroup EventBits_t EventBits_t - * \ingroup EventGroup - */ -typedef TickType_t EventBits_t; - -/** - * event_groups.h - *
- EventGroupHandle_t xEventGroupCreate( void );
- 
- * - * Create a new event group. - * - * Internally, within the FreeRTOS implementation, event groups use a [small] - * block of memory, in which the event group's structure is stored. If an event - * groups is created using xEventGropuCreate() then the required memory is - * automatically dynamically allocated inside the xEventGroupCreate() function. - * (see http://www.freertos.org/a00111.html). If an event group is created - * using xEventGropuCreateStatic() then the application writer must instead - * provide the memory that will get used by the event group. - * xEventGroupCreateStatic() therefore allows an event group to be created - * without using any dynamic memory allocation. - * - * Although event groups are not related to ticks, for internal implementation - * reasons the number of bits available for use in an event group is dependent - * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If - * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit - * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has - * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store - * event bits within an event group. - * - * @return If the event group was created then a handle to the event group is - * returned. If there was insufficient FreeRTOS heap available to create the - * event group then NULL is returned. See http://www.freertos.org/a00111.html - * - * Example usage: -
-	// Declare a variable to hold the created event group.
-	EventGroupHandle_t xCreatedEventGroup;
-
-	// Attempt to create the event group.
-	xCreatedEventGroup = xEventGroupCreate();
-
-	// Was the event group created successfully?
-	if( xCreatedEventGroup == NULL )
-	{
-		// The event group was not created because there was insufficient
-		// FreeRTOS heap available.
-	}
-	else
-	{
-		// The event group was created.
-	}
-   
- * \defgroup xEventGroupCreate xEventGroupCreate - * \ingroup EventGroup - */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; -#endif - -/** - * event_groups.h - *
- EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
- 
- * - * Create a new event group. - * - * Internally, within the FreeRTOS implementation, event groups use a [small] - * block of memory, in which the event group's structure is stored. If an event - * groups is created using xEventGropuCreate() then the required memory is - * automatically dynamically allocated inside the xEventGroupCreate() function. - * (see http://www.freertos.org/a00111.html). If an event group is created - * using xEventGropuCreateStatic() then the application writer must instead - * provide the memory that will get used by the event group. - * xEventGroupCreateStatic() therefore allows an event group to be created - * without using any dynamic memory allocation. - * - * Although event groups are not related to ticks, for internal implementation - * reasons the number of bits available for use in an event group is dependent - * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If - * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit - * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has - * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store - * event bits within an event group. - * - * @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type - * StaticEventGroup_t, which will be then be used to hold the event group's data - * structures, removing the need for the memory to be allocated dynamically. - * - * @return If the event group was created then a handle to the event group is - * returned. If pxEventGroupBuffer was NULL then NULL is returned. - * - * Example usage: -
-	// StaticEventGroup_t is a publicly accessible structure that has the same
-	// size and alignment requirements as the real event group structure.  It is
-	// provided as a mechanism for applications to know the size of the event
-	// group (which is dependent on the architecture and configuration file
-	// settings) without breaking the strict data hiding policy by exposing the
-	// real event group internals.  This StaticEventGroup_t variable is passed
-	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
-	// the event group's data structures
-	StaticEventGroup_t xEventGroupBuffer;
-
-	// Create the event group without dynamically allocating any memory.
-	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
-   
- */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; -#endif - -/** - * event_groups.h - *
-	EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
-										const EventBits_t uxBitsToWaitFor,
-										const BaseType_t xClearOnExit,
-										const BaseType_t xWaitForAllBits,
-										const TickType_t xTicksToWait );
- 
- * - * [Potentially] block to wait for one or more bits to be set within a - * previously created event group. - * - * This function cannot be called from an interrupt. - * - * @param xEventGroup The event group in which the bits are being tested. The - * event group must have previously been created using a call to - * xEventGroupCreate(). - * - * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test - * inside the event group. For example, to wait for bit 0 and/or bit 2 set - * uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set - * uxBitsToWaitFor to 0x07. Etc. - * - * @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within - * uxBitsToWaitFor that are set within the event group will be cleared before - * xEventGroupWaitBits() returns if the wait condition was met (if the function - * returns for a reason other than a timeout). If xClearOnExit is set to - * pdFALSE then the bits set in the event group are not altered when the call to - * xEventGroupWaitBits() returns. - * - * @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then - * xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor - * are set or the specified block time expires. If xWaitForAllBits is set to - * pdFALSE then xEventGroupWaitBits() will return when any one of the bits set - * in uxBitsToWaitFor is set or the specified block time expires. The block - * time is specified by the xTicksToWait parameter. - * - * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait - * for one/all (depending on the xWaitForAllBits value) of the bits specified by - * uxBitsToWaitFor to become set. - * - * @return The value of the event group at the time either the bits being waited - * for became set, or the block time expired. Test the return value to know - * which bits were set. If xEventGroupWaitBits() returned because its timeout - * expired then not all the bits being waited for will be set. If - * xEventGroupWaitBits() returned because the bits it was waiting for were set - * then the returned value is the event group value before any bits were - * automatically cleared in the case that xClearOnExit parameter was set to - * pdTRUE. - * - * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
-		// the event group.  Clear the bits before exiting.
-		uxBits = xEventGroupWaitBits(
-					xEventGroup,	// The event group being tested.
-					BIT_0 | BIT_4,	// The bits within the event group to wait for.
-					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
-					pdFALSE,		// Don't wait for both bits, either bit will do.
-					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// xEventGroupWaitBits() returned because both bits were set.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_0 was set.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// xEventGroupWaitBits() returned because just BIT_4 was set.
-		}
-		else
-		{
-			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
-			// without either BIT_0 or BIT_4 becoming set.
-		}
-   }
-   
- * \defgroup xEventGroupWaitBits xEventGroupWaitBits - * \ingroup EventGroup - */ -EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * event_groups.h - *
-	EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
- 
- * - * Clear bits within an event group. This function cannot be called from an - * interrupt. - * - * @param xEventGroup The event group in which the bits are to be cleared. - * - * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear - * in the event group. For example, to clear bit 3 only, set uxBitsToClear to - * 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09. - * - * @return The value of the event group before the specified bits were cleared. - * - * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Clear bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupClearBits(
-								xEventGroup,	// The event group being updated.
-								BIT_0 | BIT_4 );// The bits being cleared.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
-			// called.  Both will now be clear (not set).
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 was set before xEventGroupClearBits() was called.  It will
-			// now be clear.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 were set in the first place.
-		}
-   }
-   
- * \defgroup xEventGroupClearBits xEventGroupClearBits - * \ingroup EventGroup - */ -EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; - -/** - * event_groups.h - *
-	BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * - * A version of xEventGroupClearBits() that can be called from an interrupt. - * - * Setting bits in an event group is not a deterministic operation because there - * are an unknown number of tasks that may be waiting for the bit or bits being - * set. FreeRTOS does not allow nondeterministic operations to be performed - * while interrupts are disabled, so protects event groups that are accessed - * from tasks by suspending the scheduler rather than disabling interrupts. As - * a result event groups cannot be accessed directly from an interrupt service - * routine. Therefore xEventGroupClearBitsFromISR() sends a message to the - * timer task to have the clear operation performed in the context of the timer - * task. - * - * @param xEventGroup The event group in which the bits are to be cleared. - * - * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear. - * For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3 - * and bit 0 set uxBitsToClear to 0x09. - * - * @return If the request to execute the function was posted successfully then - * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned - * if the timer service queue was full. - * - * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-		// Clear bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupClearBitsFromISR(
-							xEventGroup,	 // The event group being updated.
-							BIT_0 | BIT_4 ); // The bits being set.
-
-		if( xResult == pdPASS )
-		{
-			// The message was posted successfully.
-		}
-  }
-   
- * \defgroup xEventGroupClearBitsFromISR xEventGroupClearBitsFromISR - * \ingroup EventGroup - */ -#if( configUSE_TRACE_FACILITY == 1 ) - BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; -#else - #define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ) -#endif - -/** - * event_groups.h - *
-	EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
- 
- * - * Set bits within an event group. - * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() - * is a version that can be called from an interrupt. - * - * Setting bits in an event group will automatically unblock tasks that are - * blocked waiting for the bits. - * - * @param xEventGroup The event group in which the bits are to be set. - * - * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. - * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 - * and bit 0 set uxBitsToSet to 0x09. - * - * @return The value of the event group at the time the call to - * xEventGroupSetBits() returns. There are two reasons why the returned value - * might have the bits specified by the uxBitsToSet parameter cleared. First, - * if setting a bit results in a task that was waiting for the bit leaving the - * blocked state then it is possible the bit will be cleared automatically - * (see the xClearBitOnExit parameter of xEventGroupWaitBits()). Second, any - * unblocked (or otherwise Ready state) task that has a priority above that of - * the task that called xEventGroupSetBits() will execute and may change the - * event group value before the call to xEventGroupSetBits() returns. - * - * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   void aFunction( EventGroupHandle_t xEventGroup )
-   {
-   EventBits_t uxBits;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		uxBits = xEventGroupSetBits(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4 );// The bits being set.
-
-		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
-		{
-			// Both bit 0 and bit 4 remained set when the function returned.
-		}
-		else if( ( uxBits & BIT_0 ) != 0 )
-		{
-			// Bit 0 remained set when the function returned, but bit 4 was
-			// cleared.  It might be that bit 4 was cleared automatically as a
-			// task that was waiting for bit 4 was removed from the Blocked
-			// state.
-		}
-		else if( ( uxBits & BIT_4 ) != 0 )
-		{
-			// Bit 4 remained set when the function returned, but bit 0 was
-			// cleared.  It might be that bit 0 was cleared automatically as a
-			// task that was waiting for bit 0 was removed from the Blocked
-			// state.
-		}
-		else
-		{
-			// Neither bit 0 nor bit 4 remained set.  It might be that a task
-			// was waiting for both of the bits to be set, and the bits were
-			// cleared as the task left the Blocked state.
-		}
-   }
-   
- * \defgroup xEventGroupSetBits xEventGroupSetBits - * \ingroup EventGroup - */ -EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; - -/** - * event_groups.h - *
-	BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
- 
- * - * A version of xEventGroupSetBits() that can be called from an interrupt. - * - * Setting bits in an event group is not a deterministic operation because there - * are an unknown number of tasks that may be waiting for the bit or bits being - * set. FreeRTOS does not allow nondeterministic operations to be performed in - * interrupts or from critical sections. Therefore xEventGroupSetBitsFromISR() - * sends a message to the timer task to have the set operation performed in the - * context of the timer task - where a scheduler lock is used in place of a - * critical section. - * - * @param xEventGroup The event group in which the bits are to be set. - * - * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. - * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 - * and bit 0 set uxBitsToSet to 0x09. - * - * @param pxHigherPriorityTaskWoken As mentioned above, calling this function - * will result in a message being sent to the timer daemon task. If the - * priority of the timer daemon task is higher than the priority of the - * currently running task (the task the interrupt interrupted) then - * *pxHigherPriorityTaskWoken will be set to pdTRUE by - * xEventGroupSetBitsFromISR(), indicating that a context switch should be - * requested before the interrupt exits. For that reason - * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the - * example code below. - * - * @return If the request to execute the function was posted successfully then - * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned - * if the timer service queue was full. - * - * Example usage: -
-   #define BIT_0	( 1 << 0 )
-   #define BIT_4	( 1 << 4 )
-
-   // An event group which it is assumed has already been created by a call to
-   // xEventGroupCreate().
-   EventGroupHandle_t xEventGroup;
-
-   void anInterruptHandler( void )
-   {
-   BaseType_t xHigherPriorityTaskWoken, xResult;
-
-		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
-		xHigherPriorityTaskWoken = pdFALSE;
-
-		// Set bit 0 and bit 4 in xEventGroup.
-		xResult = xEventGroupSetBitsFromISR(
-							xEventGroup,	// The event group being updated.
-							BIT_0 | BIT_4   // The bits being set.
-							&xHigherPriorityTaskWoken );
-
-		// Was the message posted successfully?
-		if( xResult == pdPASS )
-		{
-			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
-			// switch should be requested.  The macro used is port specific and
-			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
-			// refer to the documentation page for the port being used.
-			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-		}
-  }
-   
- * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR - * \ingroup EventGroup - */ -#if( configUSE_TRACE_FACILITY == 1 ) - BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; -#else - #define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ) -#endif - -/** - * event_groups.h - *
-	EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
-									const EventBits_t uxBitsToSet,
-									const EventBits_t uxBitsToWaitFor,
-									TickType_t xTicksToWait );
- 
- * - * Atomically set bits within an event group, then wait for a combination of - * bits to be set within the same event group. This functionality is typically - * used to synchronise multiple tasks, where each task has to wait for the other - * tasks to reach a synchronisation point before proceeding. - * - * This function cannot be used from an interrupt. - * - * The function will return before its block time expires if the bits specified - * by the uxBitsToWait parameter are set, or become set within that time. In - * this case all the bits specified by uxBitsToWait will be automatically - * cleared before the function returns. - * - * @param xEventGroup The event group in which the bits are being tested. The - * event group must have previously been created using a call to - * xEventGroupCreate(). - * - * @param uxBitsToSet The bits to set in the event group before determining - * if, and possibly waiting for, all the bits specified by the uxBitsToWait - * parameter are set. - * - * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test - * inside the event group. For example, to wait for bit 0 and bit 2 set - * uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set - * uxBitsToWaitFor to 0x07. Etc. - * - * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait - * for all of the bits specified by uxBitsToWaitFor to become set. - * - * @return The value of the event group at the time either the bits being waited - * for became set, or the block time expired. Test the return value to know - * which bits were set. If xEventGroupSync() returned because its timeout - * expired then not all the bits being waited for will be set. If - * xEventGroupSync() returned because all the bits it was waiting for were - * set then the returned value is the event group value before any bits were - * automatically cleared. - * - * Example usage: -
- // Bits used by the three tasks.
- #define TASK_0_BIT		( 1 << 0 )
- #define TASK_1_BIT		( 1 << 1 )
- #define TASK_2_BIT		( 1 << 2 )
-
- #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
-
- // Use an event group to synchronise three tasks.  It is assumed this event
- // group has already been created elsewhere.
- EventGroupHandle_t xEventBits;
-
- void vTask0( void *pvParameters )
- {
- EventBits_t uxReturn;
- TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
-
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 0 in the event flag to note this task has reached the
-		// sync point.  The other two tasks will set the other two bits defined
-		// by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
-		// point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
-		// for this to happen.
-		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
-
-		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
-		{
-			// All three tasks reached the synchronisation point before the call
-			// to xEventGroupSync() timed out.
-		}
-	}
- }
-
- void vTask1( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 1 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	 }
- }
-
- void vTask2( void *pvParameters )
- {
-	 for( ;; )
-	 {
-		// Perform task functionality here.
-
-		// Set bit 2 in the event flag to note this task has reached the
-		// synchronisation point.  The other two tasks will set the other two
-		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
-		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
-		// indefinitely for this to happen.
-		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
-
-		// xEventGroupSync() was called with an indefinite block time, so
-		// this task will only reach here if the syncrhonisation was made by all
-		// three tasks, so there is no need to test the return value.
-	}
- }
-
- 
- * \defgroup xEventGroupSync xEventGroupSync - * \ingroup EventGroup - */ -EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - - -/** - * event_groups.h - *
-	EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
- 
- * - * Returns the current value of the bits in an event group. This function - * cannot be used from an interrupt. - * - * @param xEventGroup The event group being queried. - * - * @return The event group bits at the time xEventGroupGetBits() was called. - * - * \defgroup xEventGroupGetBits xEventGroupGetBits - * \ingroup EventGroup - */ -#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) - -/** - * event_groups.h - *
-	EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
- 
- * - * A version of xEventGroupGetBits() that can be called from an ISR. - * - * @param xEventGroup The event group being queried. - * - * @return The event group bits at the time xEventGroupGetBitsFromISR() was called. - * - * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR - * \ingroup EventGroup - */ -EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; - -/** - * event_groups.h - *
-	void xEventGroupDelete( EventGroupHandle_t xEventGroup );
- 
- * - * Delete an event group that was previously created by a call to - * xEventGroupCreate(). Tasks that are blocked on the event group will be - * unblocked and obtain 0 as the event group's value. - * - * @param xEventGroup The event group being deleted. - */ -void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; - -/* For internal use only. */ -void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION; -void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; - - -#if (configUSE_TRACE_FACILITY == 1) - UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) PRIVILEGED_FUNCTION; - void vEventGroupSetNumber( void* xEventGroup, UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION; -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* EVENT_GROUPS_H */ - - +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +#ifndef EVENT_GROUPS_H +#define EVENT_GROUPS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include event_groups.h" +#endif + +/* FreeRTOS includes. */ +#include "timers.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * An event group is a collection of bits to which an application can assign a + * meaning. For example, an application may create an event group to convey + * the status of various CAN bus related events in which bit 0 might mean "A CAN + * message has been received and is ready for processing", bit 1 might mean "The + * application has queued a message that is ready for sending onto the CAN + * network", and bit 2 might mean "It is time to send a SYNC message onto the + * CAN network" etc. A task can then test the bit values to see which events + * are active, and optionally enter the Blocked state to wait for a specified + * bit or a group of specified bits to be active. To continue the CAN bus + * example, a CAN controlling task can enter the Blocked state (and therefore + * not consume any processing time) until either bit 0, bit 1 or bit 2 are + * active, at which time the bit that was actually active would inform the task + * which action it had to take (process a received message, send a message, or + * send a SYNC). + * + * The event groups implementation contains intelligence to avoid race + * conditions that would otherwise occur were an application to use a simple + * variable for the same purpose. This is particularly important with respect + * to when a bit within an event group is to be cleared, and when bits have to + * be set and then tested atomically - as is the case where event groups are + * used to create a synchronisation point between multiple tasks (a + * 'rendezvous'). + * + * \defgroup EventGroup + */ + + + +/** + * event_groups.h + * + * Type by which event groups are referenced. For example, a call to + * xEventGroupCreate() returns an EventGroupHandle_t variable that can then + * be used as a parameter to other event group functions. + * + * \defgroup EventGroupHandle_t EventGroupHandle_t + * \ingroup EventGroup + */ +struct EventGroupDef_t; +typedef struct EventGroupDef_t * EventGroupHandle_t; + +/* + * The type that holds event bits always matches TickType_t - therefore the + * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1, + * 32 bits if set to 0. + * + * \defgroup EventBits_t EventBits_t + * \ingroup EventGroup + */ +typedef TickType_t EventBits_t; + +/** + * event_groups.h + *
+ * EventGroupHandle_t xEventGroupCreate( void );
+ * 
+ * + * Create a new event group. + * + * Internally, within the FreeRTOS implementation, event groups use a [small] + * block of memory, in which the event group's structure is stored. If an event + * groups is created using xEventGropuCreate() then the required memory is + * automatically dynamically allocated inside the xEventGroupCreate() function. + * (see https://www.FreeRTOS.org/a00111.html). If an event group is created + * using xEventGropuCreateStatic() then the application writer must instead + * provide the memory that will get used by the event group. + * xEventGroupCreateStatic() therefore allows an event group to be created + * without using any dynamic memory allocation. + * + * Although event groups are not related to ticks, for internal implementation + * reasons the number of bits available for use in an event group is dependent + * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If + * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit + * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has + * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store + * event bits within an event group. + * + * @return If the event group was created then a handle to the event group is + * returned. If there was insufficient FreeRTOS heap available to create the + * event group then NULL is returned. See https://www.FreeRTOS.org/a00111.html + * + * Example usage: + *
+ *  // Declare a variable to hold the created event group.
+ *  EventGroupHandle_t xCreatedEventGroup;
+ *
+ *  // Attempt to create the event group.
+ *  xCreatedEventGroup = xEventGroupCreate();
+ *
+ *  // Was the event group created successfully?
+ *  if( xCreatedEventGroup == NULL )
+ *  {
+ *      // The event group was not created because there was insufficient
+ *      // FreeRTOS heap available.
+ *  }
+ *  else
+ *  {
+ *      // The event group was created.
+ *  }
+ * 
+ * \defgroup xEventGroupCreate xEventGroupCreate + * \ingroup EventGroup + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; +#endif + +/** + * event_groups.h + *
+ * EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
+ * 
+ * + * Create a new event group. + * + * Internally, within the FreeRTOS implementation, event groups use a [small] + * block of memory, in which the event group's structure is stored. If an event + * groups is created using xEventGropuCreate() then the required memory is + * automatically dynamically allocated inside the xEventGroupCreate() function. + * (see https://www.FreeRTOS.org/a00111.html). If an event group is created + * using xEventGropuCreateStatic() then the application writer must instead + * provide the memory that will get used by the event group. + * xEventGroupCreateStatic() therefore allows an event group to be created + * without using any dynamic memory allocation. + * + * Although event groups are not related to ticks, for internal implementation + * reasons the number of bits available for use in an event group is dependent + * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If + * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit + * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has + * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store + * event bits within an event group. + * + * @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type + * StaticEventGroup_t, which will be then be used to hold the event group's data + * structures, removing the need for the memory to be allocated dynamically. + * + * @return If the event group was created then a handle to the event group is + * returned. If pxEventGroupBuffer was NULL then NULL is returned. + * + * Example usage: + *
+ *  // StaticEventGroup_t is a publicly accessible structure that has the same
+ *  // size and alignment requirements as the real event group structure.  It is
+ *  // provided as a mechanism for applications to know the size of the event
+ *  // group (which is dependent on the architecture and configuration file
+ *  // settings) without breaking the strict data hiding policy by exposing the
+ *  // real event group internals.  This StaticEventGroup_t variable is passed
+ *  // into the xSemaphoreCreateEventGroupStatic() function and is used to store
+ *  // the event group's data structures
+ *  StaticEventGroup_t xEventGroupBuffer;
+ *
+ *  // Create the event group without dynamically allocating any memory.
+ *  xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
+ * 
+ */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) PRIVILEGED_FUNCTION; +#endif + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupWaitBits(    EventGroupHandle_t xEventGroup,
+ *                                      const EventBits_t uxBitsToWaitFor,
+ *                                      const BaseType_t xClearOnExit,
+ *                                      const BaseType_t xWaitForAllBits,
+ *                                      const TickType_t xTicksToWait );
+ * 
+ * + * [Potentially] block to wait for one or more bits to be set within a + * previously created event group. + * + * This function cannot be called from an interrupt. + * + * @param xEventGroup The event group in which the bits are being tested. The + * event group must have previously been created using a call to + * xEventGroupCreate(). + * + * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test + * inside the event group. For example, to wait for bit 0 and/or bit 2 set + * uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set + * uxBitsToWaitFor to 0x07. Etc. + * + * @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within + * uxBitsToWaitFor that are set within the event group will be cleared before + * xEventGroupWaitBits() returns if the wait condition was met (if the function + * returns for a reason other than a timeout). If xClearOnExit is set to + * pdFALSE then the bits set in the event group are not altered when the call to + * xEventGroupWaitBits() returns. + * + * @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then + * xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor + * are set or the specified block time expires. If xWaitForAllBits is set to + * pdFALSE then xEventGroupWaitBits() will return when any one of the bits set + * in uxBitsToWaitFor is set or the specified block time expires. The block + * time is specified by the xTicksToWait parameter. + * + * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait + * for one/all (depending on the xWaitForAllBits value) of the bits specified by + * uxBitsToWaitFor to become set. + * + * @return The value of the event group at the time either the bits being waited + * for became set, or the block time expired. Test the return value to know + * which bits were set. If xEventGroupWaitBits() returned because its timeout + * expired then not all the bits being waited for will be set. If + * xEventGroupWaitBits() returned because the bits it was waiting for were set + * then the returned value is the event group value before any bits were + * automatically cleared in the case that xClearOnExit parameter was set to + * pdTRUE. + * + * Example usage: + *
+ #define BIT_0 ( 1 << 0 )
+ #define BIT_4 ( 1 << 4 )
+ *
+ * void aFunction( EventGroupHandle_t xEventGroup )
+ * {
+ * EventBits_t uxBits;
+ * const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+ *
+ *      // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
+ *      // the event group.  Clear the bits before exiting.
+ *      uxBits = xEventGroupWaitBits(
+ *                  xEventGroup,    // The event group being tested.
+ *                  BIT_0 | BIT_4,  // The bits within the event group to wait for.
+ *                  pdTRUE,         // BIT_0 and BIT_4 should be cleared before returning.
+ *                  pdFALSE,        // Don't wait for both bits, either bit will do.
+ *                  xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
+ *
+ *      if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ *      {
+ *          // xEventGroupWaitBits() returned because both bits were set.
+ *      }
+ *      else if( ( uxBits & BIT_0 ) != 0 )
+ *      {
+ *          // xEventGroupWaitBits() returned because just BIT_0 was set.
+ *      }
+ *      else if( ( uxBits & BIT_4 ) != 0 )
+ *      {
+ *          // xEventGroupWaitBits() returned because just BIT_4 was set.
+ *      }
+ *      else
+ *      {
+ *          // xEventGroupWaitBits() returned because xTicksToWait ticks passed
+ *          // without either BIT_0 or BIT_4 becoming set.
+ *      }
+ * }
+ * 
+ * \defgroup xEventGroupWaitBits xEventGroupWaitBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
+ * 
+ * + * Clear bits within an event group. This function cannot be called from an + * interrupt. + * + * @param xEventGroup The event group in which the bits are to be cleared. + * + * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear + * in the event group. For example, to clear bit 3 only, set uxBitsToClear to + * 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09. + * + * @return The value of the event group before the specified bits were cleared. + * + * Example usage: + *
+ #define BIT_0 ( 1 << 0 )
+ #define BIT_4 ( 1 << 4 )
+ *
+ * void aFunction( EventGroupHandle_t xEventGroup )
+ * {
+ * EventBits_t uxBits;
+ *
+ *      // Clear bit 0 and bit 4 in xEventGroup.
+ *      uxBits = xEventGroupClearBits(
+ *                              xEventGroup,    // The event group being updated.
+ *                              BIT_0 | BIT_4 );// The bits being cleared.
+ *
+ *      if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ *      {
+ *          // Both bit 0 and bit 4 were set before xEventGroupClearBits() was
+ *          // called.  Both will now be clear (not set).
+ *      }
+ *      else if( ( uxBits & BIT_0 ) != 0 )
+ *      {
+ *          // Bit 0 was set before xEventGroupClearBits() was called.  It will
+ *          // now be clear.
+ *      }
+ *      else if( ( uxBits & BIT_4 ) != 0 )
+ *      {
+ *          // Bit 4 was set before xEventGroupClearBits() was called.  It will
+ *          // now be clear.
+ *      }
+ *      else
+ *      {
+ *          // Neither bit 0 nor bit 4 were set in the first place.
+ *      }
+ * }
+ * 
+ * \defgroup xEventGroupClearBits xEventGroupClearBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+ *  BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
+ * 
+ * + * A version of xEventGroupClearBits() that can be called from an interrupt. + * + * Setting bits in an event group is not a deterministic operation because there + * are an unknown number of tasks that may be waiting for the bit or bits being + * set. FreeRTOS does not allow nondeterministic operations to be performed + * while interrupts are disabled, so protects event groups that are accessed + * from tasks by suspending the scheduler rather than disabling interrupts. As + * a result event groups cannot be accessed directly from an interrupt service + * routine. Therefore xEventGroupClearBitsFromISR() sends a message to the + * timer task to have the clear operation performed in the context of the timer + * task. + * + * @param xEventGroup The event group in which the bits are to be cleared. + * + * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear. + * For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3 + * and bit 0 set uxBitsToClear to 0x09. + * + * @return If the request to execute the function was posted successfully then + * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned + * if the timer service queue was full. + * + * Example usage: + *
+ #define BIT_0 ( 1 << 0 )
+ #define BIT_4 ( 1 << 4 )
+ *
+ * // An event group which it is assumed has already been created by a call to
+ * // xEventGroupCreate().
+ * EventGroupHandle_t xEventGroup;
+ *
+ * void anInterruptHandler( void )
+ * {
+ *      // Clear bit 0 and bit 4 in xEventGroup.
+ *      xResult = xEventGroupClearBitsFromISR(
+ *                          xEventGroup,     // The event group being updated.
+ *                          BIT_0 | BIT_4 ); // The bits being set.
+ *
+ *      if( xResult == pdPASS )
+ *      {
+ *          // The message was posted successfully.
+ *      }
+ * }
+ * 
+ * \defgroup xEventGroupClearBitsFromISR xEventGroupClearBitsFromISR + * \ingroup EventGroup + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; +#else + #define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) \ + xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ) +#endif + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
+ * 
+ * + * Set bits within an event group. + * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() + * is a version that can be called from an interrupt. + * + * Setting bits in an event group will automatically unblock tasks that are + * blocked waiting for the bits. + * + * @param xEventGroup The event group in which the bits are to be set. + * + * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. + * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 + * and bit 0 set uxBitsToSet to 0x09. + * + * @return The value of the event group at the time the call to + * xEventGroupSetBits() returns. There are two reasons why the returned value + * might have the bits specified by the uxBitsToSet parameter cleared. First, + * if setting a bit results in a task that was waiting for the bit leaving the + * blocked state then it is possible the bit will be cleared automatically + * (see the xClearBitOnExit parameter of xEventGroupWaitBits()). Second, any + * unblocked (or otherwise Ready state) task that has a priority above that of + * the task that called xEventGroupSetBits() will execute and may change the + * event group value before the call to xEventGroupSetBits() returns. + * + * Example usage: + *
+ #define BIT_0 ( 1 << 0 )
+ #define BIT_4 ( 1 << 4 )
+ *
+ * void aFunction( EventGroupHandle_t xEventGroup )
+ * {
+ * EventBits_t uxBits;
+ *
+ *      // Set bit 0 and bit 4 in xEventGroup.
+ *      uxBits = xEventGroupSetBits(
+ *                          xEventGroup,    // The event group being updated.
+ *                          BIT_0 | BIT_4 );// The bits being set.
+ *
+ *      if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+ *      {
+ *          // Both bit 0 and bit 4 remained set when the function returned.
+ *      }
+ *      else if( ( uxBits & BIT_0 ) != 0 )
+ *      {
+ *          // Bit 0 remained set when the function returned, but bit 4 was
+ *          // cleared.  It might be that bit 4 was cleared automatically as a
+ *          // task that was waiting for bit 4 was removed from the Blocked
+ *          // state.
+ *      }
+ *      else if( ( uxBits & BIT_4 ) != 0 )
+ *      {
+ *          // Bit 4 remained set when the function returned, but bit 0 was
+ *          // cleared.  It might be that bit 0 was cleared automatically as a
+ *          // task that was waiting for bit 0 was removed from the Blocked
+ *          // state.
+ *      }
+ *      else
+ *      {
+ *          // Neither bit 0 nor bit 4 remained set.  It might be that a task
+ *          // was waiting for both of the bits to be set, and the bits were
+ *          // cleared as the task left the Blocked state.
+ *      }
+ * }
+ * 
+ * \defgroup xEventGroupSetBits xEventGroupSetBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+ *  BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
+ * 
+ * + * A version of xEventGroupSetBits() that can be called from an interrupt. + * + * Setting bits in an event group is not a deterministic operation because there + * are an unknown number of tasks that may be waiting for the bit or bits being + * set. FreeRTOS does not allow nondeterministic operations to be performed in + * interrupts or from critical sections. Therefore xEventGroupSetBitsFromISR() + * sends a message to the timer task to have the set operation performed in the + * context of the timer task - where a scheduler lock is used in place of a + * critical section. + * + * @param xEventGroup The event group in which the bits are to be set. + * + * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. + * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 + * and bit 0 set uxBitsToSet to 0x09. + * + * @param pxHigherPriorityTaskWoken As mentioned above, calling this function + * will result in a message being sent to the timer daemon task. If the + * priority of the timer daemon task is higher than the priority of the + * currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE by + * xEventGroupSetBitsFromISR(), indicating that a context switch should be + * requested before the interrupt exits. For that reason + * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the + * example code below. + * + * @return If the request to execute the function was posted successfully then + * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned + * if the timer service queue was full. + * + * Example usage: + *
+ #define BIT_0 ( 1 << 0 )
+ #define BIT_4 ( 1 << 4 )
+ *
+ * // An event group which it is assumed has already been created by a call to
+ * // xEventGroupCreate().
+ * EventGroupHandle_t xEventGroup;
+ *
+ * void anInterruptHandler( void )
+ * {
+ * BaseType_t xHigherPriorityTaskWoken, xResult;
+ *
+ *      // xHigherPriorityTaskWoken must be initialised to pdFALSE.
+ *      xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *      // Set bit 0 and bit 4 in xEventGroup.
+ *      xResult = xEventGroupSetBitsFromISR(
+ *                          xEventGroup,    // The event group being updated.
+ *                          BIT_0 | BIT_4   // The bits being set.
+ *                          &xHigherPriorityTaskWoken );
+ *
+ *      // Was the message posted successfully?
+ *      if( xResult == pdPASS )
+ *      {
+ *          // If xHigherPriorityTaskWoken is now set to pdTRUE then a context
+ *          // switch should be requested.  The macro used is port specific and
+ *          // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
+ *          // refer to the documentation page for the port being used.
+ *          portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ *      }
+ * }
+ * 
+ * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * \ingroup EventGroup + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#else + #define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) \ + xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ) +#endif + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupSync(    EventGroupHandle_t xEventGroup,
+ *                                  const EventBits_t uxBitsToSet,
+ *                                  const EventBits_t uxBitsToWaitFor,
+ *                                  TickType_t xTicksToWait );
+ * 
+ * + * Atomically set bits within an event group, then wait for a combination of + * bits to be set within the same event group. This functionality is typically + * used to synchronise multiple tasks, where each task has to wait for the other + * tasks to reach a synchronisation point before proceeding. + * + * This function cannot be used from an interrupt. + * + * The function will return before its block time expires if the bits specified + * by the uxBitsToWait parameter are set, or become set within that time. In + * this case all the bits specified by uxBitsToWait will be automatically + * cleared before the function returns. + * + * @param xEventGroup The event group in which the bits are being tested. The + * event group must have previously been created using a call to + * xEventGroupCreate(). + * + * @param uxBitsToSet The bits to set in the event group before determining + * if, and possibly waiting for, all the bits specified by the uxBitsToWait + * parameter are set. + * + * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test + * inside the event group. For example, to wait for bit 0 and bit 2 set + * uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set + * uxBitsToWaitFor to 0x07. Etc. + * + * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait + * for all of the bits specified by uxBitsToWaitFor to become set. + * + * @return The value of the event group at the time either the bits being waited + * for became set, or the block time expired. Test the return value to know + * which bits were set. If xEventGroupSync() returned because its timeout + * expired then not all the bits being waited for will be set. If + * xEventGroupSync() returned because all the bits it was waiting for were + * set then the returned value is the event group value before any bits were + * automatically cleared. + * + * Example usage: + *
+ * // Bits used by the three tasks.
+ #define TASK_0_BIT     ( 1 << 0 )
+ #define TASK_1_BIT     ( 1 << 1 )
+ #define TASK_2_BIT     ( 1 << 2 )
+ *
+ #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
+ *
+ * // Use an event group to synchronise three tasks.  It is assumed this event
+ * // group has already been created elsewhere.
+ * EventGroupHandle_t xEventBits;
+ *
+ * void vTask0( void *pvParameters )
+ * {
+ * EventBits_t uxReturn;
+ * TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+ *
+ *   for( ;; )
+ *   {
+ *      // Perform task functionality here.
+ *
+ *      // Set bit 0 in the event flag to note this task has reached the
+ *      // sync point.  The other two tasks will set the other two bits defined
+ *      // by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
+ *      // point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
+ *      // for this to happen.
+ *      uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
+ *
+ *      if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
+ *      {
+ *          // All three tasks reached the synchronisation point before the call
+ *          // to xEventGroupSync() timed out.
+ *      }
+ *  }
+ * }
+ *
+ * void vTask1( void *pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *      // Perform task functionality here.
+ *
+ *      // Set bit 1 in the event flag to note this task has reached the
+ *      // synchronisation point.  The other two tasks will set the other two
+ *      // bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+ *      // synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+ *      // indefinitely for this to happen.
+ *      xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+ *
+ *      // xEventGroupSync() was called with an indefinite block time, so
+ *      // this task will only reach here if the synchronisation was made by all
+ *      // three tasks, so there is no need to test the return value.
+ *   }
+ * }
+ *
+ * void vTask2( void *pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *      // Perform task functionality here.
+ *
+ *      // Set bit 2 in the event flag to note this task has reached the
+ *      // synchronisation point.  The other two tasks will set the other two
+ *      // bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+ *      // synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+ *      // indefinitely for this to happen.
+ *      xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+ *
+ *      // xEventGroupSync() was called with an indefinite block time, so
+ *      // this task will only reach here if the synchronisation was made by all
+ *      // three tasks, so there is no need to test the return value.
+ *  }
+ * }
+ *
+ * 
+ * \defgroup xEventGroupSync xEventGroupSync + * \ingroup EventGroup + */ +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + const EventBits_t uxBitsToWaitFor, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
+ * 
+ * + * Returns the current value of the bits in an event group. This function + * cannot be used from an interrupt. + * + * @param xEventGroup The event group being queried. + * + * @return The event group bits at the time xEventGroupGetBits() was called. + * + * \defgroup xEventGroupGetBits xEventGroupGetBits + * \ingroup EventGroup + */ +#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) + +/** + * event_groups.h + *
+ *  EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
+ * 
+ * + * A version of xEventGroupGetBits() that can be called from an ISR. + * + * @param xEventGroup The event group being queried. + * + * @return The event group bits at the time xEventGroupGetBitsFromISR() was called. + * + * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR + * \ingroup EventGroup + */ +EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+ *  void xEventGroupDelete( EventGroupHandle_t xEventGroup );
+ * 
+ * + * Delete an event group that was previously created by a call to + * xEventGroupCreate(). Tasks that are blocked on the event group will be + * unblocked and obtain 0 as the event group's value. + * + * @param xEventGroup The event group being deleted. + */ +void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION; + +/* For internal use only. */ +void vEventGroupSetBitsCallback( void * pvEventGroup, + const uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION; +void vEventGroupClearBitsCallback( void * pvEventGroup, + const uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; + + +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxEventGroupGetNumber( void * xEventGroup ) PRIVILEGED_FUNCTION; + void vEventGroupSetNumber( void * xEventGroup, + UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION; +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* EVENT_GROUPS_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/list.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/list.h index 0598a935f3..e2a00750cb 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/list.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/list.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /* @@ -53,13 +52,14 @@ * \ingroup FreeRTOSIntro */ -#ifndef INC_FREERTOS_H - #error FreeRTOS.h must be included before list.h -#endif #ifndef LIST_H #define LIST_H +#ifndef INC_FREERTOS_H + #error "FreeRTOS.h must be included before list.h" +#endif + /* * The list structure members are modified from within interrupts, and therefore * by rights should be declared volatile. However, they are only modified in a @@ -89,47 +89,49 @@ * "#define configLIST_VOLATILE volatile" */ #ifndef configLIST_VOLATILE - #define configLIST_VOLATILE + #define configLIST_VOLATILE #endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */ +/* *INDENT-OFF* */ #ifdef __cplusplus -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /* Macros that can be used to place known values within the list structures, -then check that the known values do not get corrupted during the execution of -the application. These may catch the list data structures being overwritten in -memory. They will not catch data errors caused by incorrect configuration or -use of FreeRTOS.*/ -#if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) - /* Define the macros to do nothing. */ - #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE - #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE - #define listFIRST_LIST_INTEGRITY_CHECK_VALUE - #define listSECOND_LIST_INTEGRITY_CHECK_VALUE - #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) - #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) - #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) - #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) - #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) - #define listTEST_LIST_INTEGRITY( pxList ) -#else - /* Define macros that add new members into the list structures. */ - #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1; - #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2; - #define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1; - #define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2; - - /* Define macros that set the new structure members to known values. */ - #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE - #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE - #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE - #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE - - /* Define macros that will assert if one of the structure members does not - contain its expected value. */ - #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) - #define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) + * then check that the known values do not get corrupted during the execution of + * the application. These may catch the list data structures being overwritten in + * memory. They will not catch data errors caused by incorrect configuration or + * use of FreeRTOS.*/ +#if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) + /* Define the macros to do nothing. */ + #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE + #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE + #define listFIRST_LIST_INTEGRITY_CHECK_VALUE + #define listSECOND_LIST_INTEGRITY_CHECK_VALUE + #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) + #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) + #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) + #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) + #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) + #define listTEST_LIST_INTEGRITY( pxList ) +#else /* if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) */ + /* Define macros that add new members into the list structures. */ + #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1; + #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2; + #define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1; + #define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2; + +/* Define macros that set the new structure members to known values. */ + #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE + #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE + #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE + #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE + +/* Define macros that will assert if one of the structure members does not + * contain its expected value. */ + #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) + #define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) #endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */ @@ -139,22 +141,22 @@ use of FreeRTOS.*/ struct xLIST; struct xLIST_ITEM { - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ - struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ - struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ - void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ - struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ - listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ + listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ }; -typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ +typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ struct xMINI_LIST_ITEM { - listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - configLIST_VOLATILE TickType_t xItemValue; - struct xLIST_ITEM * configLIST_VOLATILE pxNext; - struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; + listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + configLIST_VOLATILE TickType_t xItemValue; + struct xLIST_ITEM * configLIST_VOLATILE pxNext; + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; }; typedef struct xMINI_LIST_ITEM MiniListItem_t; @@ -163,11 +165,11 @@ typedef struct xMINI_LIST_ITEM MiniListItem_t; */ typedef struct xLIST { - listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - volatile UBaseType_t uxNumberOfItems; - ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ - MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ - listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + volatile UBaseType_t uxNumberOfItems; + ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ + MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ + listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ } List_t; /* @@ -177,7 +179,7 @@ typedef struct xLIST * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER * \ingroup LinkedList */ -#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) ) +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) ) /* * Access macro to get the owner of a list item. The owner of a list item @@ -186,7 +188,7 @@ typedef struct xLIST * \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER * \ingroup LinkedList */ -#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) +#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) /* * Access macro to set the value of the list item. In most cases the value is @@ -195,7 +197,7 @@ typedef struct xLIST * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE * \ingroup LinkedList */ -#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) ) +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) ) /* * Access macro to retrieve the value of the list item. The value can @@ -205,7 +207,7 @@ typedef struct xLIST * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE * \ingroup LinkedList */ -#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) /* * Access macro to retrieve the value of the list item at the head of a given @@ -214,7 +216,7 @@ typedef struct xLIST * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE * \ingroup LinkedList */ -#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue ) +#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue ) /* * Return the list item at the head of the list. @@ -222,7 +224,7 @@ typedef struct xLIST * \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY * \ingroup LinkedList */ -#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) +#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) /* * Return the next list item. @@ -230,7 +232,7 @@ typedef struct xLIST * \page listGET_NEXT listGET_NEXT * \ingroup LinkedList */ -#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext ) +#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext ) /* * Return the list item that marks the end of the list @@ -238,7 +240,7 @@ typedef struct xLIST * \page listGET_END_MARKER listGET_END_MARKER * \ingroup LinkedList */ -#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) ) +#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) ) /* * Access macro to determine if a list contains any items. The macro will @@ -247,12 +249,12 @@ typedef struct xLIST * \page listLIST_IS_EMPTY listLIST_IS_EMPTY * \ingroup LinkedList */ -#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) +#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) /* * Access macro to return the number of items in the list. */ -#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) /* * Access function to obtain the owner of the next entry in a list. @@ -274,18 +276,18 @@ typedef struct xLIST * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY * \ingroup LinkedList */ -#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ -{ \ -List_t * const pxConstList = ( pxList ); \ - /* Increment the index to the next item and return the item, ensuring */ \ - /* we don't return the marker used at the end of the list. */ \ - ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ - if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ - { \ - ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ - } \ - ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ -} +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ + { \ + List_t * const pxConstList = ( pxList ); \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ + } /* @@ -304,7 +306,7 @@ List_t * const pxConstList = ( pxList ); \ * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY * \ingroup LinkedList */ -#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner ) +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner ) /* * Check to see if a list item is within a list. The list item maintains a @@ -315,7 +317,7 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item we want to know if is in the list. * @return pdTRUE if the list item is in the list, otherwise pdFALSE. */ -#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) ) +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) ) /* * Return the list a list item is contained within (referenced from). @@ -323,14 +325,14 @@ List_t * const pxConstList = ( pxList ); \ * @param pxListItem The list item being queried. * @return A pointer to the List_t object that references the pxListItem */ -#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) +#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) /* * This provides a crude means of knowing if a list has been initialised, as * pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise() * function. */ -#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY ) +#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY ) /* * Must be called before a list is used! This initialises all the members @@ -366,7 +368,8 @@ void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION; * \page vListInsert vListInsert * \ingroup LinkedList */ -void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; +void vListInsert( List_t * const pxList, + ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Insert a list item into a list. The item will be inserted in a position @@ -387,7 +390,8 @@ void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIV * \page vListInsertEnd vListInsertEnd * \ingroup LinkedList */ -void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; +void vListInsertEnd( List_t * const pxList, + ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; /* * Remove an item from a list. The list item has a pointer to the list that @@ -404,9 +408,10 @@ void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) P */ UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION; +/* *INDENT-OFF* */ #ifdef __cplusplus -} -#endif - + } #endif +/* *INDENT-ON* */ +#endif /* ifndef LIST_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h index b20c09e81e..0fc36702b6 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ @@ -63,15 +62,17 @@ #define FREERTOS_MESSAGE_BUFFER_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include message_buffer.h" + #error "include FreeRTOS.h must appear in source files before include message_buffer.h" #endif /* Message buffers are built onto of stream buffers. */ #include "stream_buffer.h" +/* *INDENT-OFF* */ #if defined( __cplusplus ) -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /** * Type by which message buffers are referenced. For example, a call to @@ -86,9 +87,9 @@ typedef void * MessageBufferHandle_t; /** * message_buffer.h * -
-MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
-
+ *
+ * MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
+ * 
* * Creates a new message buffer using dynamically allocated memory. See * xMessageBufferCreateStatic() for a version that uses statically allocated @@ -112,43 +113,44 @@ MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes ); * buffer. * * Example use: -
-
-void vAFunction( void )
-{
-MessageBufferHandle_t xMessageBuffer;
-const size_t xMessageBufferSizeBytes = 100;
-
-    // Create a message buffer that can hold 100 bytes.  The memory used to hold
-    // both the message buffer structure and the messages themselves is allocated
-    // dynamically.  Each message added to the buffer consumes an additional 4
-    // bytes which are used to hold the lengh of the message.
-    xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
-
-    if( xMessageBuffer == NULL )
-    {
-        // There was not enough heap memory space available to create the
-        // message buffer.
-    }
-    else
-    {
-        // The message buffer was created successfully and can now be used.
-    }
-
-
+ *
+ *
+ * void vAFunction( void )
+ * {
+ * MessageBufferHandle_t xMessageBuffer;
+ * const size_t xMessageBufferSizeBytes = 100;
+ *
+ *  // Create a message buffer that can hold 100 bytes.  The memory used to hold
+ *  // both the message buffer structure and the messages themselves is allocated
+ *  // dynamically.  Each message added to the buffer consumes an additional 4
+ *  // bytes which are used to hold the lengh of the message.
+ *  xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
+ *
+ *  if( xMessageBuffer == NULL )
+ *  {
+ *      // There was not enough heap memory space available to create the
+ *      // message buffer.
+ *  }
+ *  else
+ *  {
+ *      // The message buffer was created successfully and can now be used.
+ *  }
+ *
+ * 
* \defgroup xMessageBufferCreate xMessageBufferCreate * \ingroup MessageBufferManagement */ -#define xMessageBufferCreate( xBufferSizeBytes ) ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE ) +#define xMessageBufferCreate( xBufferSizeBytes ) \ + ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE ) /** * message_buffer.h * -
-MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
-                                                  uint8_t *pucMessageBufferStorageArea,
-                                                  StaticMessageBuffer_t *pxStaticMessageBuffer );
-
+ *
+ * MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
+ *                                                uint8_t *pucMessageBufferStorageArea,
+ *                                                StaticMessageBuffer_t *pxStaticMessageBuffer );
+ * 
* Creates a new message buffer using statically allocated memory. See * xMessageBufferCreate() for a version that uses dynamically allocated memory. * @@ -173,49 +175,50 @@ MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes, * pxStaticmessageBuffer are NULL then NULL is returned. * * Example use: -
-
-// Used to dimension the array used to hold the messages.  The available space
-// will actually be one less than this, so 999.
-#define STORAGE_SIZE_BYTES 1000
-
-// Defines the memory that will actually hold the messages within the message
-// buffer.
-static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
-
-// The variable used to hold the message buffer structure.
-StaticMessageBuffer_t xMessageBufferStruct;
-
-void MyFunction( void )
-{
-MessageBufferHandle_t xMessageBuffer;
-
-    xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ),
-                                                 ucBufferStorage,
-                                                 &xMessageBufferStruct );
-
-    // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
-    // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
-    // reference the created message buffer in other message buffer API calls.
-
-    // Other code that uses the message buffer can go here.
-}
-
-
+ *
+ *
+ * // Used to dimension the array used to hold the messages.  The available space
+ * // will actually be one less than this, so 999.
+ #define STORAGE_SIZE_BYTES 1000
+ *
+ * // Defines the memory that will actually hold the messages within the message
+ * // buffer.
+ * static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+ *
+ * // The variable used to hold the message buffer structure.
+ * StaticMessageBuffer_t xMessageBufferStruct;
+ *
+ * void MyFunction( void )
+ * {
+ * MessageBufferHandle_t xMessageBuffer;
+ *
+ *  xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ),
+ *                                               ucBufferStorage,
+ *                                               &xMessageBufferStruct );
+ *
+ *  // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
+ *  // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
+ *  // reference the created message buffer in other message buffer API calls.
+ *
+ *  // Other code that uses the message buffer can go here.
+ * }
+ *
+ * 
* \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic * \ingroup MessageBufferManagement */ -#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer ) +#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) \ + ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer ) /** * message_buffer.h * -
-size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
-                           const void *pvTxData,
-                           size_t xDataLengthBytes,
-                           TickType_t xTicksToWait );
-
+ * 
+ * size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
+ *                         const void *pvTxData,
+ *                         size_t xDataLengthBytes,
+ *                         TickType_t xTicksToWait );
+ * 
* * Sends a discrete message to the message buffer. The message can be any * length that fits within the buffer's free space, and is copied into the @@ -272,49 +275,50 @@ size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer, * time out then xDataLengthBytes is returned. * * Example use: -
-void vAFunction( MessageBufferHandle_t xMessageBuffer )
-{
-size_t xBytesSent;
-uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
-char *pcStringToSend = "String to send";
-const TickType_t x100ms = pdMS_TO_TICKS( 100 );
-
-    // Send an array to the message buffer, blocking for a maximum of 100ms to
-    // wait for enough space to be available in the message buffer.
-    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
-
-    if( xBytesSent != sizeof( ucArrayToSend ) )
-    {
-        // The call to xMessageBufferSend() times out before there was enough
-        // space in the buffer for the data to be written.
-    }
-
-    // Send the string to the message buffer.  Return immediately if there is
-    // not enough space in the buffer.
-    xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
-
-    if( xBytesSent != strlen( pcStringToSend ) )
-    {
-        // The string could not be added to the message buffer because there was
-        // not enough free space in the buffer.
-    }
-}
-
+ *
+ * void vAFunction( MessageBufferHandle_t xMessageBuffer )
+ * {
+ * size_t xBytesSent;
+ * uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+ * char *pcStringToSend = "String to send";
+ * const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+ *
+ *  // Send an array to the message buffer, blocking for a maximum of 100ms to
+ *  // wait for enough space to be available in the message buffer.
+ *  xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+ *
+ *  if( xBytesSent != sizeof( ucArrayToSend ) )
+ *  {
+ *      // The call to xMessageBufferSend() times out before there was enough
+ *      // space in the buffer for the data to be written.
+ *  }
+ *
+ *  // Send the string to the message buffer.  Return immediately if there is
+ *  // not enough space in the buffer.
+ *  xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+ *
+ *  if( xBytesSent != strlen( pcStringToSend ) )
+ *  {
+ *      // The string could not be added to the message buffer because there was
+ *      // not enough free space in the buffer.
+ *  }
+ * }
+ * 
* \defgroup xMessageBufferSend xMessageBufferSend * \ingroup MessageBufferManagement */ -#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) +#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) \ + xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) /** * message_buffer.h * -
-size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
-                                  const void *pvTxData,
-                                  size_t xDataLengthBytes,
-                                  BaseType_t *pxHigherPriorityTaskWoken );
-
+ * 
+ * size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
+ *                                const void *pvTxData,
+ *                                size_t xDataLengthBytes,
+ *                                BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * Interrupt safe version of the API function that sends a discrete message to * the message buffer. The message can be any length that fits within the @@ -372,53 +376,54 @@ size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer, * then 0 is returned, otherwise xDataLengthBytes is returned. * * Example use: -
-// A message buffer that has already been created.
-MessageBufferHandle_t xMessageBuffer;
-
-void vAnInterruptServiceRoutine( void )
-{
-size_t xBytesSent;
-char *pcStringToSend = "String to send";
-BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
-
-    // Attempt to send the string to the message buffer.
-    xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
-                                            ( void * ) pcStringToSend,
-                                            strlen( pcStringToSend ),
-                                            &xHigherPriorityTaskWoken );
-
-    if( xBytesSent != strlen( pcStringToSend ) )
-    {
-        // The string could not be added to the message buffer because there was
-        // not enough free space in the buffer.
-    }
-
-    // If xHigherPriorityTaskWoken was set to pdTRUE inside
-    // xMessageBufferSendFromISR() then a task that has a priority above the
-    // priority of the currently executing task was unblocked and a context
-    // switch should be performed to ensure the ISR returns to the unblocked
-    // task.  In most FreeRTOS ports this is done by simply passing
-    // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
-    // variables value, and perform the context switch if necessary.  Check the
-    // documentation for the port in use for port specific instructions.
-    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-}
-
+ *
+ * // A message buffer that has already been created.
+ * MessageBufferHandle_t xMessageBuffer;
+ *
+ * void vAnInterruptServiceRoutine( void )
+ * {
+ * size_t xBytesSent;
+ * char *pcStringToSend = "String to send";
+ * BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+ *
+ *  // Attempt to send the string to the message buffer.
+ *  xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
+ *                                          ( void * ) pcStringToSend,
+ *                                          strlen( pcStringToSend ),
+ *                                          &xHigherPriorityTaskWoken );
+ *
+ *  if( xBytesSent != strlen( pcStringToSend ) )
+ *  {
+ *      // The string could not be added to the message buffer because there was
+ *      // not enough free space in the buffer.
+ *  }
+ *
+ *  // If xHigherPriorityTaskWoken was set to pdTRUE inside
+ *  // xMessageBufferSendFromISR() then a task that has a priority above the
+ *  // priority of the currently executing task was unblocked and a context
+ *  // switch should be performed to ensure the ISR returns to the unblocked
+ *  // task.  In most FreeRTOS ports this is done by simply passing
+ *  // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
+ *  // variables value, and perform the context switch if necessary.  Check the
+ *  // documentation for the port in use for port specific instructions.
+ *  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ * }
+ * 
* \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR * \ingroup MessageBufferManagement */ -#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) +#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) \ + xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) /** * message_buffer.h * -
-size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
-                              void *pvRxData,
-                              size_t xBufferLengthBytes,
-                              TickType_t xTicksToWait );
-
+ *
+ * size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
+ *                            void *pvRxData,
+ *                            size_t xBufferLengthBytes,
+ *                            TickType_t xTicksToWait );
+ * 
* * Receives a discrete message from a message buffer. Messages can be of * variable length and are copied out of the buffer. @@ -471,43 +476,44 @@ size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer, * zero is returned. * * Example use: -
-void vAFunction( MessageBuffer_t xMessageBuffer )
-{
-uint8_t ucRxData[ 20 ];
-size_t xReceivedBytes;
-const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
-
-    // Receive the next message from the message buffer.  Wait in the Blocked
-    // state (so not using any CPU processing time) for a maximum of 100ms for
-    // a message to become available.
-    xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
-                                            ( void * ) ucRxData,
-                                            sizeof( ucRxData ),
-                                            xBlockTime );
-
-    if( xReceivedBytes > 0 )
-    {
-        // A ucRxData contains a message that is xReceivedBytes long.  Process
-        // the message here....
-    }
-}
-
+ *
+ * void vAFunction( MessageBuffer_t xMessageBuffer )
+ * {
+ * uint8_t ucRxData[ 20 ];
+ * size_t xReceivedBytes;
+ * const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+ *
+ *  // Receive the next message from the message buffer.  Wait in the Blocked
+ *  // state (so not using any CPU processing time) for a maximum of 100ms for
+ *  // a message to become available.
+ *  xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
+ *                                          ( void * ) ucRxData,
+ *                                          sizeof( ucRxData ),
+ *                                          xBlockTime );
+ *
+ *  if( xReceivedBytes > 0 )
+ *  {
+ *      // A ucRxData contains a message that is xReceivedBytes long.  Process
+ *      // the message here....
+ *  }
+ * }
+ * 
* \defgroup xMessageBufferReceive xMessageBufferReceive * \ingroup MessageBufferManagement */ -#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) +#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) \ + xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) /** * message_buffer.h * -
-size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
-                                     void *pvRxData,
-                                     size_t xBufferLengthBytes,
-                                     BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
+ *                                   void *pvRxData,
+ *                                   size_t xBufferLengthBytes,
+ *                                   BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * An interrupt safe version of the API function that receives a discrete * message from a message buffer. Messages can be of variable length and are @@ -561,50 +567,51 @@ size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer, * any. * * Example use: -
-// A message buffer that has already been created.
-MessageBuffer_t xMessageBuffer;
-
-void vAnInterruptServiceRoutine( void )
-{
-uint8_t ucRxData[ 20 ];
-size_t xReceivedBytes;
-BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
-
-    // Receive the next message from the message buffer.
-    xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
-                                                  ( void * ) ucRxData,
-                                                  sizeof( ucRxData ),
-                                                  &xHigherPriorityTaskWoken );
-
-    if( xReceivedBytes > 0 )
-    {
-        // A ucRxData contains a message that is xReceivedBytes long.  Process
-        // the message here....
-    }
-
-    // If xHigherPriorityTaskWoken was set to pdTRUE inside
-    // xMessageBufferReceiveFromISR() then a task that has a priority above the
-    // priority of the currently executing task was unblocked and a context
-    // switch should be performed to ensure the ISR returns to the unblocked
-    // task.  In most FreeRTOS ports this is done by simply passing
-    // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
-    // variables value, and perform the context switch if necessary.  Check the
-    // documentation for the port in use for port specific instructions.
-    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-}
-
+ *
+ * // A message buffer that has already been created.
+ * MessageBuffer_t xMessageBuffer;
+ *
+ * void vAnInterruptServiceRoutine( void )
+ * {
+ * uint8_t ucRxData[ 20 ];
+ * size_t xReceivedBytes;
+ * BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+ *
+ *  // Receive the next message from the message buffer.
+ *  xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
+ *                                                ( void * ) ucRxData,
+ *                                                sizeof( ucRxData ),
+ *                                                &xHigherPriorityTaskWoken );
+ *
+ *  if( xReceivedBytes > 0 )
+ *  {
+ *      // A ucRxData contains a message that is xReceivedBytes long.  Process
+ *      // the message here....
+ *  }
+ *
+ *  // If xHigherPriorityTaskWoken was set to pdTRUE inside
+ *  // xMessageBufferReceiveFromISR() then a task that has a priority above the
+ *  // priority of the currently executing task was unblocked and a context
+ *  // switch should be performed to ensure the ISR returns to the unblocked
+ *  // task.  In most FreeRTOS ports this is done by simply passing
+ *  // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
+ *  // variables value, and perform the context switch if necessary.  Check the
+ *  // documentation for the port in use for port specific instructions.
+ *  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ * }
+ * 
* \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR * \ingroup MessageBufferManagement */ -#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) +#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) \ + xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) /** * message_buffer.h * -
-void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
-
+ *
+ * void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
+ * 
* * Deletes a message buffer that was previously created using a call to * xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message @@ -617,13 +624,14 @@ void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer ); * @param xMessageBuffer The handle of the message buffer to be deleted. * */ -#define vMessageBufferDelete( xMessageBuffer ) vStreamBufferDelete( ( StreamBufferHandle_t ) xMessageBuffer ) +#define vMessageBufferDelete( xMessageBuffer ) \ + vStreamBufferDelete( ( StreamBufferHandle_t ) xMessageBuffer ) /** * message_buffer.h -
-BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) );
-
+ *
+ * BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) );
+ * 
* * Tests to see if a message buffer is full. A message buffer is full if it * cannot accept any more messages, of any size, until space is made available @@ -634,13 +642,14 @@ BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) ); * @return If the message buffer referenced by xMessageBuffer is full then * pdTRUE is returned. Otherwise pdFALSE is returned. */ -#define xMessageBufferIsFull( xMessageBuffer ) xStreamBufferIsFull( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferIsFull( xMessageBuffer ) \ + xStreamBufferIsFull( ( StreamBufferHandle_t ) xMessageBuffer ) /** * message_buffer.h -
-BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) );
-
+ *
+ * BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) );
+ * 
* * Tests to see if a message buffer is empty (does not contain any messages). * @@ -650,13 +659,14 @@ BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) ); * pdTRUE is returned. Otherwise pdFALSE is returned. * */ -#define xMessageBufferIsEmpty( xMessageBuffer ) xStreamBufferIsEmpty( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferIsEmpty( xMessageBuffer ) \ + xStreamBufferIsEmpty( ( StreamBufferHandle_t ) xMessageBuffer ) /** * message_buffer.h -
-BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
-
+ *
+ * BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
+ * 
* * Resets a message buffer to its initial empty state, discarding any message it * contained. @@ -673,14 +683,15 @@ BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer ); * \defgroup xMessageBufferReset xMessageBufferReset * \ingroup MessageBufferManagement */ -#define xMessageBufferReset( xMessageBuffer ) xStreamBufferReset( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferReset( xMessageBuffer ) \ + xStreamBufferReset( ( StreamBufferHandle_t ) xMessageBuffer ) /** * message_buffer.h -
-size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) );
-
+ *
+ * size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) );
+ * 
* Returns the number of bytes of free space in the message buffer. * * @param xMessageBuffer The handle of the message buffer being queried. @@ -695,14 +706,16 @@ size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) ); * \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable * \ingroup MessageBufferManagement */ -#define xMessageBufferSpaceAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) -#define xMessageBufferSpacesAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) /* Corrects typo in original macro name. */ +#define xMessageBufferSpaceAvailable( xMessageBuffer ) \ + xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) +#define xMessageBufferSpacesAvailable( xMessageBuffer ) \ + xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) /* Corrects typo in original macro name. */ /** * message_buffer.h -
- size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer ) );
- 
+ *
+ * size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer ) );
+ * 
* Returns the length (in bytes) of the next message in a message buffer. * Useful if xMessageBufferReceive() returned 0 because the size of the buffer * passed into xMessageBufferReceive() was too small to hold the next message. @@ -715,14 +728,15 @@ size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) ); * \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes * \ingroup MessageBufferManagement */ -#define xMessageBufferNextLengthBytes( xMessageBuffer ) xStreamBufferNextMessageLengthBytes( ( StreamBufferHandle_t ) xMessageBuffer ) PRIVILEGED_FUNCTION; +#define xMessageBufferNextLengthBytes( xMessageBuffer ) \ + xStreamBufferNextMessageLengthBytes( ( StreamBufferHandle_t ) xMessageBuffer ) PRIVILEGED_FUNCTION; /** * message_buffer.h * -
-BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * For advanced users only. * @@ -754,14 +768,15 @@ BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuff * \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR * \ingroup StreamBufferManagement */ -#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferSendCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) +#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \ + xStreamBufferSendCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) /** * message_buffer.h * -
-BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * For advanced users only. * @@ -794,10 +809,13 @@ BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamB * \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR * \ingroup StreamBufferManagement */ -#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferReceiveCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) +#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \ + xStreamBufferReceiveCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken ) +/* *INDENT-OFF* */ #if defined( __cplusplus ) -} /* extern "C" */ + } /* extern "C" */ #endif +/* *INDENT-ON* */ -#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */ +#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h new file mode 100644 index 0000000000..d95dcd8c4b --- /dev/null +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h @@ -0,0 +1,263 @@ +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* + * When the MPU is used the standard (non MPU) API functions are mapped to + * equivalents that start "MPU_", the prototypes for which are defined in this + * header files. This will cause the application code to call the MPU_ version + * which wraps the non-MPU version with privilege promoting then demoting code, + * so the kernel code always runs will full privileges. + */ + + +#ifndef MPU_PROTOTYPES_H +#define MPU_PROTOTYPES_H + +/* MPU versions of tasks.h API functions. */ +BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint16_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, + const MemoryRegion_t * const pxRegions ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskStartScheduler( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL; +char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetHandle( const char * pcNameToQuery ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL; +TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + uint32_t * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskList( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskGetRunTimeStats( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) FREERTOS_SYSTEM_CALL; +uint32_t MPU_ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskIncrementTick( void ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vTaskMissedYield( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of queue.h API functions. */ +BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, + const void * const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, + StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount, + StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, + const char * pcName ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue, + const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL; +QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL; +QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, + const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, + BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL; +void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, + UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; +uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of timers.h API functions. */ +TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) FREERTOS_SYSTEM_CALL; +TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t * pxTimerBuffer ) FREERTOS_SYSTEM_CALL; +void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetTimerID( TimerHandle_t xTimer, + void * pvNewID ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +void MPU_vTimerSetReloadMode( TimerHandle_t xTimer, + const UBaseType_t uxAutoReload ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerCreateTimerTask( void ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t * const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of event_group.h API functions. */ +EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL; +EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL; +EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + const EventBits_t uxBitsToWaitFor, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL; +UBaseType_t MPU_uxEventGroupGetNumber( void * xEventGroup ) FREERTOS_SYSTEM_CALL; + +/* MPU versions of message/stream_buffer.h API functions. */ +size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL; +BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, + size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer ) FREERTOS_SYSTEM_CALL; +StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) FREERTOS_SYSTEM_CALL; + + + +#endif /* MPU_PROTOTYPES_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h new file mode 100644 index 0000000000..7951b0ac9e --- /dev/null +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h @@ -0,0 +1,186 @@ +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +#ifndef MPU_WRAPPERS_H +#define MPU_WRAPPERS_H + +/* This file redefines API functions to be called through a wrapper macro, but + * only for ports that are using the MPU. */ +#ifdef portUSING_MPU_WRAPPERS +#error MPU + +/* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is + * included from queue.c or task.c to prevent it from having an effect within + * those files. */ +#ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* + * Map standard (non MPU) API functions to equivalents that start + * "MPU_". This will cause the application code to call the MPU_ + * version, which wraps the non-MPU version with privilege promoting + * then demoting code, so the kernel code always runs will full + * privileges. + */ + +/* Map standard tasks.h API functions to the MPU equivalents. */ +#define xTaskCreate MPU_xTaskCreate +#define xTaskCreateStatic MPU_xTaskCreateStatic +#define xTaskCreateRestricted MPU_xTaskCreateRestricted +#define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions +#define vTaskDelete MPU_vTaskDelete +#define vTaskDelay MPU_vTaskDelay +#define vTaskDelayUntil MPU_vTaskDelayUntil +#define xTaskAbortDelay MPU_xTaskAbortDelay +#define uxTaskPriorityGet MPU_uxTaskPriorityGet +#define eTaskGetState MPU_eTaskGetState +#define vTaskGetInfo MPU_vTaskGetInfo +#define vTaskPrioritySet MPU_vTaskPrioritySet +#define vTaskSuspend MPU_vTaskSuspend +#define vTaskResume MPU_vTaskResume +#define vTaskSuspendAll MPU_vTaskSuspendAll +#define xTaskResumeAll MPU_xTaskResumeAll +#define xTaskGetTickCount MPU_xTaskGetTickCount +#define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks +#define pcTaskGetName MPU_pcTaskGetName +#define xTaskGetHandle MPU_xTaskGetHandle +#define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark +#define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2 +#define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag +#define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag +#define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer +#define pvTaskGetThreadLocalStoragePointer MPU_pvTaskGetThreadLocalStoragePointer +#define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook +#define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle +#define uxTaskGetSystemState MPU_uxTaskGetSystemState +#define vTaskList MPU_vTaskList +#define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats +#define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter +#define xTaskGenericNotify MPU_xTaskGenericNotify +#define xTaskGenericNotifyWait MPU_xTaskGenericNotifyWait +#define ulTaskGenericNotifyTake MPU_ulTaskGenericNotifyTake +#define xTaskGenericNotifyStateClear MPU_xTaskGenericNotifyStateClear +#define ulTaskGenericNotifyValueClear MPU_ulTaskGenericNotifyValueClear +#define xTaskCatchUpTicks MPU_xTaskCatchUpTicks + +#define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle +#define vTaskSetTimeOutState MPU_vTaskSetTimeOutState +#define xTaskCheckForTimeOut MPU_xTaskCheckForTimeOut +#define xTaskGetSchedulerState MPU_xTaskGetSchedulerState + +/* Map standard queue.h API functions to the MPU equivalents. */ +#define xQueueGenericSend MPU_xQueueGenericSend +#define xQueueReceive MPU_xQueueReceive +#define xQueuePeek MPU_xQueuePeek +#define xQueueSemaphoreTake MPU_xQueueSemaphoreTake +#define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting +#define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable +#define vQueueDelete MPU_vQueueDelete +#define xQueueCreateMutex MPU_xQueueCreateMutex +#define xQueueCreateMutexStatic MPU_xQueueCreateMutexStatic +#define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore +#define xQueueCreateCountingSemaphoreStatic MPU_xQueueCreateCountingSemaphoreStatic +#define xQueueGetMutexHolder MPU_xQueueGetMutexHolder +#define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive +#define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive +#define xQueueGenericCreate MPU_xQueueGenericCreate +#define xQueueGenericCreateStatic MPU_xQueueGenericCreateStatic +#define xQueueCreateSet MPU_xQueueCreateSet +#define xQueueAddToSet MPU_xQueueAddToSet +#define xQueueRemoveFromSet MPU_xQueueRemoveFromSet +#define xQueueSelectFromSet MPU_xQueueSelectFromSet +#define xQueueGenericReset MPU_xQueueGenericReset + +#if (configQUEUE_REGISTRY_SIZE > 0) +#define vQueueAddToRegistry MPU_vQueueAddToRegistry +#define vQueueUnregisterQueue MPU_vQueueUnregisterQueue +#define pcQueueGetName MPU_pcQueueGetName +#endif + +/* Map standard timer.h API functions to the MPU equivalents. */ +#define xTimerCreate MPU_xTimerCreate +#define xTimerCreateStatic MPU_xTimerCreateStatic +#define pvTimerGetTimerID MPU_pvTimerGetTimerID +#define vTimerSetTimerID MPU_vTimerSetTimerID +#define xTimerIsTimerActive MPU_xTimerIsTimerActive +#define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle +#define xTimerPendFunctionCall MPU_xTimerPendFunctionCall +#define pcTimerGetName MPU_pcTimerGetName +#define vTimerSetReloadMode MPU_vTimerSetReloadMode +#define uxTimerGetReloadMode MPU_uxTimerGetReloadMode +#define xTimerGetPeriod MPU_xTimerGetPeriod +#define xTimerGetExpiryTime MPU_xTimerGetExpiryTime +#define xTimerGenericCommand MPU_xTimerGenericCommand + +/* Map standard event_group.h API functions to the MPU equivalents. */ +#define xEventGroupCreate MPU_xEventGroupCreate +#define xEventGroupCreateStatic MPU_xEventGroupCreateStatic +#define xEventGroupWaitBits MPU_xEventGroupWaitBits +#define xEventGroupClearBits MPU_xEventGroupClearBits +#define xEventGroupSetBits MPU_xEventGroupSetBits +#define xEventGroupSync MPU_xEventGroupSync +#define vEventGroupDelete MPU_vEventGroupDelete + +/* Map standard message/stream_buffer.h API functions to the MPU + * equivalents. */ +#define xStreamBufferSend MPU_xStreamBufferSend +#define xStreamBufferReceive MPU_xStreamBufferReceive +#define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes +#define vStreamBufferDelete MPU_vStreamBufferDelete +#define xStreamBufferIsFull MPU_xStreamBufferIsFull +#define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty +#define xStreamBufferReset MPU_xStreamBufferReset +#define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable +#define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable +#define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel +#define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate +#define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic + +/* Remove the privileged function macro, but keep the PRIVILEGED_DATA + * macro so applications can place data in privileged access sections + * (useful when using statically allocated objects). */ +#define PRIVILEGED_FUNCTION +#define PRIVILEGED_DATA __attribute__((section("privileged_data"))) +#define FREERTOS_SYSTEM_CALL + +#else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +/* Ensure API functions go in the privileged execution section. */ +#define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) +#define PRIVILEGED_DATA __attribute__((section("privileged_data"))) +#define FREERTOS_SYSTEM_CALL __attribute__((section("freertos_system_calls"))) + +#endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +#else /* portUSING_MPU_WRAPPERS */ + +#define PRIVILEGED_FUNCTION +#define PRIVILEGED_DATA +#define FREERTOS_SYSTEM_CALL +#define portUSING_MPU_WRAPPERS 0 + +#endif /* portUSING_MPU_WRAPPERS */ + +#endif /* MPU_WRAPPERS_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h index d5b449a480..7bcea02483 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/portable.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,87 +19,85 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /*----------------------------------------------------------- - * Portable layer API. Each function must be defined for each port. - *----------------------------------------------------------*/ +* Portable layer API. Each function must be defined for each port. +*----------------------------------------------------------*/ #ifndef PORTABLE_H #define PORTABLE_H /* Each FreeRTOS port has a unique portmacro.h header file. Originally a -pre-processor definition was used to ensure the pre-processor found the correct -portmacro.h file for the port being used. That scheme was deprecated in favour -of setting the compiler's include path such that it found the correct -portmacro.h file - removing the need for the constant and allowing the -portmacro.h file to be located anywhere in relation to the port being used. -Purely for reasons of backward compatibility the old method is still valid, but -to make it clear that new projects should not use it, support for the port -specific constants has been moved into the deprecated_definitions.h header -file. */ + * pre-processor definition was used to ensure the pre-processor found the correct + * portmacro.h file for the port being used. That scheme was deprecated in favour + * of setting the compiler's include path such that it found the correct + * portmacro.h file - removing the need for the constant and allowing the + * portmacro.h file to be located anywhere in relation to the port being used. + * Purely for reasons of backward compatibility the old method is still valid, but + * to make it clear that new projects should not use it, support for the port + * specific constants has been moved into the deprecated_definitions.h header + * file. */ +#include "deprecated_definitions.h" /* If portENTER_CRITICAL is not defined then including deprecated_definitions.h -did not result in a portmacro.h header file being included - and it should be -included here. In this case the path to the correct portmacro.h header file -must be set in the compiler's include path. */ + * did not result in a portmacro.h header file being included - and it should be + * included here. In this case the path to the correct portmacro.h header file + * must be set in the compiler's include path. */ #ifndef portENTER_CRITICAL -#include "portmacro.h" + #include "portmacro.h" #endif #if portBYTE_ALIGNMENT == 32 -#define portBYTE_ALIGNMENT_MASK (0x001f) + #define portBYTE_ALIGNMENT_MASK ( 0x001f ) #endif #if portBYTE_ALIGNMENT == 16 -#define portBYTE_ALIGNMENT_MASK (0x000f) + #define portBYTE_ALIGNMENT_MASK ( 0x000f ) #endif #if portBYTE_ALIGNMENT == 8 -#define portBYTE_ALIGNMENT_MASK (0x0007) + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) #endif #if portBYTE_ALIGNMENT == 4 -#define portBYTE_ALIGNMENT_MASK (0x0003) + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) #endif #if portBYTE_ALIGNMENT == 2 -#define portBYTE_ALIGNMENT_MASK (0x0001) + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) #endif #if portBYTE_ALIGNMENT == 1 -#define portBYTE_ALIGNMENT_MASK (0x0000) + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) #endif #ifndef portBYTE_ALIGNMENT_MASK -#error "Invalid portBYTE_ALIGNMENT definition" + #error "Invalid portBYTE_ALIGNMENT definition" #endif #ifndef portNUM_CONFIGURABLE_REGIONS -#define portNUM_CONFIGURABLE_REGIONS 1 + #define portNUM_CONFIGURABLE_REGIONS 1 #endif #ifndef portHAS_STACK_OVERFLOW_CHECKING -#define portHAS_STACK_OVERFLOW_CHECKING 0 + #define portHAS_STACK_OVERFLOW_CHECKING 0 #endif #ifndef portARCH_NAME -#define portARCH_NAME NULL + #define portARCH_NAME NULL #endif +/* *INDENT-OFF* */ #ifdef __cplusplus -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ -#define PRIVILEGED_FUNCTION -#define PRIVILEGED_DATA -#define FREERTOS_SYSTEM_CALL -#define portUSING_MPU_WRAPPERS 0 -// #include "mpu_wrappers.h" +#include "mpu_wrappers.h" /* * Setup the stack of a new task so it is ready to be placed under the @@ -107,36 +105,50 @@ extern "C" { * the order that the port expects to find them. * */ -#if (portUSING_MPU_WRAPPERS == 1) -#if (portHAS_STACK_OVERFLOW_CHECKING == 1) -StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged) PRIVILEGED_FUNCTION; -#else -StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged) PRIVILEGED_FUNCTION; -#endif -#else -#if (portHAS_STACK_OVERFLOW_CHECKING == 1) -StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters) PRIVILEGED_FUNCTION; -#else -StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) PRIVILEGED_FUNCTION; -#endif -#endif +#if ( portUSING_MPU_WRAPPERS == 1 ) + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + StackType_t * pxEndOfStack, + TaskFunction_t pxCode, + void * pvParameters, + BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #else + StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters, + BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #endif +#else /* if ( portUSING_MPU_WRAPPERS == 1 ) */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + StackType_t * pxEndOfStack, + TaskFunction_t pxCode, + void * pvParameters ) PRIVILEGED_FUNCTION; + #else + StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters ) PRIVILEGED_FUNCTION; + #endif +#endif /* if ( portUSING_MPU_WRAPPERS == 1 ) */ /* Used by heap_5.c to define the start address and size of each memory region -that together comprise the total FreeRTOS heap space. */ -typedef struct HeapRegion { - uint8_t *pucStartAddress; - size_t xSizeInBytes; + * that together comprise the total FreeRTOS heap space. */ +typedef struct HeapRegion +{ + uint8_t * pucStartAddress; + size_t xSizeInBytes; } HeapRegion_t; /* Used to pass information about the heap out of vPortGetHeapStats(). */ -typedef struct xHeapStats { - size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */ - size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */ - size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */ - size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */ - size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */ +typedef struct xHeapStats +{ + size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */ + size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */ + size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */ + size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */ } HeapStats_t; /* @@ -150,35 +162,35 @@ typedef struct xHeapStats { * terminated by a HeapRegions_t structure that has a size of 0. The region * with the lowest start address must appear first in the array. */ -void vPortDefineHeapRegions(const HeapRegion_t *const pxHeapRegions) PRIVILEGED_FUNCTION; +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION; /* * Returns a HeapStats_t structure filled with information about the current * heap state. */ -void vPortGetHeapStats(HeapStats_t *pxHeapStats); +void vPortGetHeapStats( HeapStats_t * pxHeapStats ); /* * Map to the memory management routines required for the port. */ -void *pvPortMalloc(size_t xSize) PRIVILEGED_FUNCTION; -void vPortFree(void *pv) PRIVILEGED_FUNCTION; -void vPortInitialiseBlocks(void) PRIVILEGED_FUNCTION; -size_t xPortGetFreeHeapSize(void) PRIVILEGED_FUNCTION; -size_t xPortGetMinimumEverFreeHeapSize(void) PRIVILEGED_FUNCTION; +void * pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void * pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; /* * Setup the hardware ready for the scheduler to take control. This generally * sets up a tick interrupt and sets timers for the correct tick frequency. */ -BaseType_t xPortStartScheduler(void) PRIVILEGED_FUNCTION; +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; /* * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so * the hardware is left in its original condition after the scheduler stops * executing. */ -void vPortEndScheduler(void) PRIVILEGED_FUNCTION; +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; /* * The structures and methods of manipulating the MPU are contained within the @@ -187,13 +199,18 @@ void vPortEndScheduler(void) PRIVILEGED_FUNCTION; * Fills the xMPUSettings structure with the memory region information * contained in xRegions. */ -#if (portUSING_MPU_WRAPPERS == 1) -struct xMEMORY_REGION; -void vPortStoreTaskMPUSettings(xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION *const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth) PRIVILEGED_FUNCTION; +#if ( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings, + const struct xMEMORY_REGION * const xRegions, + StackType_t * pxBottomOfStack, + uint32_t ulStackDepth ) PRIVILEGED_FUNCTION; #endif +/* *INDENT-OFF* */ #ifdef __cplusplus -} + } #endif +/* *INDENT-ON* */ #endif /* PORTABLE_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h index 75d4155b8e..918256564b 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/projdefs.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #ifndef PROJDEFS_H @@ -32,93 +31,90 @@ * Defines the prototype to which task functions must conform. Defined in this * file to ensure the type is known before portable.h is included. */ -typedef void (*TaskFunction_t)( void * ); +typedef void (* TaskFunction_t)( void * ); /* Converts a time in milliseconds to a time in ticks. This macro can be -overridden by a macro of the same name defined in FreeRTOSConfig.h in case the -definition here is not suitable for your application. */ + * overridden by a macro of the same name defined in FreeRTOSConfig.h in case the + * definition here is not suitable for your application. */ #ifndef pdMS_TO_TICKS - #define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) ) + #define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000U ) ) #endif -#define pdFALSE ( ( BaseType_t ) 0 ) -#define pdTRUE ( ( BaseType_t ) 1 ) +#define pdFALSE ( ( BaseType_t ) 0 ) +#define pdTRUE ( ( BaseType_t ) 1 ) -#define pdPASS ( pdTRUE ) -#define pdFAIL ( pdFALSE ) -#define errQUEUE_EMPTY ( ( BaseType_t ) 0 ) -#define errQUEUE_FULL ( ( BaseType_t ) 0 ) +#define pdPASS ( pdTRUE ) +#define pdFAIL ( pdFALSE ) +#define errQUEUE_EMPTY ( ( BaseType_t ) 0 ) +#define errQUEUE_FULL ( ( BaseType_t ) 0 ) /* FreeRTOS error definitions. */ -#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) -#define errQUEUE_BLOCKED ( -4 ) -#define errQUEUE_YIELD ( -5 ) +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) /* Macros used for basic data corruption checks. */ #ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES - #define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0 + #define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0 #endif -#if( configUSE_16_BIT_TICKS == 1 ) - #define pdINTEGRITY_CHECK_VALUE 0x5a5a +#if ( configUSE_16_BIT_TICKS == 1 ) + #define pdINTEGRITY_CHECK_VALUE 0x5a5a #else - #define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5aUL + #define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5aUL #endif /* The following errno values are used by FreeRTOS+ components, not FreeRTOS -itself. */ -#define pdFREERTOS_ERRNO_NONE 0 /* No errors */ -#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */ -#define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */ -#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */ -#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */ -#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */ -#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */ -#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */ -#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */ -#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */ -#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */ -#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */ -#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */ -#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */ -#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */ -#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */ -#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */ -#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */ -#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */ -#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */ -#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */ -#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */ -#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */ -#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */ -#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */ -#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */ -#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */ -#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ -#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */ -#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */ -#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */ -#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */ -#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */ -#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */ -#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */ -#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */ -#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */ -#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */ -#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */ -#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */ + * itself. */ +#define pdFREERTOS_ERRNO_NONE 0 /* No errors */ +#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */ +#define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */ +#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */ +#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */ +#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */ +#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */ +#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */ +#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */ +#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */ +#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */ +#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */ +#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */ +#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */ +#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */ +#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */ +#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */ +#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */ +#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */ +#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */ +#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */ +#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */ +#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */ +#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */ +#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */ +#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */ +#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */ +#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */ +#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */ +#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */ +#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */ +#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */ +#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */ +#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */ +#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */ +#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */ +#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */ +#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */ +#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */ /* The following endian values are used by FreeRTOS+ components, not FreeRTOS -itself. */ -#define pdFREERTOS_LITTLE_ENDIAN 0 -#define pdFREERTOS_BIG_ENDIAN 1 + * itself. */ +#define pdFREERTOS_LITTLE_ENDIAN 0 +#define pdFREERTOS_BIG_ENDIAN 1 /* Re-defining endian values for generic naming. */ -#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN -#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN +#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN +#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN #endif /* PROJDEFS_H */ - - - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h index fb82315286..d2ea2ae3bb 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/queue.h @@ -1,1655 +1,1716 @@ -/* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef QUEUE_H -#define QUEUE_H - -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h" must appear in source files before "include queue.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include "task.h" - -/** - * Type by which queues are referenced. For example, a call to xQueueCreate() - * returns an QueueHandle_t variable that can then be used as a parameter to - * xQueueSend(), xQueueReceive(), etc. - */ -struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ -typedef struct QueueDefinition * QueueHandle_t; - -/** - * Type by which queue sets are referenced. For example, a call to - * xQueueCreateSet() returns an xQueueSet variable that can then be used as a - * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. - */ -typedef struct QueueDefinition * QueueSetHandle_t; - -/** - * Queue sets can contain both queues and semaphores, so the - * QueueSetMemberHandle_t is defined as a type to be used where a parameter or - * return value can be either an QueueHandle_t or an SemaphoreHandle_t. - */ -typedef struct QueueDefinition * QueueSetMemberHandle_t; - -/* For internal use only. */ -#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) -#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) -#define queueOVERWRITE ( ( BaseType_t ) 2 ) - -/* For internal use only. These definitions *must* match those in queue.c. */ -#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) -#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) -#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) -#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) -#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) -#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) - -/** - * queue. h - *
- QueueHandle_t xQueueCreate(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize
-						  );
- * 
- * - * Creates a new queue instance, and returns a handle by which the new queue - * can be referenced. - * - * Internally, within the FreeRTOS implementation, queues use two blocks of - * memory. The first block is used to hold the queue's data structures. The - * second block is used to hold items placed into the queue. If a queue is - * created using xQueueCreate() then both blocks of memory are automatically - * dynamically allocated inside the xQueueCreate() function. (see - * http://www.freertos.org/a00111.html). If a queue is created using - * xQueueCreateStatic() then the application writer must provide the memory that - * will get used by the queue. xQueueCreateStatic() therefore allows a queue to - * be created without using any dynamic memory allocation. - * - * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html - * - * @param uxQueueLength The maximum number of items that the queue can contain. - * - * @param uxItemSize The number of bytes each item in the queue will require. - * Items are queued by copy, not by reference, so this is the number of bytes - * that will be copied for each posted item. Each item on the queue must be - * the same size. - * - * @return If the queue is successfully create then a handle to the newly - * created queue is returned. If the queue cannot be created then 0 is - * returned. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-	if( xQueue1 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue2 == 0 )
-	{
-		// Queue was not created and must not be used.
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreate xQueueCreate - * \ingroup QueueManagement - */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) -#endif - -/** - * queue. h - *
- QueueHandle_t xQueueCreateStatic(
-							  UBaseType_t uxQueueLength,
-							  UBaseType_t uxItemSize,
-							  uint8_t *pucQueueStorageBuffer,
-							  StaticQueue_t *pxQueueBuffer
-						  );
- * 
- * - * Creates a new queue instance, and returns a handle by which the new queue - * can be referenced. - * - * Internally, within the FreeRTOS implementation, queues use two blocks of - * memory. The first block is used to hold the queue's data structures. The - * second block is used to hold items placed into the queue. If a queue is - * created using xQueueCreate() then both blocks of memory are automatically - * dynamically allocated inside the xQueueCreate() function. (see - * http://www.freertos.org/a00111.html). If a queue is created using - * xQueueCreateStatic() then the application writer must provide the memory that - * will get used by the queue. xQueueCreateStatic() therefore allows a queue to - * be created without using any dynamic memory allocation. - * - * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html - * - * @param uxQueueLength The maximum number of items that the queue can contain. - * - * @param uxItemSize The number of bytes each item in the queue will require. - * Items are queued by copy, not by reference, so this is the number of bytes - * that will be copied for each posted item. Each item on the queue must be - * the same size. - * - * @param pucQueueStorageBuffer If uxItemSize is not zero then - * pucQueueStorageBuffer must point to a uint8_t array that is at least large - * enough to hold the maximum number of items that can be in the queue at any - * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is - * zero then pucQueueStorageBuffer can be NULL. - * - * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which - * will be used to hold the queue's data structure. - * - * @return If the queue is created then a handle to the created queue is - * returned. If pxQueueBuffer is NULL then NULL is returned. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- };
-
- #define QUEUE_LENGTH 10
- #define ITEM_SIZE sizeof( uint32_t )
-
- // xQueueBuffer will hold the queue structure.
- StaticQueue_t xQueueBuffer;
-
- // ucQueueStorage will hold the items posted to the queue.  Must be at least
- // [(queue length) * ( queue item size)] bytes long.
- uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
-							ITEM_SIZE	  // The size of each item in the queue
-							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
-							&xQueueBuffer ); // The buffer that will hold the queue structure.
-
-	// The queue is guaranteed to be created successfully as no dynamic memory
-	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueCreateStatic xQueueCreateStatic - * \ingroup QueueManagement - */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) -#endif /* configSUPPORT_STATIC_ALLOCATION */ - -/** - * queue. h - *
- BaseType_t xQueueSendToToFront(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * - * Post an item to the front of a queue. The item is queued by copy, not by - * reference. This function must not be called from an interrupt service - * routine. See xQueueSendFromISR () for an alternative which may be used - * in an ISR. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for space to become available on the queue, should it already - * be full. The call will return immediately if this is set to 0 and the - * queue is full. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * - * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend - * \ingroup QueueManagement - */ -#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) - -/** - * queue. h - *
- BaseType_t xQueueSendToBack(
-								   QueueHandle_t	xQueue,
-								   const void		*pvItemToQueue,
-								   TickType_t		xTicksToWait
-							   );
- * 
- * - * This is a macro that calls xQueueGenericSend(). - * - * Post an item to the back of a queue. The item is queued by copy, not by - * reference. This function must not be called from an interrupt service - * routine. See xQueueSendFromISR () for an alternative which may be used - * in an ISR. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for space to become available on the queue, should it already - * be full. The call will return immediately if this is set to 0 and the queue - * is full. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * - * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend - * \ingroup QueueManagement - */ -#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) - -/** - * queue. h - *
- BaseType_t xQueueSend(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  TickType_t xTicksToWait
-						 );
- * 
- * - * This is a macro that calls xQueueGenericSend(). It is included for - * backward compatibility with versions of FreeRTOS.org that did not - * include the xQueueSendToFront() and xQueueSendToBack() macros. It is - * equivalent to xQueueSendToBack(). - * - * Post an item on a queue. The item is queued by copy, not by reference. - * This function must not be called from an interrupt service routine. - * See xQueueSendFromISR () for an alternative which may be used in an ISR. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for space to become available on the queue, should it already - * be full. The call will return immediately if this is set to 0 and the - * queue is full. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * - * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend - * \ingroup QueueManagement - */ -#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) - -/** - * queue. h - *
- BaseType_t xQueueOverwrite(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue
-						 );
- * 
- * - * Only for use with queues that have a length of one - so the queue is either - * empty or full. - * - * Post an item on a queue. If the queue is already full then overwrite the - * value held in the queue. The item is queued by copy, not by reference. - * - * This function must not be called from an interrupt service routine. - * See xQueueOverwriteFromISR () for an alternative which may be used in an ISR. - * - * @param xQueue The handle of the queue to which the data is being sent. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @return xQueueOverwrite() is a macro that calls xQueueGenericSend(), and - * therefore has the same return values as xQueueSendToFront(). However, pdPASS - * is the only value that can be returned because xQueueOverwrite() will write - * to the queue even when the queue is already full. - * - * Example usage: -
-
- void vFunction( void *pvParameters )
- {
- QueueHandle_t xQueue;
- uint32_t ulVarToSend, ulValReceived;
-
-	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwrite() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-
-	// Write the value 10 to the queue using xQueueOverwrite().
-	ulVarToSend = 10;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// Peeking the queue should now return 10, but leave the value 10 in
-	// the queue.  A block time of zero is used as it is known that the
-	// queue holds a value.
-	ulValReceived = 0;
-	xQueuePeek( xQueue, &ulValReceived, 0 );
-
-	if( ulValReceived != 10 )
-	{
-		// Error unless the item was removed by a different task.
-	}
-
-	// The queue is still full.  Use xQueueOverwrite() to overwrite the
-	// value held in the queue with 100.
-	ulVarToSend = 100;
-	xQueueOverwrite( xQueue, &ulVarToSend );
-
-	// This time read from the queue, leaving the queue empty once more.
-	// A block time of 0 is used again.
-	xQueueReceive( xQueue, &ulValReceived, 0 );
-
-	// The value read should be the last value written, even though the
-	// queue was already full when the value was written.
-	if( ulValReceived != 100 )
-	{
-		// Error!
-	}
-
-	// ...
-}
- 
- * \defgroup xQueueOverwrite xQueueOverwrite - * \ingroup QueueManagement - */ -#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) - - -/** - * queue. h - *
- BaseType_t xQueueGenericSend(
-									QueueHandle_t xQueue,
-									const void * pvItemToQueue,
-									TickType_t xTicksToWait
-									BaseType_t xCopyPosition
-								);
- * 
- * - * It is preferred that the macros xQueueSend(), xQueueSendToFront() and - * xQueueSendToBack() are used in place of calling this function directly. - * - * Post an item on a queue. The item is queued by copy, not by reference. - * This function must not be called from an interrupt service routine. - * See xQueueSendFromISR () for an alternative which may be used in an ISR. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for space to become available on the queue, should it already - * be full. The call will return immediately if this is set to 0 and the - * queue is full. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * - * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the - * item at the back of the queue, or queueSEND_TO_FRONT to place the item - * at the front of the queue (for high priority messages). - * - * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- uint32_t ulVar = 10UL;
-
- void vATask( void *pvParameters )
- {
- QueueHandle_t xQueue1, xQueue2;
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 uint32_t values.
-	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
-
-	// ...
-
-	if( xQueue1 != 0 )
-	{
-		// Send an uint32_t.  Wait for 10 ticks for space to become
-		// available if necessary.
-		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
-		{
-			// Failed to post the message, even after 10 ticks.
-		}
-	}
-
-	if( xQueue2 != 0 )
-	{
-		// Send a pointer to a struct AMessage object.  Don't block if the
-		// queue is already full.
-		pxMessage = & xMessage;
-		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueSend xQueueSend - * \ingroup QueueManagement - */ -BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
- BaseType_t xQueuePeek(
-							 QueueHandle_t xQueue,
-							 void * const pvBuffer,
-							 TickType_t xTicksToWait
-						 );
- * - * Receive an item from a queue without removing the item from the queue. - * The item is received by copy so a buffer of adequate size must be - * provided. The number of bytes copied into the buffer was defined when - * the queue was created. - * - * Successfully received items remain on the queue so will be returned again - * by the next call, or a call to xQueueReceive(). - * - * This macro must not be used in an interrupt service routine. See - * xQueuePeekFromISR() for an alternative that can be called from an interrupt - * service routine. - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for an item to receive should the queue be empty at the time - * of the call. The time is defined in tick periods so the constant - * portTICK_PERIOD_MS should be used to convert to real time if this is required. - * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue - * is empty. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to peek the data from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Peek a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask, but the item still remains on the queue.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueuePeek xQueuePeek - * \ingroup QueueManagement - */ -BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
- BaseType_t xQueuePeekFromISR(
-									QueueHandle_t xQueue,
-									void *pvBuffer,
-								);
- * - * A version of xQueuePeek() that can be called from an interrupt service - * routine (ISR). - * - * Receive an item from a queue without removing the item from the queue. - * The item is received by copy so a buffer of adequate size must be - * provided. The number of bytes copied into the buffer was defined when - * the queue was created. - * - * Successfully received items remain on the queue so will be returned again - * by the next call, or a call to xQueueReceive(). - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * \defgroup xQueuePeekFromISR xQueuePeekFromISR - * \ingroup QueueManagement - */ -BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
- BaseType_t xQueueReceive(
-								 QueueHandle_t xQueue,
-								 void *pvBuffer,
-								 TickType_t xTicksToWait
-							);
- * - * Receive an item from a queue. The item is received by copy so a buffer of - * adequate size must be provided. The number of bytes copied into the buffer - * was defined when the queue was created. - * - * Successfully received items are removed from the queue. - * - * This function must not be used in an interrupt service routine. See - * xQueueReceiveFromISR for an alternative that can. - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @param xTicksToWait The maximum amount of time the task should block - * waiting for an item to receive should the queue be empty at the time - * of the call. xQueueReceive() will return immediately if xTicksToWait - * is zero and the queue is empty. The time is defined in tick periods so the - * constant portTICK_PERIOD_MS should be used to convert to real time if this is - * required. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * Example usage: -
- struct AMessage
- {
-	char ucMessageID;
-	char ucData[ 20 ];
- } xMessage;
-
- QueueHandle_t xQueue;
-
- // Task to create a queue and post a value.
- void vATask( void *pvParameters )
- {
- struct AMessage *pxMessage;
-
-	// Create a queue capable of containing 10 pointers to AMessage structures.
-	// These should be passed by pointer as they contain a lot of data.
-	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Send a pointer to a struct AMessage object.  Don't block if the
-	// queue is already full.
-	pxMessage = & xMessage;
-	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
-
-	// ... Rest of task code.
- }
-
- // Task to receive from the queue.
- void vADifferentTask( void *pvParameters )
- {
- struct AMessage *pxRxedMessage;
-
-	if( xQueue != 0 )
-	{
-		// Receive a message on the created queue.  Block for 10 ticks if a
-		// message is not immediately available.
-		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
-		{
-			// pcRxedMessage now points to the struct AMessage variable posted
-			// by vATask.
-		}
-	}
-
-	// ... Rest of task code.
- }
- 
- * \defgroup xQueueReceive xQueueReceive - * \ingroup QueueManagement - */ -BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
- * - * Return the number of messages stored in a queue. - * - * @param xQueue A handle to the queue being queried. - * - * @return The number of messages available in the queue. - * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting - * \ingroup QueueManagement - */ -UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
- * - * Return the number of free spaces available in a queue. This is equal to the - * number of items that can be sent to the queue before the queue becomes full - * if no items are removed. - * - * @param xQueue A handle to the queue being queried. - * - * @return The number of spaces available in the queue. - * - * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting - * \ingroup QueueManagement - */ -UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
void vQueueDelete( QueueHandle_t xQueue );
- * - * Delete a queue - freeing all the memory allocated for storing of items - * placed on the queue. - * - * @param xQueue A handle to the queue to be deleted. - * - * \defgroup vQueueDelete vQueueDelete - * \ingroup QueueManagement - */ -void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
- BaseType_t xQueueSendToFrontFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * - * This is a macro that calls xQueueGenericSendFromISR(). - * - * Post an item to the front of a queue. It is safe to use this macro from - * within an interrupt service routine. - * - * Items are queued by copy not reference so it is preferable to only - * queue small items, especially when called from an ISR. In most cases - * it would be preferable to store a pointer to the item being queued. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task - * to unblock, and the unblocked task has a priority higher than the currently - * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then - * a context switch should be requested before the interrupt is exited. - * - * @return pdTRUE if the data was successfully sent to the queue, otherwise - * errQUEUE_FULL. - * - * Example usage for buffered IO (where the ISR can obtain more than one value - * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPrioritTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		taskYIELD ();
-	}
- }
- 
- * - * \defgroup xQueueSendFromISR xQueueSendFromISR - * \ingroup QueueManagement - */ -#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) - - -/** - * queue. h - *
- BaseType_t xQueueSendToBackFromISR(
-										 QueueHandle_t xQueue,
-										 const void *pvItemToQueue,
-										 BaseType_t *pxHigherPriorityTaskWoken
-									  );
- 
- * - * This is a macro that calls xQueueGenericSendFromISR(). - * - * Post an item to the back of a queue. It is safe to use this macro from - * within an interrupt service routine. - * - * Items are queued by copy not reference so it is preferable to only - * queue small items, especially when called from an ISR. In most cases - * it would be preferable to store a pointer to the item being queued. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task - * to unblock, and the unblocked task has a priority higher than the currently - * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then - * a context switch should be requested before the interrupt is exited. - * - * @return pdTRUE if the data was successfully sent to the queue, otherwise - * errQUEUE_FULL. - * - * Example usage for buffered IO (where the ISR can obtain more than one value - * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		taskYIELD ();
-	}
- }
- 
- * - * \defgroup xQueueSendFromISR xQueueSendFromISR - * \ingroup QueueManagement - */ -#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) - -/** - * queue. h - *
- BaseType_t xQueueOverwriteFromISR(
-							  QueueHandle_t xQueue,
-							  const void * pvItemToQueue,
-							  BaseType_t *pxHigherPriorityTaskWoken
-						 );
- * 
- * - * A version of xQueueOverwrite() that can be used in an interrupt service - * routine (ISR). - * - * Only for use with queues that can hold a single item - so the queue is either - * empty or full. - * - * Post an item on a queue. If the queue is already full then overwrite the - * value held in the queue. The item is queued by copy, not by reference. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task - * to unblock, and the unblocked task has a priority higher than the currently - * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then - * a context switch should be requested before the interrupt is exited. - * - * @return xQueueOverwriteFromISR() is a macro that calls - * xQueueGenericSendFromISR(), and therefore has the same return values as - * xQueueSendToFrontFromISR(). However, pdPASS is the only value that can be - * returned because xQueueOverwriteFromISR() will write to the queue even when - * the queue is already full. - * - * Example usage: -
-
- QueueHandle_t xQueue;
-
- void vFunction( void *pvParameters )
- {
- 	// Create a queue to hold one uint32_t value.  It is strongly
-	// recommended *not* to use xQueueOverwriteFromISR() on queues that can
-	// contain more than one value, and doing so will trigger an assertion
-	// if configASSERT() is defined.
-	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
-}
-
-void vAnInterruptHandler( void )
-{
-// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
-BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-uint32_t ulVarToSend, ulValReceived;
-
-	// Write the value 10 to the queue using xQueueOverwriteFromISR().
-	ulVarToSend = 10;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// The queue is full, but calling xQueueOverwriteFromISR() again will still
-	// pass because the value held in the queue will be overwritten with the
-	// new value.
-	ulVarToSend = 100;
-	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
-
-	// Reading from the queue will now return 100.
-
-	// ...
-
-	if( xHigherPrioritytaskWoken == pdTRUE )
-	{
-		// Writing to the queue caused a task to unblock and the unblocked task
-		// has a priority higher than or equal to the priority of the currently
-		// executing task (the task this interrupt interrupted).  Perform a context
-		// switch so this interrupt returns directly to the unblocked task.
-		portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
-	}
-}
- 
- * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR - * \ingroup QueueManagement - */ -#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) - -/** - * queue. h - *
- BaseType_t xQueueSendFromISR(
-									 QueueHandle_t xQueue,
-									 const void *pvItemToQueue,
-									 BaseType_t *pxHigherPriorityTaskWoken
-								);
- 
- * - * This is a macro that calls xQueueGenericSendFromISR(). It is included - * for backward compatibility with versions of FreeRTOS.org that did not - * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() - * macros. - * - * Post an item to the back of a queue. It is safe to use this function from - * within an interrupt service routine. - * - * Items are queued by copy not reference so it is preferable to only - * queue small items, especially when called from an ISR. In most cases - * it would be preferable to store a pointer to the item being queued. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task - * to unblock, and the unblocked task has a priority higher than the currently - * running task. If xQueueSendFromISR() sets this value to pdTRUE then - * a context switch should be requested before the interrupt is exited. - * - * @return pdTRUE if the data was successfully sent to the queue, otherwise - * errQUEUE_FULL. - * - * Example usage for buffered IO (where the ISR can obtain more than one value - * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWoken;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWoken = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post the byte.
-		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.
-	if( xHigherPriorityTaskWoken )
-	{
-		// Actual macro used here is port specific.
-		portYIELD_FROM_ISR ();
-	}
- }
- 
- * - * \defgroup xQueueSendFromISR xQueueSendFromISR - * \ingroup QueueManagement - */ -#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) - -/** - * queue. h - *
- BaseType_t xQueueGenericSendFromISR(
-										   QueueHandle_t		xQueue,
-										   const	void	*pvItemToQueue,
-										   BaseType_t	*pxHigherPriorityTaskWoken,
-										   BaseType_t	xCopyPosition
-									   );
- 
- * - * It is preferred that the macros xQueueSendFromISR(), - * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place - * of calling this function directly. xQueueGiveFromISR() is an - * equivalent for use by semaphores that don't actually copy any data. - * - * Post an item on a queue. It is safe to use this function from within an - * interrupt service routine. - * - * Items are queued by copy not reference so it is preferable to only - * queue small items, especially when called from an ISR. In most cases - * it would be preferable to store a pointer to the item being queued. - * - * @param xQueue The handle to the queue on which the item is to be posted. - * - * @param pvItemToQueue A pointer to the item that is to be placed on the - * queue. The size of the items the queue will hold was defined when the - * queue was created, so this many bytes will be copied from pvItemToQueue - * into the queue storage area. - * - * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task - * to unblock, and the unblocked task has a priority higher than the currently - * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then - * a context switch should be requested before the interrupt is exited. - * - * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the - * item at the back of the queue, or queueSEND_TO_FRONT to place the item - * at the front of the queue (for high priority messages). - * - * @return pdTRUE if the data was successfully sent to the queue, otherwise - * errQUEUE_FULL. - * - * Example usage for buffered IO (where the ISR can obtain more than one value - * per call): -
- void vBufferISR( void )
- {
- char cIn;
- BaseType_t xHigherPriorityTaskWokenByPost;
-
-	// We have not woken a task at the start of the ISR.
-	xHigherPriorityTaskWokenByPost = pdFALSE;
-
-	// Loop until the buffer is empty.
-	do
-	{
-		// Obtain a byte from the buffer.
-		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
-
-		// Post each byte.
-		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
-
-	} while( portINPUT_BYTE( BUFFER_COUNT ) );
-
-	// Now the buffer is empty we can switch context if necessary.  Note that the
-	// name of the yield function required is port specific.
-	if( xHigherPriorityTaskWokenByPost )
-	{
-		portYIELD_FROM_ISR();
-	}
- }
- 
- * - * \defgroup xQueueSendFromISR xQueueSendFromISR - * \ingroup QueueManagement - */ -BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; -BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; - -/** - * queue. h - *
- BaseType_t xQueueReceiveFromISR(
-									   QueueHandle_t	xQueue,
-									   void	*pvBuffer,
-									   BaseType_t *pxTaskWoken
-								   );
- * 
- * - * Receive an item from a queue. It is safe to use this function from within an - * interrupt service routine. - * - * @param xQueue The handle to the queue from which the item is to be - * received. - * - * @param pvBuffer Pointer to the buffer into which the received item will - * be copied. - * - * @param pxTaskWoken A task may be blocked waiting for space to become - * available on the queue. If xQueueReceiveFromISR causes such a task to - * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will - * remain unchanged. - * - * @return pdTRUE if an item was successfully received from the queue, - * otherwise pdFALSE. - * - * Example usage: -
-
- QueueHandle_t xQueue;
-
- // Function to create a queue and post some values.
- void vAFunction( void *pvParameters )
- {
- char cValueToPost;
- const TickType_t xTicksToWait = ( TickType_t )0xff;
-
-	// Create a queue capable of containing 10 characters.
-	xQueue = xQueueCreate( 10, sizeof( char ) );
-	if( xQueue == 0 )
-	{
-		// Failed to create the queue.
-	}
-
-	// ...
-
-	// Post some characters that will be used within an ISR.  If the queue
-	// is full then this task will block for xTicksToWait ticks.
-	cValueToPost = 'a';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-	cValueToPost = 'b';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
-
-	// ... keep posting characters ... this task may block when the queue
-	// becomes full.
-
-	cValueToPost = 'c';
-	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
- }
-
- // ISR that outputs all the characters received on the queue.
- void vISR_Routine( void )
- {
- BaseType_t xTaskWokenByReceive = pdFALSE;
- char cRxedChar;
-
-	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
-	{
-		// A character was received.  Output the character now.
-		vOutputCharacter( cRxedChar );
-
-		// If removing the character from the queue woke the task that was
-		// posting onto the queue cTaskWokenByReceive will have been set to
-		// pdTRUE.  No matter how many times this loop iterates only one
-		// task will be woken.
-	}
-
-	if( cTaskWokenByPost != ( char ) pdFALSE;
-	{
-		taskYIELD ();
-	}
- }
- 
- * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR - * \ingroup QueueManagement - */ -BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; - -/* - * Utilities to query queues that are safe to use from an ISR. These utilities - * should be used only from witin an ISR, or within a critical section. - */ -BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; -BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; -UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - -/* - * The functions defined above are for passing data to and from tasks. The - * functions below are the equivalents for passing data to and from - * co-routines. - * - * These functions are called from the co-routine macro implementation and - * should not be called directly from application code. Instead use the macro - * wrappers defined within croutine.h. - */ -BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ); -BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken ); -BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ); -BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ); - -/* - * For internal use only. Use xSemaphoreCreateMutex(), - * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling - * these functions directly. - */ -QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; -QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; -QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; -QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; -BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; -TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; -TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; - -/* - * For internal use only. Use xSemaphoreTakeMutexRecursive() or - * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. - */ -BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; -BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; - -/* - * Reset a queue back to its original empty state. The return value is now - * obsolete and is always set to pdPASS. - */ -#define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) - -/* - * The registry is provided as a means for kernel aware debuggers to - * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add - * a queue, semaphore or mutex handle to the registry if you want the handle - * to be available to a kernel aware debugger. If you are not using a kernel - * aware debugger then this function can be ignored. - * - * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the - * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 - * within FreeRTOSConfig.h for the registry to be available. Its value - * does not effect the number of queues, semaphores and mutexes that can be - * created - just the number that the registry can hold. - * - * @param xQueue The handle of the queue being added to the registry. This - * is the handle returned by a call to xQueueCreate(). Semaphore and mutex - * handles can also be passed in here. - * - * @param pcName The name to be associated with the handle. This is the - * name that the kernel aware debugger will display. The queue registry only - * stores a pointer to the string - so the string must be persistent (global or - * preferably in ROM/Flash), not on the stack. - */ -#if( configQUEUE_REGISTRY_SIZE > 0 ) - void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -#endif - -/* - * The registry is provided as a means for kernel aware debuggers to - * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add - * a queue, semaphore or mutex handle to the registry if you want the handle - * to be available to a kernel aware debugger, and vQueueUnregisterQueue() to - * remove the queue, semaphore or mutex from the register. If you are not using - * a kernel aware debugger then this function can be ignored. - * - * @param xQueue The handle of the queue being removed from the registry. - */ -#if( configQUEUE_REGISTRY_SIZE > 0 ) - void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; -#endif - -/* - * The queue registry is provided as a means for kernel aware debuggers to - * locate queues, semaphores and mutexes. Call pcQueueGetName() to look - * up and return the name of a queue in the queue registry from the queue's - * handle. - * - * @param xQueue The handle of the queue the name of which will be returned. - * @return If the queue is in the registry then a pointer to the name of the - * queue is returned. If the queue is not in the registry then NULL is - * returned. - */ -#if( configQUEUE_REGISTRY_SIZE > 0 ) - const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -#endif - -/* - * Generic version of the function used to creaet a queue using dynamic memory - * allocation. This is called by other functions and macros that create other - * RTOS objects that use the queue structure as their base. - */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; -#endif - -/* - * Generic version of the function used to creaet a queue using dynamic memory - * allocation. This is called by other functions and macros that create other - * RTOS objects that use the queue structure as their base. - */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; -#endif - -/* - * Queue sets provide a mechanism to allow a task to block (pend) on a read - * operation from multiple queues or semaphores simultaneously. - * - * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this - * function. - * - * A queue set must be explicitly created using a call to xQueueCreateSet() - * before it can be used. Once created, standard FreeRTOS queues and semaphores - * can be added to the set using calls to xQueueAddToSet(). - * xQueueSelectFromSet() is then used to determine which, if any, of the queues - * or semaphores contained in the set is in a state where a queue read or - * semaphore take operation would be successful. - * - * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html - * for reasons why queue sets are very rarely needed in practice as there are - * simpler methods of blocking on multiple objects. - * - * Note 2: Blocking on a queue set that contains a mutex will not cause the - * mutex holder to inherit the priority of the blocked task. - * - * Note 3: An additional 4 bytes of RAM is required for each space in a every - * queue added to a queue set. Therefore counting semaphores that have a high - * maximum count value should not be added to a queue set. - * - * Note 4: A receive (in the case of a queue) or take (in the case of a - * semaphore) operation must not be performed on a member of a queue set unless - * a call to xQueueSelectFromSet() has first returned a handle to that set member. - * - * @param uxEventQueueLength Queue sets store events that occur on - * the queues and semaphores contained in the set. uxEventQueueLength specifies - * the maximum number of events that can be queued at once. To be absolutely - * certain that events are not lost uxEventQueueLength should be set to the - * total sum of the length of the queues added to the set, where binary - * semaphores and mutexes have a length of 1, and counting semaphores have a - * length set by their maximum count value. Examples: - * + If a queue set is to hold a queue of length 5, another queue of length 12, - * and a binary semaphore, then uxEventQueueLength should be set to - * (5 + 12 + 1), or 18. - * + If a queue set is to hold three binary semaphores then uxEventQueueLength - * should be set to (1 + 1 + 1 ), or 3. - * + If a queue set is to hold a counting semaphore that has a maximum count of - * 5, and a counting semaphore that has a maximum count of 3, then - * uxEventQueueLength should be set to (5 + 3), or 8. - * - * @return If the queue set is created successfully then a handle to the created - * queue set is returned. Otherwise NULL is returned. - */ -QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; - -/* - * Adds a queue or semaphore to a queue set that was previously created by a - * call to xQueueCreateSet(). - * - * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this - * function. - * - * Note 1: A receive (in the case of a queue) or take (in the case of a - * semaphore) operation must not be performed on a member of a queue set unless - * a call to xQueueSelectFromSet() has first returned a handle to that set member. - * - * @param xQueueOrSemaphore The handle of the queue or semaphore being added to - * the queue set (cast to an QueueSetMemberHandle_t type). - * - * @param xQueueSet The handle of the queue set to which the queue or semaphore - * is being added. - * - * @return If the queue or semaphore was successfully added to the queue set - * then pdPASS is returned. If the queue could not be successfully added to the - * queue set because it is already a member of a different queue set then pdFAIL - * is returned. - */ -BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; - -/* - * Removes a queue or semaphore from a queue set. A queue or semaphore can only - * be removed from a set if the queue or semaphore is empty. - * - * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this - * function. - * - * @param xQueueOrSemaphore The handle of the queue or semaphore being removed - * from the queue set (cast to an QueueSetMemberHandle_t type). - * - * @param xQueueSet The handle of the queue set in which the queue or semaphore - * is included. - * - * @return If the queue or semaphore was successfully removed from the queue set - * then pdPASS is returned. If the queue was not in the queue set, or the - * queue (or semaphore) was not empty, then pdFAIL is returned. - */ -BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; - -/* - * xQueueSelectFromSet() selects from the members of a queue set a queue or - * semaphore that either contains data (in the case of a queue) or is available - * to take (in the case of a semaphore). xQueueSelectFromSet() effectively - * allows a task to block (pend) on a read operation on all the queues and - * semaphores in a queue set simultaneously. - * - * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this - * function. - * - * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html - * for reasons why queue sets are very rarely needed in practice as there are - * simpler methods of blocking on multiple objects. - * - * Note 2: Blocking on a queue set that contains a mutex will not cause the - * mutex holder to inherit the priority of the blocked task. - * - * Note 3: A receive (in the case of a queue) or take (in the case of a - * semaphore) operation must not be performed on a member of a queue set unless - * a call to xQueueSelectFromSet() has first returned a handle to that set member. - * - * @param xQueueSet The queue set on which the task will (potentially) block. - * - * @param xTicksToWait The maximum time, in ticks, that the calling task will - * remain in the Blocked state (with other tasks executing) to wait for a member - * of the queue set to be ready for a successful queue read or semaphore take - * operation. - * - * @return xQueueSelectFromSet() will return the handle of a queue (cast to - * a QueueSetMemberHandle_t type) contained in the queue set that contains data, - * or the handle of a semaphore (cast to a QueueSetMemberHandle_t type) contained - * in the queue set that is available, or NULL if no such queue or semaphore - * exists before before the specified block time expires. - */ -QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/* - * A version of xQueueSelectFromSet() that can be used from an ISR. - */ -QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; - -/* Not public API functions. */ -void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; -BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; -void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; -UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; -uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; - - -#ifdef __cplusplus -} -#endif - -#endif /* QUEUE_H */ - +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + + +#ifndef QUEUE_H +#define QUEUE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include queue.h" +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +#include "task.h" + +/** + * Type by which queues are referenced. For example, a call to xQueueCreate() + * returns an QueueHandle_t variable that can then be used as a parameter to + * xQueueSend(), xQueueReceive(), etc. + */ +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +typedef struct QueueDefinition * QueueHandle_t; + +/** + * Type by which queue sets are referenced. For example, a call to + * xQueueCreateSet() returns an xQueueSet variable that can then be used as a + * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. + */ +typedef struct QueueDefinition * QueueSetHandle_t; + +/** + * Queue sets can contain both queues and semaphores, so the + * QueueSetMemberHandle_t is defined as a type to be used where a parameter or + * return value can be either an QueueHandle_t or an SemaphoreHandle_t. + */ +typedef struct QueueDefinition * QueueSetMemberHandle_t; + +/* For internal use only. */ +#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) +#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) +#define queueOVERWRITE ( ( BaseType_t ) 2 ) + +/* For internal use only. These definitions *must* match those in queue.c. */ +#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) +#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) +#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) +#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) + +/** + * queue. h + *
+ * QueueHandle_t xQueueCreate(
+ *                            UBaseType_t uxQueueLength,
+ *                            UBaseType_t uxItemSize
+ *                        );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * https://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * };
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1, xQueue2;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+ *  if( xQueue1 == 0 )
+ *  {
+ *      // Queue was not created and must not be used.
+ *  }
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *  if( xQueue2 == 0 )
+ *  {
+ *      // Queue was not created and must not be used.
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) +#endif + +/** + * queue. h + *
+ * QueueHandle_t xQueueCreateStatic(
+ *                            UBaseType_t uxQueueLength,
+ *                            UBaseType_t uxItemSize,
+ *                            uint8_t *pucQueueStorageBuffer,
+ *                            StaticQueue_t *pxQueueBuffer
+ *                        );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * https://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @param pucQueueStorageBuffer If uxItemSize is not zero then + * pucQueueStorageBuffer must point to a uint8_t array that is at least large + * enough to hold the maximum number of items that can be in the queue at any + * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is + * zero then pucQueueStorageBuffer can be NULL. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue is created then a handle to the created queue is + * returned. If pxQueueBuffer is NULL then NULL is returned. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * };
+ *
+ #define QUEUE_LENGTH 10
+ #define ITEM_SIZE sizeof( uint32_t )
+ *
+ * // xQueueBuffer will hold the queue structure.
+ * StaticQueue_t xQueueBuffer;
+ *
+ * // ucQueueStorage will hold the items posted to the queue.  Must be at least
+ * // [(queue length) * ( queue item size)] bytes long.
+ * uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
+ *                          ITEM_SIZE     // The size of each item in the queue
+ *                          &( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
+ *                          &xQueueBuffer ); // The buffer that will hold the queue structure.
+ *
+ *  // The queue is guaranteed to be created successfully as no dynamic memory
+ *  // allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueCreateStatic xQueueCreateStatic + * \ingroup QueueManagement + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * queue. h + *
+ * BaseType_t xQueueSendToToFront(
+ *                                 QueueHandle_t    xQueue,
+ *                                 const void       *pvItemToQueue,
+ *                                 TickType_t       xTicksToWait
+ *                             );
+ * 
+ * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * uint32_t ulVar = 10UL;
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1, xQueue2;
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *
+ *  // ...
+ *
+ *  if( xQueue1 != 0 )
+ *  {
+ *      // Send an uint32_t.  Wait for 10 ticks for space to become
+ *      // available if necessary.
+ *      if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+ *      {
+ *          // Failed to post the message, even after 10 ticks.
+ *      }
+ *  }
+ *
+ *  if( xQueue2 != 0 )
+ *  {
+ *      // Send a pointer to a struct AMessage object.  Don't block if the
+ *      // queue is already full.
+ *      pxMessage = & xMessage;
+ *      xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) + +/** + * queue. h + *
+ * BaseType_t xQueueSendToBack(
+ *                                 QueueHandle_t    xQueue,
+ *                                 const void       *pvItemToQueue,
+ *                                 TickType_t       xTicksToWait
+ *                             );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * uint32_t ulVar = 10UL;
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1, xQueue2;
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *
+ *  // ...
+ *
+ *  if( xQueue1 != 0 )
+ *  {
+ *      // Send an uint32_t.  Wait for 10 ticks for space to become
+ *      // available if necessary.
+ *      if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+ *      {
+ *          // Failed to post the message, even after 10 ticks.
+ *      }
+ *  }
+ *
+ *  if( xQueue2 != 0 )
+ *  {
+ *      // Send a pointer to a struct AMessage object.  Don't block if the
+ *      // queue is already full.
+ *      pxMessage = & xMessage;
+ *      xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ * BaseType_t xQueueSend(
+ *                            QueueHandle_t xQueue,
+ *                            const void * pvItemToQueue,
+ *                            TickType_t xTicksToWait
+ *                       );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * uint32_t ulVar = 10UL;
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1, xQueue2;
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *
+ *  // ...
+ *
+ *  if( xQueue1 != 0 )
+ *  {
+ *      // Send an uint32_t.  Wait for 10 ticks for space to become
+ *      // available if necessary.
+ *      if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+ *      {
+ *          // Failed to post the message, even after 10 ticks.
+ *      }
+ *  }
+ *
+ *  if( xQueue2 != 0 )
+ *  {
+ *      // Send a pointer to a struct AMessage object.  Don't block if the
+ *      // queue is already full.
+ *      pxMessage = & xMessage;
+ *      xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ * BaseType_t xQueueOverwrite(
+ *                            QueueHandle_t xQueue,
+ *                            const void * pvItemToQueue
+ *                       );
+ * 
+ * + * Only for use with queues that have a length of one - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * This function must not be called from an interrupt service routine. + * See xQueueOverwriteFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle of the queue to which the data is being sent. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @return xQueueOverwrite() is a macro that calls xQueueGenericSend(), and + * therefore has the same return values as xQueueSendToFront(). However, pdPASS + * is the only value that can be returned because xQueueOverwrite() will write + * to the queue even when the queue is already full. + * + * Example usage: + *
+ *
+ * void vFunction( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue;
+ * uint32_t ulVarToSend, ulValReceived;
+ *
+ *  // Create a queue to hold one uint32_t value.  It is strongly
+ *  // recommended *not* to use xQueueOverwrite() on queues that can
+ *  // contain more than one value, and doing so will trigger an assertion
+ *  // if configASSERT() is defined.
+ *  xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+ *
+ *  // Write the value 10 to the queue using xQueueOverwrite().
+ *  ulVarToSend = 10;
+ *  xQueueOverwrite( xQueue, &ulVarToSend );
+ *
+ *  // Peeking the queue should now return 10, but leave the value 10 in
+ *  // the queue.  A block time of zero is used as it is known that the
+ *  // queue holds a value.
+ *  ulValReceived = 0;
+ *  xQueuePeek( xQueue, &ulValReceived, 0 );
+ *
+ *  if( ulValReceived != 10 )
+ *  {
+ *      // Error unless the item was removed by a different task.
+ *  }
+ *
+ *  // The queue is still full.  Use xQueueOverwrite() to overwrite the
+ *  // value held in the queue with 100.
+ *  ulVarToSend = 100;
+ *  xQueueOverwrite( xQueue, &ulVarToSend );
+ *
+ *  // This time read from the queue, leaving the queue empty once more.
+ *  // A block time of 0 is used again.
+ *  xQueueReceive( xQueue, &ulValReceived, 0 );
+ *
+ *  // The value read should be the last value written, even though the
+ *  // queue was already full when the value was written.
+ *  if( ulValReceived != 100 )
+ *  {
+ *      // Error!
+ *  }
+ *
+ *  // ...
+ * }
+ * 
+ * \defgroup xQueueOverwrite xQueueOverwrite + * \ingroup QueueManagement + */ +#define xQueueOverwrite( xQueue, pvItemToQueue ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) + + +/** + * queue. h + *
+ * BaseType_t xQueueGenericSend(
+ *                                  QueueHandle_t xQueue,
+ *                                  const void * pvItemToQueue,
+ *                                  TickType_t xTicksToWait
+ *                                  BaseType_t xCopyPosition
+ *                              );
+ * 
+ * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * uint32_t ulVar = 10UL;
+ *
+ * void vATask( void *pvParameters )
+ * {
+ * QueueHandle_t xQueue1, xQueue2;
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 uint32_t values.
+ *  xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *
+ *  // ...
+ *
+ *  if( xQueue1 != 0 )
+ *  {
+ *      // Send an uint32_t.  Wait for 10 ticks for space to become
+ *      // available if necessary.
+ *      if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
+ *      {
+ *          // Failed to post the message, even after 10 ticks.
+ *      }
+ *  }
+ *
+ *  if( xQueue2 != 0 )
+ *  {
+ *      // Send a pointer to a struct AMessage object.  Don't block if the
+ *      // queue is already full.
+ *      pxMessage = & xMessage;
+ *      xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, + const void * const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * BaseType_t xQueuePeek(
+ *                           QueueHandle_t xQueue,
+ *                           void * const pvBuffer,
+ *                           TickType_t xTicksToWait
+ *                       );
+ * 
+ * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. See + * xQueuePeekFromISR() for an alternative that can be called from an interrupt + * service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * QueueHandle_t xQueue;
+ *
+ * // Task to create a queue and post a value.
+ * void vATask( void *pvParameters )
+ * {
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *  if( xQueue == 0 )
+ *  {
+ *      // Failed to create the queue.
+ *  }
+ *
+ *  // ...
+ *
+ *  // Send a pointer to a struct AMessage object.  Don't block if the
+ *  // queue is already full.
+ *  pxMessage = & xMessage;
+ *  xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+ *
+ *  // ... Rest of task code.
+ * }
+ *
+ * // Task to peek the data from the queue.
+ * void vADifferentTask( void *pvParameters )
+ * {
+ * struct AMessage *pxRxedMessage;
+ *
+ *  if( xQueue != 0 )
+ *  {
+ *      // Peek a message on the created queue.  Block for 10 ticks if a
+ *      // message is not immediately available.
+ *      if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+ *      {
+ *          // pcRxedMessage now points to the struct AMessage variable posted
+ *          // by vATask, but the item still remains on the queue.
+ *      }
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueuePeek xQueuePeek + * \ingroup QueueManagement + */ +BaseType_t xQueuePeek( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * BaseType_t xQueuePeekFromISR(
+ *                                  QueueHandle_t xQueue,
+ *                                  void *pvBuffer,
+ *                              );
+ * 
+ * + * A version of xQueuePeek() that can be called from an interrupt service + * routine (ISR). + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * \defgroup xQueuePeekFromISR xQueuePeekFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, + void * const pvBuffer ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * BaseType_t xQueueReceive(
+ *                               QueueHandle_t xQueue,
+ *                               void *pvBuffer,
+ *                               TickType_t xTicksToWait
+ *                          );
+ * 
+ * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_PERIOD_MS should be used to convert to real time if this is + * required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: + *
+ * struct AMessage
+ * {
+ *  char ucMessageID;
+ *  char ucData[ 20 ];
+ * } xMessage;
+ *
+ * QueueHandle_t xQueue;
+ *
+ * // Task to create a queue and post a value.
+ * void vATask( void *pvParameters )
+ * {
+ * struct AMessage *pxMessage;
+ *
+ *  // Create a queue capable of containing 10 pointers to AMessage structures.
+ *  // These should be passed by pointer as they contain a lot of data.
+ *  xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+ *  if( xQueue == 0 )
+ *  {
+ *      // Failed to create the queue.
+ *  }
+ *
+ *  // ...
+ *
+ *  // Send a pointer to a struct AMessage object.  Don't block if the
+ *  // queue is already full.
+ *  pxMessage = & xMessage;
+ *  xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+ *
+ *  // ... Rest of task code.
+ * }
+ *
+ * // Task to receive from the queue.
+ * void vADifferentTask( void *pvParameters )
+ * {
+ * struct AMessage *pxRxedMessage;
+ *
+ *  if( xQueue != 0 )
+ *  {
+ *      // Receive a message on the created queue.  Block for 10 ticks if a
+ *      // message is not immediately available.
+ *      if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+ *      {
+ *          // pcRxedMessage now points to the struct AMessage variable posted
+ *          // by vATask.
+ *      }
+ *  }
+ *
+ *  // ... Rest of task code.
+ * }
+ * 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +BaseType_t xQueueReceive( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
+ * 
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
+ * 
+ * + * Return the number of free spaces available in a queue. This is equal to the + * number of items that can be sent to the queue before the queue becomes full + * if no items are removed. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of spaces available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * void vQueueDelete( QueueHandle_t xQueue );
+ * 
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \defgroup vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * BaseType_t xQueueSendToFrontFromISR(
+ *                                       QueueHandle_t xQueue,
+ *                                       const void *pvItemToQueue,
+ *                                       BaseType_t *pxHigherPriorityTaskWoken
+ *                                    );
+ * 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + *
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPrioritTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) + + +/** + * queue. h + *
+ * BaseType_t xQueueSendToBackFromISR(
+ *                                       QueueHandle_t xQueue,
+ *                                       const void *pvItemToQueue,
+ *                                       BaseType_t *pxHigherPriorityTaskWoken
+ *                                    );
+ * 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + *
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ * BaseType_t xQueueOverwriteFromISR(
+ *                            QueueHandle_t xQueue,
+ *                            const void * pvItemToQueue,
+ *                            BaseType_t *pxHigherPriorityTaskWoken
+ *                       );
+ * 
+ * + * A version of xQueueOverwrite() that can be used in an interrupt service + * routine (ISR). + * + * Only for use with queues that can hold a single item - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return xQueueOverwriteFromISR() is a macro that calls + * xQueueGenericSendFromISR(), and therefore has the same return values as + * xQueueSendToFrontFromISR(). However, pdPASS is the only value that can be + * returned because xQueueOverwriteFromISR() will write to the queue even when + * the queue is already full. + * + * Example usage: + *
+ *
+ * QueueHandle_t xQueue;
+ *
+ * void vFunction( void *pvParameters )
+ * {
+ *  // Create a queue to hold one uint32_t value.  It is strongly
+ *  // recommended *not* to use xQueueOverwriteFromISR() on queues that can
+ *  // contain more than one value, and doing so will trigger an assertion
+ *  // if configASSERT() is defined.
+ *  xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+ * }
+ *
+ * void vAnInterruptHandler( void )
+ * {
+ * // xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
+ * BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ * uint32_t ulVarToSend, ulValReceived;
+ *
+ *  // Write the value 10 to the queue using xQueueOverwriteFromISR().
+ *  ulVarToSend = 10;
+ *  xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+ *
+ *  // The queue is full, but calling xQueueOverwriteFromISR() again will still
+ *  // pass because the value held in the queue will be overwritten with the
+ *  // new value.
+ *  ulVarToSend = 100;
+ *  xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+ *
+ *  // Reading from the queue will now return 100.
+ *
+ *  // ...
+ *
+ *  if( xHigherPrioritytaskWoken == pdTRUE )
+ *  {
+ *      // Writing to the queue caused a task to unblock and the unblocked task
+ *      // has a priority higher than or equal to the priority of the currently
+ *      // executing task (the task this interrupt interrupted).  Perform a context
+ *      // switch so this interrupt returns directly to the unblocked task.
+ *      portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
+ *  }
+ * }
+ * 
+ * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR + * \ingroup QueueManagement + */ +#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) + +/** + * queue. h + *
+ * BaseType_t xQueueSendFromISR(
+ *                                   QueueHandle_t xQueue,
+ *                                   const void *pvItemToQueue,
+ *                                   BaseType_t *pxHigherPriorityTaskWoken
+ *                              );
+ * 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + *
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWoken;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWoken = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post the byte.
+ *      xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.
+ *  if( xHigherPriorityTaskWoken )
+ *  {
+ *      // Actual macro used here is port specific.
+ *      portYIELD_FROM_ISR ();
+ *  }
+ * }
+ * 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ * BaseType_t xQueueGenericSendFromISR(
+ *                                         QueueHandle_t    xQueue,
+ *                                         const    void    *pvItemToQueue,
+ *                                         BaseType_t  *pxHigherPriorityTaskWoken,
+ *                                         BaseType_t  xCopyPosition
+ *                                     );
+ * 
+ * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. xQueueGiveFromISR() is an + * equivalent for use by semaphores that don't actually copy any data. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + *
+ * void vBufferISR( void )
+ * {
+ * char cIn;
+ * BaseType_t xHigherPriorityTaskWokenByPost;
+ *
+ *  // We have not woken a task at the start of the ISR.
+ *  xHigherPriorityTaskWokenByPost = pdFALSE;
+ *
+ *  // Loop until the buffer is empty.
+ *  do
+ *  {
+ *      // Obtain a byte from the buffer.
+ *      cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+ *
+ *      // Post each byte.
+ *      xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+ *
+ *  } while( portINPUT_BYTE( BUFFER_COUNT ) );
+ *
+ *  // Now the buffer is empty we can switch context if necessary.  Note that the
+ *  // name of the yield function required is port specific.
+ *  if( xHigherPriorityTaskWokenByPost )
+ *  {
+ *      portYIELD_FROM_ISR();
+ *  }
+ * }
+ * 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, + const void * const pvItemToQueue, + BaseType_t * const pxHigherPriorityTaskWoken, + const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ * BaseType_t xQueueReceiveFromISR(
+ *                                     QueueHandle_t    xQueue,
+ *                                     void             *pvBuffer,
+ *                                     BaseType_t       *pxTaskWoken
+ *                                 );
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: + *
+ *
+ * QueueHandle_t xQueue;
+ *
+ * // Function to create a queue and post some values.
+ * void vAFunction( void *pvParameters )
+ * {
+ * char cValueToPost;
+ * const TickType_t xTicksToWait = ( TickType_t )0xff;
+ *
+ *  // Create a queue capable of containing 10 characters.
+ *  xQueue = xQueueCreate( 10, sizeof( char ) );
+ *  if( xQueue == 0 )
+ *  {
+ *      // Failed to create the queue.
+ *  }
+ *
+ *  // ...
+ *
+ *  // Post some characters that will be used within an ISR.  If the queue
+ *  // is full then this task will block for xTicksToWait ticks.
+ *  cValueToPost = 'a';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ *  cValueToPost = 'b';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ *
+ *  // ... keep posting characters ... this task may block when the queue
+ *  // becomes full.
+ *
+ *  cValueToPost = 'c';
+ *  xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ * }
+ *
+ * // ISR that outputs all the characters received on the queue.
+ * void vISR_Routine( void )
+ * {
+ * BaseType_t xTaskWokenByReceive = pdFALSE;
+ * char cRxedChar;
+ *
+ *  while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+ *  {
+ *      // A character was received.  Output the character now.
+ *      vOutputCharacter( cRxedChar );
+ *
+ *      // If removing the character from the queue woke the task that was
+ *      // posting onto the queue cTaskWokenByReceive will have been set to
+ *      // pdTRUE.  No matter how many times this loop iterates only one
+ *      // task will be woken.
+ *  }
+ *
+ *  if( cTaskWokenByPost != ( char ) pdFALSE;
+ *  {
+ *      taskYIELD ();
+ *  }
+ * }
+ * 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, + void * const pvBuffer, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* + * Utilities to query queues that are safe to use from an ISR. These utilities + * should be used only from witin an ISR, or within a critical section. + */ +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, + const void * pvItemToQueue, + BaseType_t xCoRoutinePreviouslyWoken ); +BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, + void * pvBuffer, + BaseType_t * pxTaskWoken ); +BaseType_t xQueueCRSend( QueueHandle_t xQueue, + const void * pvItemToQueue, + TickType_t xTicksToWait ); +BaseType_t xQueueCRReceive( QueueHandle_t xQueue, + void * pvBuffer, + TickType_t xTicksToWait ); + +/* + * For internal use only. Use xSemaphoreCreateMutex(), + * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling + * these functions directly. + */ +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, + StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount, + StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Use xSemaphoreTakeMutexRecursive() or + * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. + */ +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; + +/* + * Reset a queue back to its original empty state. The return value is now + * obsolete and is always set to pdPASS. + */ +#define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not effect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. The queue registry only + * stores a pointer to the string - so the string must be persistent (global or + * preferably in ROM/Flash), not on the stack. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueAddToRegistry( QueueHandle_t xQueue, + const char * pcQueueName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger, and vQueueUnregisterQueue() to + * remove the queue, semaphore or mutex from the register. If you are not using + * a kernel aware debugger then this function can be ignored. + * + * @param xQueue The handle of the queue being removed from the registry. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * The queue registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call pcQueueGetName() to look + * up and return the name of a queue in the queue registry from the queue's + * handle. + * + * @param xQueue The handle of the queue the name of which will be returned. + * @return If the queue is in the registry then a pointer to the name of the + * queue is returned. If the queue is not in the registry then NULL is + * returned. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + const char * pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + +/* + * Generic version of the function used to create a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to create a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue, + const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Queue sets provide a mechanism to allow a task to block (pend) on a read + * operation from multiple queues or semaphores simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * A queue set must be explicitly created using a call to xQueueCreateSet() + * before it can be used. Once created, standard FreeRTOS queues and semaphores + * can be added to the set using calls to xQueueAddToSet(). + * xQueueSelectFromSet() is then used to determine which, if any, of the queues + * or semaphores contained in the set is in a state where a queue read or + * semaphore take operation would be successful. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: An additional 4 bytes of RAM is required for each space in a every + * queue added to a queue set. Therefore counting semaphores that have a high + * maximum count value should not be added to a queue set. + * + * Note 4: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param uxEventQueueLength Queue sets store events that occur on + * the queues and semaphores contained in the set. uxEventQueueLength specifies + * the maximum number of events that can be queued at once. To be absolutely + * certain that events are not lost uxEventQueueLength should be set to the + * total sum of the length of the queues added to the set, where binary + * semaphores and mutexes have a length of 1, and counting semaphores have a + * length set by their maximum count value. Examples: + * + If a queue set is to hold a queue of length 5, another queue of length 12, + * and a binary semaphore, then uxEventQueueLength should be set to + * (5 + 12 + 1), or 18. + * + If a queue set is to hold three binary semaphores then uxEventQueueLength + * should be set to (1 + 1 + 1 ), or 3. + * + If a queue set is to hold a counting semaphore that has a maximum count of + * 5, and a counting semaphore that has a maximum count of 3, then + * uxEventQueueLength should be set to (5 + 3), or 8. + * + * @return If the queue set is created successfully then a handle to the created + * queue set is returned. Otherwise NULL is returned. + */ +QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; + +/* + * Adds a queue or semaphore to a queue set that was previously created by a + * call to xQueueCreateSet(). + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being added to + * the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set to which the queue or semaphore + * is being added. + * + * @return If the queue or semaphore was successfully added to the queue set + * then pdPASS is returned. If the queue could not be successfully added to the + * queue set because it is already a member of a different queue set then pdFAIL + * is returned. + */ +BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * Removes a queue or semaphore from a queue set. A queue or semaphore can only + * be removed from a set if the queue or semaphore is empty. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being removed + * from the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set in which the queue or semaphore + * is included. + * + * @return If the queue or semaphore was successfully removed from the queue set + * then pdPASS is returned. If the queue was not in the queue set, or the + * queue (or semaphore) was not empty, then pdFAIL is returned. + */ +BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * xQueueSelectFromSet() selects from the members of a queue set a queue or + * semaphore that either contains data (in the case of a queue) or is available + * to take (in the case of a semaphore). xQueueSelectFromSet() effectively + * allows a task to block (pend) on a read operation on all the queues and + * semaphores in a queue set simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueSet The queue set on which the task will (potentially) block. + * + * @param xTicksToWait The maximum time, in ticks, that the calling task will + * remain in the Blocked state (with other tasks executing) to wait for a member + * of the queue set to be ready for a successful queue read or semaphore take + * operation. + * + * @return xQueueSelectFromSet() will return the handle of a queue (cast to + * a QueueSetMemberHandle_t type) contained in the queue set that contains data, + * or the handle of a semaphore (cast to a QueueSetMemberHandle_t type) contained + * in the queue set that is available, or NULL if no such queue or semaphore + * exists before before the specified block time expires. + */ +QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * A version of xQueueSelectFromSet() that can be used from an ISR. + */ +QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* Not public API functions. */ +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, + BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; +void vQueueSetQueueNumber( QueueHandle_t xQueue, + UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* QUEUE_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h index ff21a392b6..fa82554e21 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/semphr.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,35 +19,36 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #ifndef SEMAPHORE_H #define SEMAPHORE_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h" must appear in source files before "include semphr.h" + #error "include FreeRTOS.h" must appear in source files before "include semphr.h" #endif #include "queue.h" typedef QueueHandle_t SemaphoreHandle_t; -#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( uint8_t ) 1U ) -#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) -#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( uint8_t ) 1U ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) +#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) /** * semphr. h - *
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
+ *
+ * vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore );
+ * 
* * In many usage scenarios it is faster and more memory efficient to use a * direct to task notification in place of a binary semaphore! - * http://www.freertos.org/RTOS-task-notifications.html + * https://www.FreeRTOS.org/RTOS-task-notifications.html * * This old vSemaphoreCreateBinary() macro is now deprecated in favour of the * xSemaphoreCreateBinary() function. Note that binary semaphores created using @@ -71,52 +72,54 @@ typedef QueueHandle_t SemaphoreHandle_t; * @param xSemaphore Handle to the created semaphore. Should be of type SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
-    // This is a macro so pass the variable in directly.
-    vSemaphoreCreateBinary( xSemaphore );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+ *  // This is a macro so pass the variable in directly.
+ *  vSemaphoreCreateBinary( xSemaphore );
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // The semaphore was created successfully.
+ *      // The semaphore can now be used.
+ *  }
+ * }
+ * 
* \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary * \ingroup Semaphores */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - #define vSemaphoreCreateBinary( xSemaphore ) \ - { \ - ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ - if( ( xSemaphore ) != NULL ) \ - { \ - ( void ) xSemaphoreGive( ( xSemaphore ) ); \ - } \ - } +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define vSemaphoreCreateBinary( xSemaphore ) \ + { \ + ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + ( void ) xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } #endif /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateBinary( void );
+ * 
* * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * * In many usage scenarios it is faster and more memory efficient to use a * direct to task notification in place of a binary semaphore! - * http://www.freertos.org/RTOS-task-notifications.html + * https://www.FreeRTOS.org/RTOS-task-notifications.html * * Internally, within the FreeRTOS implementation, binary semaphores use a block * of memory, in which the semaphore structure is stored. If a binary semaphore * is created using xSemaphoreCreateBinary() then the required memory is * automatically dynamically allocated inside the xSemaphoreCreateBinary() - * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * function. (see https://www.FreeRTOS.org/a00111.html). If a binary semaphore * is created using xSemaphoreCreateBinaryStatic() then the application writer * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a * binary semaphore to be created without using any dynamic memory allocation. @@ -139,45 +142,47 @@ typedef QueueHandle_t SemaphoreHandle_t; * hold the semaphore's data structures could not be allocated. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateBinary();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
+ *  // This is a macro so pass the variable in directly.
+ *  xSemaphore = xSemaphoreCreateBinary();
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // The semaphore was created successfully.
+ *      // The semaphore can now be used.
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreCreateBinary xSemaphoreCreateBinary * \ingroup Semaphores */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) #endif /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );
+ * 
* * Creates a new binary semaphore instance, and returns a handle by which the * new semaphore can be referenced. * * NOTE: In many usage scenarios it is faster and more memory efficient to use a * direct to task notification in place of a binary semaphore! - * http://www.freertos.org/RTOS-task-notifications.html + * https://www.FreeRTOS.org/RTOS-task-notifications.html * * Internally, within the FreeRTOS implementation, binary semaphores use a block * of memory, in which the semaphore structure is stored. If a binary semaphore * is created using xSemaphoreCreateBinary() then the required memory is * automatically dynamically allocated inside the xSemaphoreCreateBinary() - * function. (see http://www.freertos.org/a00111.html). If a binary semaphore + * function. (see https://www.FreeRTOS.org/a00111.html). If a binary semaphore * is created using xSemaphoreCreateBinaryStatic() then the application writer * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a * binary semaphore to be created without using any dynamic memory allocation. @@ -197,36 +202,38 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. If pxSemaphoreBuffer is NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
-    // The semaphore's data structures will be placed in the xSemaphoreBuffer
-    // variable, the address of which is passed into the function.  The
-    // function's parameter is not NULL, so the function will not attempt any
-    // dynamic memory allocation, and therefore the function will not return
-    // return NULL.
-    xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
-
-    // Rest of task code goes here.
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore = NULL;
+ * StaticSemaphore_t xSemaphoreBuffer;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Semaphore cannot be used before a call to xSemaphoreCreateBinary().
+ *  // The semaphore's data structures will be placed in the xSemaphoreBuffer
+ *  // variable, the address of which is passed into the function.  The
+ *  // function's parameter is not NULL, so the function will not attempt any
+ *  // dynamic memory allocation, and therefore the function will not return
+ *  // return NULL.
+ *  xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer );
+ *
+ *  // Rest of task code goes here.
+ * }
+ * 
* \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic * \ingroup Semaphores */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, queueQUEUE_TYPE_BINARY_SEMAPHORE ) #endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h - *
xSemaphoreTake(
+ * 
+ * xSemaphoreTake(
  *                   SemaphoreHandle_t xSemaphore,
  *                   TickType_t xBlockTime
- *               )
+ * ); + *
* * Macro to obtain a semaphore. The semaphore must have previously been * created with a call to xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or @@ -245,55 +252,57 @@ typedef QueueHandle_t SemaphoreHandle_t; * if xBlockTime expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- // A task that creates a semaphore.
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    xSemaphore = xSemaphoreCreateBinary();
- }
-
- // A task that uses the semaphore.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xSemaphore != NULL )
-    {
-        // See if we can obtain the semaphore.  If the semaphore is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the semaphore and can now access the
-            // shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource.  Release the
-            // semaphore.
-            xSemaphoreGive( xSemaphore );
-        }
-        else
-        {
-            // We could not obtain the semaphore and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ * // A task that creates a semaphore.
+ * void vATask( void * pvParameters )
+ * {
+ *  // Create the semaphore to guard a shared resource.
+ *  xSemaphore = xSemaphoreCreateBinary();
+ * }
+ *
+ * // A task that uses the semaphore.
+ * void vAnotherTask( void * pvParameters )
+ * {
+ *  // ... Do other things.
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // See if we can obtain the semaphore.  If the semaphore is not available
+ *      // wait 10 ticks to see if it becomes free.
+ *      if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
+ *      {
+ *          // We were able to obtain the semaphore and can now access the
+ *          // shared resource.
+ *
+ *          // ...
+ *
+ *          // We have finished accessing the shared resource.  Release the
+ *          // semaphore.
+ *          xSemaphoreGive( xSemaphore );
+ *      }
+ *      else
+ *      {
+ *          // We could not obtain the semaphore and can therefore not access
+ *          // the shared resource safely.
+ *      }
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreTake xSemaphoreTake * \ingroup Semaphores */ -#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) ) +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) ) /** * semphr. h + *
  * xSemaphoreTakeRecursive(
  *                          SemaphoreHandle_t xMutex,
  *                          TickType_t xBlockTime
- *                        )
+ *                        );
+ * 
* * Macro to recursively obtain, or 'take', a mutex type semaphore. * The mutex must have previously been created using a call to @@ -324,68 +333,70 @@ typedef QueueHandle_t SemaphoreHandle_t; * expired without the semaphore becoming available. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-            // xSemaphoreTakeRecursive() are made on the same mutex.  In real
-            // code these would not be just sequential calls as this would make
-            // no sense.  Instead the calls are likely to be buried inside
-            // a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-            // available to another task until it has also been given back
-            // three times.  Again it is unlikely that real code would have
-            // these calls sequentially, but instead buried in a more complex
-            // call structure.  This is just for illustrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-            xSemaphoreGiveRecursive( xMutex );
-            xSemaphoreGiveRecursive( xMutex );
-
-            // Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xMutex = NULL;
+ *
+ * // A task that creates a mutex.
+ * void vATask( void * pvParameters )
+ * {
+ *  // Create the mutex to guard a shared resource.
+ *  xMutex = xSemaphoreCreateRecursiveMutex();
+ * }
+ *
+ * // A task that uses the mutex.
+ * void vAnotherTask( void * pvParameters )
+ * {
+ *  // ... Do other things.
+ *
+ *  if( xMutex != NULL )
+ *  {
+ *      // See if we can obtain the mutex.  If the mutex is not available
+ *      // wait 10 ticks to see if it becomes free.
+ *      if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
+ *      {
+ *          // We were able to obtain the mutex and can now access the
+ *          // shared resource.
+ *
+ *          // ...
+ *          // For some reason due to the nature of the code further calls to
+ *          // xSemaphoreTakeRecursive() are made on the same mutex.  In real
+ *          // code these would not be just sequential calls as this would make
+ *          // no sense.  Instead the calls are likely to be buried inside
+ *          // a more complex call structure.
+ *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+ *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+ *
+ *          // The mutex has now been 'taken' three times, so will not be
+ *          // available to another task until it has also been given back
+ *          // three times.  Again it is unlikely that real code would have
+ *          // these calls sequentially, but instead buried in a more complex
+ *          // call structure.  This is just for illustrative purposes.
+ *          xSemaphoreGiveRecursive( xMutex );
+ *          xSemaphoreGiveRecursive( xMutex );
+ *          xSemaphoreGiveRecursive( xMutex );
+ *
+ *          // Now the mutex can be taken by other tasks.
+ *      }
+ *      else
+ *      {
+ *          // We could not obtain the mutex and can therefore not access
+ *          // the shared resource safely.
+ *      }
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive * \ingroup Semaphores */ -#if( configUSE_RECURSIVE_MUTEXES == 1 ) - #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) #endif /** * semphr. h - *
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
+ *
+ * xSemaphoreGive( SemaphoreHandle_t xSemaphore );
+ * 
* * Macro to release a semaphore. The semaphore must have previously been * created with a call to xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or @@ -406,49 +417,51 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore was not first obtained correctly. * * Example usage: -
- SemaphoreHandle_t xSemaphore = NULL;
-
- void vATask( void * pvParameters )
- {
-    // Create the semaphore to guard a shared resource.
-    xSemaphore = vSemaphoreCreateBinary();
-
-    if( xSemaphore != NULL )
-    {
-        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-        {
-            // We would expect this call to fail because we cannot give
-            // a semaphore without first "taking" it!
-        }
-
-        // Obtain the semaphore - don't block if the semaphore is not
-        // immediately available.
-        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
-        {
-            // We now have the semaphore and can access the shared resource.
-
-            // ...
-
-            // We have finished accessing the shared resource so can free the
-            // semaphore.
-            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
-            {
-                // We would not expect this call to fail because we must have
-                // obtained the semaphore to get here.
-            }
-        }
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Create the semaphore to guard a shared resource.
+ *  xSemaphore = vSemaphoreCreateBinary();
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+ *      {
+ *          // We would expect this call to fail because we cannot give
+ *          // a semaphore without first "taking" it!
+ *      }
+ *
+ *      // Obtain the semaphore - don't block if the semaphore is not
+ *      // immediately available.
+ *      if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
+ *      {
+ *          // We now have the semaphore and can access the shared resource.
+ *
+ *          // ...
+ *
+ *          // We have finished accessing the shared resource so can free the
+ *          // semaphore.
+ *          if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+ *          {
+ *              // We would not expect this call to fail because we must have
+ *              // obtained the semaphore to get here.
+ *          }
+ *      }
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreGive xSemaphoreGive * \ingroup Semaphores */ -#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) +#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) /** * semphr. h - *
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
+ *
+ * xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex );
+ * 
* * Macro to recursively release, or 'give', a mutex type semaphore. * The mutex must have previously been created using a call to @@ -472,73 +485,74 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was given. * * Example usage: -
- SemaphoreHandle_t xMutex = NULL;
-
- // A task that creates a mutex.
- void vATask( void * pvParameters )
- {
-    // Create the mutex to guard a shared resource.
-    xMutex = xSemaphoreCreateRecursiveMutex();
- }
-
- // A task that uses the mutex.
- void vAnotherTask( void * pvParameters )
- {
-    // ... Do other things.
-
-    if( xMutex != NULL )
-    {
-        // See if we can obtain the mutex.  If the mutex is not available
-        // wait 10 ticks to see if it becomes free.
-        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
-        {
-            // We were able to obtain the mutex and can now access the
-            // shared resource.
-
-            // ...
-            // For some reason due to the nature of the code further calls to
-			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
-			// code these would not be just sequential calls as this would make
-			// no sense.  Instead the calls are likely to be buried inside
-			// a more complex call structure.
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
-
-            // The mutex has now been 'taken' three times, so will not be
-			// available to another task until it has also been given back
-			// three times.  Again it is unlikely that real code would have
-			// these calls sequentially, it would be more likely that the calls
-			// to xSemaphoreGiveRecursive() would be called as a call stack
-			// unwound.  This is just for demonstrative purposes.
-            xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-			xSemaphoreGiveRecursive( xMutex );
-
-			// Now the mutex can be taken by other tasks.
-        }
-        else
-        {
-            // We could not obtain the mutex and can therefore not access
-            // the shared resource safely.
-        }
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xMutex = NULL;
+ *
+ * // A task that creates a mutex.
+ * void vATask( void * pvParameters )
+ * {
+ *  // Create the mutex to guard a shared resource.
+ *  xMutex = xSemaphoreCreateRecursiveMutex();
+ * }
+ *
+ * // A task that uses the mutex.
+ * void vAnotherTask( void * pvParameters )
+ * {
+ *  // ... Do other things.
+ *
+ *  if( xMutex != NULL )
+ *  {
+ *      // See if we can obtain the mutex.  If the mutex is not available
+ *      // wait 10 ticks to see if it becomes free.
+ *      if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
+ *      {
+ *          // We were able to obtain the mutex and can now access the
+ *          // shared resource.
+ *
+ *          // ...
+ *          // For some reason due to the nature of the code further calls to
+ *          // xSemaphoreTakeRecursive() are made on the same mutex.  In real
+ *          // code these would not be just sequential calls as this would make
+ *          // no sense.  Instead the calls are likely to be buried inside
+ *          // a more complex call structure.
+ *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+ *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+ *
+ *          // The mutex has now been 'taken' three times, so will not be
+ *          // available to another task until it has also been given back
+ *          // three times.  Again it is unlikely that real code would have
+ *          // these calls sequentially, it would be more likely that the calls
+ *          // to xSemaphoreGiveRecursive() would be called as a call stack
+ *          // unwound.  This is just for demonstrative purposes.
+ *          xSemaphoreGiveRecursive( xMutex );
+ *          xSemaphoreGiveRecursive( xMutex );
+ *          xSemaphoreGiveRecursive( xMutex );
+ *
+ *          // Now the mutex can be taken by other tasks.
+ *      }
+ *      else
+ *      {
+ *          // We could not obtain the mutex and can therefore not access
+ *          // the shared resource safely.
+ *      }
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive * \ingroup Semaphores */ -#if( configUSE_RECURSIVE_MUTEXES == 1 ) - #define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + #define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) #endif /** * semphr. h *
- xSemaphoreGiveFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
+ * xSemaphoreGiveFromISR( + * SemaphoreHandle_t xSemaphore, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + *
* * Macro to release a semaphore. The semaphore must have previously been * created with a call to xSemaphoreCreateBinary() or xSemaphoreCreateCounting(). @@ -560,76 +574,77 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. * * Example usage: -
+ * 
  \#define LONG_TIME 0xffff
- \#define TICKS_TO_WAIT	10
- SemaphoreHandle_t xSemaphore = NULL;
-
- // Repetitive task.
- void vATask( void * pvParameters )
- {
-    for( ;; )
-    {
-        // We want this task to run every 10 ticks of a timer.  The semaphore
-        // was created before this task was started.
-
-        // Block waiting for the semaphore to become available.
-        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
-        {
-            // It is time to execute.
-
-            // ...
-
-            // We have finished our task.  Return to the top of the loop where
-            // we will block on the semaphore until it is time to execute
-            // again.  Note when using the semaphore for synchronisation with an
-			// ISR in this manner there is no need to 'give' the semaphore back.
-        }
-    }
- }
-
- // Timer ISR
- void vTimerISR( void * pvParameters )
- {
- static uint8_t ucLocalTickCount = 0;
- static BaseType_t xHigherPriorityTaskWoken;
-
-    // A timer tick has occurred.
-
-    // ... Do other time functions.
-
-    // Is it time for vATask () to run?
-	xHigherPriorityTaskWoken = pdFALSE;
-    ucLocalTickCount++;
-    if( ucLocalTickCount >= TICKS_TO_WAIT )
-    {
-        // Unblock the task by releasing the semaphore.
-        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
-
-        // Reset the count so we release the semaphore again in 10 ticks time.
-        ucLocalTickCount = 0;
-    }
-
-    if( xHigherPriorityTaskWoken != pdFALSE )
-    {
-        // We can force a context switch here.  Context switching from an
-        // ISR uses port specific syntax.  Check the demo task for your port
-        // to find the syntax required.
-    }
- }
- 
+ \#define TICKS_TO_WAIT 10 + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Repetitive task. + * void vATask( void * pvParameters ) + * { + * for( ;; ) + * { + * // We want this task to run every 10 ticks of a timer. The semaphore + * // was created before this task was started. + * + * // Block waiting for the semaphore to become available. + * if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) + * { + * // It is time to execute. + * + * // ... + * + * // We have finished our task. Return to the top of the loop where + * // we will block on the semaphore until it is time to execute + * // again. Note when using the semaphore for synchronisation with an + * // ISR in this manner there is no need to 'give' the semaphore back. + * } + * } + * } + * + * // Timer ISR + * void vTimerISR( void * pvParameters ) + * { + * static uint8_t ucLocalTickCount = 0; + * static BaseType_t xHigherPriorityTaskWoken; + * + * // A timer tick has occurred. + * + * // ... Do other time functions. + * + * // Is it time for vATask () to run? + * xHigherPriorityTaskWoken = pdFALSE; + * ucLocalTickCount++; + * if( ucLocalTickCount >= TICKS_TO_WAIT ) + * { + * // Unblock the task by releasing the semaphore. + * xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); + * + * // Reset the count so we release the semaphore again in 10 ticks time. + * ucLocalTickCount = 0; + * } + * + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // We can force a context switch here. Context switching from an + * // ISR uses port specific syntax. Check the demo task for your port + * // to find the syntax required. + * } + * } + *
* \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR * \ingroup Semaphores */ -#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) ) +#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) ) /** * semphr. h *
- xSemaphoreTakeFromISR(
-                          SemaphoreHandle_t xSemaphore,
-                          BaseType_t *pxHigherPriorityTaskWoken
-                      )
+ * xSemaphoreTakeFromISR( + * SemaphoreHandle_t xSemaphore, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + *
* * Macro to take a semaphore from an ISR. The semaphore must have * previously been created with a call to xSemaphoreCreateBinary() or @@ -655,11 +670,13 @@ typedef QueueHandle_t SemaphoreHandle_t; * @return pdTRUE if the semaphore was successfully taken, otherwise * pdFALSE */ -#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) +#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutex( void )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateMutex( void );
+ * 
* * Creates a new mutex type semaphore instance, and returns a handle by which * the new mutex can be referenced. @@ -668,7 +685,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * of memory, in which the mutex structure is stored. If a mutex is created * using xSemaphoreCreateMutex() then the required memory is automatically * dynamically allocated inside the xSemaphoreCreateMutex() function. (see - * http://www.freertos.org/a00111.html). If a mutex is created using + * https://www.FreeRTOS.org/a00111.html). If a mutex is created using * xSemaphoreCreateMutexStatic() then the application writer must provided the * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created * without using any dynamic memory allocation. @@ -693,32 +710,34 @@ typedef QueueHandle_t SemaphoreHandle_t; * data structures then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+ *  // This is a macro so pass the variable in directly.
+ *  xSemaphore = xSemaphoreCreateMutex();
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // The semaphore was created successfully.
+ *      // The semaphore can now be used.
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreCreateMutex xSemaphoreCreateMutex * \ingroup Semaphores */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ) +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ) #endif /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
+ * 
* * Creates a new mutex type semaphore instance, and returns a handle by which * the new mutex can be referenced. @@ -727,7 +746,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * of memory, in which the mutex structure is stored. If a mutex is created * using xSemaphoreCreateMutex() then the required memory is automatically * dynamically allocated inside the xSemaphoreCreateMutex() function. (see - * http://www.freertos.org/a00111.html). If a mutex is created using + * https://www.FreeRTOS.org/a00111.html). If a mutex is created using * xSemaphoreCreateMutexStatic() then the application writer must provided the * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created * without using any dynamic memory allocation. @@ -755,32 +774,34 @@ typedef QueueHandle_t SemaphoreHandle_t; * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A mutex cannot be used before it has been created.  xMutexBuffer is
-    // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
-    // attempted.
-    xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ * StaticSemaphore_t xMutexBuffer;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // A mutex cannot be used before it has been created.  xMutexBuffer is
+ *  // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is
+ *  // attempted.
+ *  xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer );
+ *
+ *  // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+ *  // so there is no need to check it.
+ * }
+ * 
* \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic * \ingroup Semaphores */ - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - #define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) ) +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) ) #endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
+ * 
* * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. @@ -790,7 +811,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * created using xSemaphoreCreateRecursiveMutex() then the required memory is * automatically dynamically allocated inside the * xSemaphoreCreateRecursiveMutex() function. (see - * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * https://www.FreeRTOS.org/a00111.html). If a recursive mutex is created using * xSemaphoreCreateRecursiveMutexStatic() then the application writer must * provide the memory that will get used by the mutex. * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to @@ -822,32 +843,34 @@ typedef QueueHandle_t SemaphoreHandle_t; * SemaphoreHandle_t. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
-    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
-    // This is a macro so pass the variable in directly.
-    xSemaphore = xSemaphoreCreateRecursiveMutex();
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+ *  // This is a macro so pass the variable in directly.
+ *  xSemaphore = xSemaphoreCreateRecursiveMutex();
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // The semaphore was created successfully.
+ *      // The semaphore can now be used.
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreCreateRecursiveMutex xSemaphoreCreateRecursiveMutex * \ingroup Semaphores */ -#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) - #define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) #endif /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer );
+ * 
* * Creates a new recursive mutex type semaphore instance, and returns a handle * by which the new recursive mutex can be referenced. @@ -857,7 +880,7 @@ typedef QueueHandle_t SemaphoreHandle_t; * created using xSemaphoreCreateRecursiveMutex() then the required memory is * automatically dynamically allocated inside the * xSemaphoreCreateRecursiveMutex() function. (see - * http://www.freertos.org/a00111.html). If a recursive mutex is created using + * https://www.FreeRTOS.org/a00111.html). If a recursive mutex is created using * xSemaphoreCreateRecursiveMutexStatic() then the application writer must * provide the memory that will get used by the mutex. * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to @@ -894,47 +917,49 @@ typedef QueueHandle_t SemaphoreHandle_t; * returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xMutexBuffer;
-
- void vATask( void * pvParameters )
- {
-    // A recursive semaphore cannot be used before it is created.  Here a
-    // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
-    // The address of xMutexBuffer is passed into the function, and will hold
-    // the mutexes data structures - so no dynamic memory allocation will be
-    // attempted.
-    xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
-
-    // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
-    // so there is no need to check it.
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ * StaticSemaphore_t xMutexBuffer;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ *  // A recursive semaphore cannot be used before it is created.  Here a
+ *  // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic().
+ *  // The address of xMutexBuffer is passed into the function, and will hold
+ *  // the mutexes data structures - so no dynamic memory allocation will be
+ *  // attempted.
+ *  xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
+ *
+ *  // As no dynamic memory allocation was performed, xSemaphore cannot be NULL,
+ *  // so there is no need to check it.
+ * }
+ * 
* \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic * \ingroup Semaphores */ -#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) - #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore ) +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore ) #endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
+ * 
* * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * * In many usage scenarios it is faster and more memory efficient to use a * direct to task notification in place of a counting semaphore! - * http://www.freertos.org/RTOS-task-notifications.html + * https://www.FreeRTOS.org/RTOS-task-notifications.html * * Internally, within the FreeRTOS implementation, counting semaphores use a * block of memory, in which the counting semaphore structure is stored. If a * counting semaphore is created using xSemaphoreCreateCounting() then the * required memory is automatically dynamically allocated inside the * xSemaphoreCreateCounting() function. (see - * http://www.freertos.org/a00111.html). If a counting semaphore is created + * https://www.FreeRTOS.org/a00111.html). If a counting semaphore is created * using xSemaphoreCreateCountingStatic() then the application writer can * instead optionally provide the memory that will get used by the counting * semaphore. xSemaphoreCreateCountingStatic() therefore allows a counting @@ -972,49 +997,51 @@ typedef QueueHandle_t SemaphoreHandle_t; * created. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
-    // The max value to which the semaphore can count should be 10, and the
-    // initial value assigned to the count should be 0.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
-
-    if( xSemaphore != NULL )
-    {
-        // The semaphore was created successfully.
-        // The semaphore can now be used.
-    }
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ *  // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
+ *  // The max value to which the semaphore can count should be 10, and the
+ *  // initial value assigned to the count should be 0.
+ *  xSemaphore = xSemaphoreCreateCounting( 10, 0 );
+ *
+ *  if( xSemaphore != NULL )
+ *  {
+ *      // The semaphore was created successfully.
+ *      // The semaphore can now be used.
+ *  }
+ * }
+ * 
* \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting * \ingroup Semaphores */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) #endif /** * semphr. h - *
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer )
+ *
+ * SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer );
+ * 
* * Creates a new counting semaphore instance, and returns a handle by which the * new counting semaphore can be referenced. * * In many usage scenarios it is faster and more memory efficient to use a * direct to task notification in place of a counting semaphore! - * http://www.freertos.org/RTOS-task-notifications.html + * https://www.FreeRTOS.org/RTOS-task-notifications.html * * Internally, within the FreeRTOS implementation, counting semaphores use a * block of memory, in which the counting semaphore structure is stored. If a * counting semaphore is created using xSemaphoreCreateCounting() then the * required memory is automatically dynamically allocated inside the * xSemaphoreCreateCounting() function. (see - * http://www.freertos.org/a00111.html). If a counting semaphore is created + * https://www.FreeRTOS.org/a00111.html). If a counting semaphore is created * using xSemaphoreCreateCountingStatic() then the application writer must * provide the memory. xSemaphoreCreateCountingStatic() therefore allows a * counting semaphore to be created without using any dynamic memory allocation. @@ -1056,36 +1083,38 @@ typedef QueueHandle_t SemaphoreHandle_t; * then NULL is returned. * * Example usage: -
- SemaphoreHandle_t xSemaphore;
- StaticSemaphore_t xSemaphoreBuffer;
-
- void vATask( void * pvParameters )
- {
- SemaphoreHandle_t xSemaphore = NULL;
-
-    // Counting semaphore cannot be used before they have been created.  Create
-    // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
-    // value to which the semaphore can count is 10, and the initial value
-    // assigned to the count will be 0.  The address of xSemaphoreBuffer is
-    // passed in and will be used to hold the semaphore structure, so no dynamic
-    // memory allocation will be used.
-    xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
-
-    // No memory allocation was attempted so xSemaphore cannot be NULL, so there
-    // is no need to check its value.
- }
- 
+ *
+ * SemaphoreHandle_t xSemaphore;
+ * StaticSemaphore_t xSemaphoreBuffer;
+ *
+ * void vATask( void * pvParameters )
+ * {
+ * SemaphoreHandle_t xSemaphore = NULL;
+ *
+ *  // Counting semaphore cannot be used before they have been created.  Create
+ *  // a counting semaphore using xSemaphoreCreateCountingStatic().  The max
+ *  // value to which the semaphore can count is 10, and the initial value
+ *  // assigned to the count will be 0.  The address of xSemaphoreBuffer is
+ *  // passed in and will be used to hold the semaphore structure, so no dynamic
+ *  // memory allocation will be used.
+ *  xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer );
+ *
+ *  // No memory allocation was attempted so xSemaphore cannot be NULL, so there
+ *  // is no need to check its value.
+ * }
+ * 
* \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic * \ingroup Semaphores */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) ) +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) ) #endif /* configSUPPORT_STATIC_ALLOCATION */ /** * semphr. h - *
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
+ *
+ * void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
+ * 
* * Delete a semaphore. This function must be used with care. For example, * do not delete a mutex type semaphore if the mutex is held by a task. @@ -1095,11 +1124,13 @@ typedef QueueHandle_t SemaphoreHandle_t; * \defgroup vSemaphoreDelete vSemaphoreDelete * \ingroup Semaphores */ -#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) +#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) /** * semphr.h - *
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
+ *
+ * TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
+ * 
* * If xMutex is indeed a mutex type semaphore, return the current mutex holder. * If xMutex is not a mutex type semaphore, or the mutex is available (not held @@ -1110,22 +1141,26 @@ typedef QueueHandle_t SemaphoreHandle_t; * the holder may change between the function exiting and the returned value * being tested. */ -#define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) +#define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) /** * semphr.h - *
TaskHandle_t xSemaphoreGetMutexHolderFromISR( SemaphoreHandle_t xMutex );
+ *
+ * TaskHandle_t xSemaphoreGetMutexHolderFromISR( SemaphoreHandle_t xMutex );
+ * 
* * If xMutex is indeed a mutex type semaphore, return the current mutex holder. * If xMutex is not a mutex type semaphore, or the mutex is available (not held * by a task), return NULL. * */ -#define xSemaphoreGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) ) +#define xSemaphoreGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) ) /** * semphr.h - *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
+ *
+ * UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
+ * 
* * If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns * its current count value. If the semaphore is a binary semaphore then @@ -1133,8 +1168,6 @@ typedef QueueHandle_t SemaphoreHandle_t; * semaphore is not available. * */ -#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) ) +#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) ) #endif /* SEMAPHORE_H */ - - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h index c505574ddf..317d1e06c6 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/stack_macros.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #ifndef STACK_MACROS_H @@ -44,86 +43,85 @@ /*-----------------------------------------------------------*/ -#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) +#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) - /* Only the current stack state is to be checked. */ - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - /* Is the currently saved stack pointer within the stack limit? */ \ - if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } +/* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } #endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ /*-----------------------------------------------------------*/ -#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) +#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) - /* Only the current stack state is to be checked. */ - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - \ - /* Is the currently saved stack pointer within the stack limit? */ \ - if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } +/* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } #endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ /*-----------------------------------------------------------*/ -#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) - - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ - const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ - \ - if( ( pulStack[ 0 ] != ulCheckValue ) || \ - ( pulStack[ 1 ] != ulCheckValue ) || \ - ( pulStack[ 2 ] != ulCheckValue ) || \ - ( pulStack[ 3 ] != ulCheckValue ) ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } +#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ + const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ + \ + if( ( pulStack[ 0 ] != ulCheckValue ) || \ + ( pulStack[ 1 ] != ulCheckValue ) || \ + ( pulStack[ 2 ] != ulCheckValue ) || \ + ( pulStack[ 3 ] != ulCheckValue ) ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } #endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ /*-----------------------------------------------------------*/ -#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) - - #define taskCHECK_FOR_STACK_OVERFLOW() \ - { \ - int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ - static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ - tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ - \ - \ - pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ - \ - /* Has the extremity of the task stack ever been written over? */ \ - if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ - { \ - vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ - } \ - } +#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + int8_t * pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } #endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ /*-----------------------------------------------------------*/ /* Remove stack overflow macro if not being used. */ #ifndef taskCHECK_FOR_STACK_OVERFLOW - #define taskCHECK_FOR_STACK_OVERFLOW() + #define taskCHECK_FOR_STACK_OVERFLOW() #endif #endif /* STACK_MACROS_H */ - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h index 3605703f48..2f56c483c3 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /* @@ -52,12 +51,14 @@ #define STREAM_BUFFER_H #ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include stream_buffer.h" + #error "include FreeRTOS.h must appear in source files before include stream_buffer.h" #endif +/* *INDENT-OFF* */ #if defined( __cplusplus ) -extern "C" { + extern "C" { #endif +/* *INDENT-ON* */ /** * Type by which stream buffers are referenced. For example, a call to @@ -72,9 +73,9 @@ typedef struct StreamBufferDef_t * StreamBufferHandle_t; /** * message_buffer.h * -
-StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );
-
+ *
+ * StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );
+ * 
* * Creates a new stream buffer using dynamically allocated memory. See * xStreamBufferCreateStatic() for a version that uses statically allocated @@ -108,43 +109,43 @@ StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTrigg * buffer. * * Example use: -
-
-void vAFunction( void )
-{
-StreamBufferHandle_t xStreamBuffer;
-const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;
-
-    // Create a stream buffer that can hold 100 bytes.  The memory used to hold
-    // both the stream buffer structure and the data in the stream buffer is
-    // allocated dynamically.
-    xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
-
-    if( xStreamBuffer == NULL )
-    {
-        // There was not enough heap memory space available to create the
-        // stream buffer.
-    }
-    else
-    {
-        // The stream buffer was created successfully and can now be used.
-    }
-}
-
+ *
+ *
+ * void vAFunction( void )
+ * {
+ * StreamBufferHandle_t xStreamBuffer;
+ * const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10;
+ *
+ *  // Create a stream buffer that can hold 100 bytes.  The memory used to hold
+ *  // both the stream buffer structure and the data in the stream buffer is
+ *  // allocated dynamically.
+ *  xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );
+ *
+ *  if( xStreamBuffer == NULL )
+ *  {
+ *      // There was not enough heap memory space available to create the
+ *      // stream buffer.
+ *  }
+ *  else
+ *  {
+ *      // The stream buffer was created successfully and can now be used.
+ *  }
+ * }
+ * 
* \defgroup xStreamBufferCreate xStreamBufferCreate * \ingroup StreamBufferManagement */ -#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE ) +#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE ) /** * stream_buffer.h * -
-StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes,
-                                                size_t xTriggerLevelBytes,
-                                                uint8_t *pucStreamBufferStorageArea,
-                                                StaticStreamBuffer_t *pxStaticStreamBuffer );
-
+ *
+ * StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes,
+ *                                              size_t xTriggerLevelBytes,
+ *                                              uint8_t *pucStreamBufferStorageArea,
+ *                                              StaticStreamBuffer_t *pxStaticStreamBuffer );
+ * 
* Creates a new stream buffer using statically allocated memory. See * xStreamBufferCreate() for a version that uses dynamically allocated memory. * @@ -181,51 +182,52 @@ StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes, * pxStaticstreamBuffer are NULL then NULL is returned. * * Example use: -
-
-// Used to dimension the array used to hold the streams.  The available space
-// will actually be one less than this, so 999.
-#define STORAGE_SIZE_BYTES 1000
-
-// Defines the memory that will actually hold the streams within the stream
-// buffer.
-static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
-
-// The variable used to hold the stream buffer structure.
-StaticStreamBuffer_t xStreamBufferStruct;
-
-void MyFunction( void )
-{
-StreamBufferHandle_t xStreamBuffer;
-const size_t xTriggerLevel = 1;
-
-    xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ),
-                                               xTriggerLevel,
-                                               ucBufferStorage,
-                                               &xStreamBufferStruct );
-
-    // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer
-    // parameters were NULL, xStreamBuffer will not be NULL, and can be used to
-    // reference the created stream buffer in other stream buffer API calls.
-
-    // Other code that uses the stream buffer can go here.
-}
-
-
+ *
+ *
+ * // Used to dimension the array used to hold the streams.  The available space
+ * // will actually be one less than this, so 999.
+ #define STORAGE_SIZE_BYTES 1000
+ *
+ * // Defines the memory that will actually hold the streams within the stream
+ * // buffer.
+ * static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
+ *
+ * // The variable used to hold the stream buffer structure.
+ * StaticStreamBuffer_t xStreamBufferStruct;
+ *
+ * void MyFunction( void )
+ * {
+ * StreamBufferHandle_t xStreamBuffer;
+ * const size_t xTriggerLevel = 1;
+ *
+ *  xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucBufferStorage ),
+ *                                             xTriggerLevel,
+ *                                             ucBufferStorage,
+ *                                             &xStreamBufferStruct );
+ *
+ *  // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer
+ *  // parameters were NULL, xStreamBuffer will not be NULL, and can be used to
+ *  // reference the created stream buffer in other stream buffer API calls.
+ *
+ *  // Other code that uses the stream buffer can go here.
+ * }
+ *
+ * 
* \defgroup xStreamBufferCreateStatic xStreamBufferCreateStatic * \ingroup StreamBufferManagement */ -#define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer ) +#define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) \ + xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pdFALSE, pucStreamBufferStorageArea, pxStaticStreamBuffer ) /** * stream_buffer.h * -
-size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
-                          const void *pvTxData,
-                          size_t xDataLengthBytes,
-                          TickType_t xTicksToWait );
-
+ *
+ * size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
+ *                        const void *pvTxData,
+ *                        size_t xDataLengthBytes,
+ *                        TickType_t xTicksToWait );
+ * 
* * Sends bytes to a stream buffer. The bytes are copied into the stream buffer. * @@ -275,54 +277,54 @@ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, * write as many bytes as possible. * * Example use: -
-void vAFunction( StreamBufferHandle_t xStreamBuffer )
-{
-size_t xBytesSent;
-uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
-char *pcStringToSend = "String to send";
-const TickType_t x100ms = pdMS_TO_TICKS( 100 );
-
-    // Send an array to the stream buffer, blocking for a maximum of 100ms to
-    // wait for enough space to be available in the stream buffer.
-    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
-
-    if( xBytesSent != sizeof( ucArrayToSend ) )
-    {
-        // The call to xStreamBufferSend() times out before there was enough
-        // space in the buffer for the data to be written, but it did
-        // successfully write xBytesSent bytes.
-    }
-
-    // Send the string to the stream buffer.  Return immediately if there is not
-    // enough space in the buffer.
-    xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
-
-    if( xBytesSent != strlen( pcStringToSend ) )
-    {
-        // The entire string could not be added to the stream buffer because
-        // there was not enough free space in the buffer, but xBytesSent bytes
-        // were sent.  Could try again to send the remaining bytes.
-    }
-}
-
+ *
+ * void vAFunction( StreamBufferHandle_t xStreamBuffer )
+ * {
+ * size_t xBytesSent;
+ * uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
+ * char *pcStringToSend = "String to send";
+ * const TickType_t x100ms = pdMS_TO_TICKS( 100 );
+ *
+ *  // Send an array to the stream buffer, blocking for a maximum of 100ms to
+ *  // wait for enough space to be available in the stream buffer.
+ *  xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
+ *
+ *  if( xBytesSent != sizeof( ucArrayToSend ) )
+ *  {
+ *      // The call to xStreamBufferSend() times out before there was enough
+ *      // space in the buffer for the data to be written, but it did
+ *      // successfully write xBytesSent bytes.
+ *  }
+ *
+ *  // Send the string to the stream buffer.  Return immediately if there is not
+ *  // enough space in the buffer.
+ *  xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
+ *
+ *  if( xBytesSent != strlen( pcStringToSend ) )
+ *  {
+ *      // The entire string could not be added to the stream buffer because
+ *      // there was not enough free space in the buffer, but xBytesSent bytes
+ *      // were sent.  Could try again to send the remaining bytes.
+ *  }
+ * }
+ * 
* \defgroup xStreamBufferSend xStreamBufferSend * \ingroup StreamBufferManagement */ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, - const void *pvTxData, - size_t xDataLengthBytes, - TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + const void * pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
-                                 const void *pvTxData,
-                                 size_t xDataLengthBytes,
-                                 BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
+ *                               const void *pvTxData,
+ *                               size_t xDataLengthBytes,
+ *                               BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * Interrupt safe version of the API function that sends a stream of bytes to * the stream buffer. @@ -374,56 +376,56 @@ size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, * space for all the bytes to be written. * * Example use: -
-// A stream buffer that has already been created.
-StreamBufferHandle_t xStreamBuffer;
-
-void vAnInterruptServiceRoutine( void )
-{
-size_t xBytesSent;
-char *pcStringToSend = "String to send";
-BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
-
-    // Attempt to send the string to the stream buffer.
-    xBytesSent = xStreamBufferSendFromISR( xStreamBuffer,
-                                           ( void * ) pcStringToSend,
-                                           strlen( pcStringToSend ),
-                                           &xHigherPriorityTaskWoken );
-
-    if( xBytesSent != strlen( pcStringToSend ) )
-    {
-        // There was not enough free space in the stream buffer for the entire
-        // string to be written, ut xBytesSent bytes were written.
-    }
-
-    // If xHigherPriorityTaskWoken was set to pdTRUE inside
-    // xStreamBufferSendFromISR() then a task that has a priority above the
-    // priority of the currently executing task was unblocked and a context
-    // switch should be performed to ensure the ISR returns to the unblocked
-    // task.  In most FreeRTOS ports this is done by simply passing
-    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
-    // variables value, and perform the context switch if necessary.  Check the
-    // documentation for the port in use for port specific instructions.
-    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-}
-
+ *
+ * // A stream buffer that has already been created.
+ * StreamBufferHandle_t xStreamBuffer;
+ *
+ * void vAnInterruptServiceRoutine( void )
+ * {
+ * size_t xBytesSent;
+ * char *pcStringToSend = "String to send";
+ * BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
+ *
+ *  // Attempt to send the string to the stream buffer.
+ *  xBytesSent = xStreamBufferSendFromISR( xStreamBuffer,
+ *                                         ( void * ) pcStringToSend,
+ *                                         strlen( pcStringToSend ),
+ *                                         &xHigherPriorityTaskWoken );
+ *
+ *  if( xBytesSent != strlen( pcStringToSend ) )
+ *  {
+ *      // There was not enough free space in the stream buffer for the entire
+ *      // string to be written, ut xBytesSent bytes were written.
+ *  }
+ *
+ *  // If xHigherPriorityTaskWoken was set to pdTRUE inside
+ *  // xStreamBufferSendFromISR() then a task that has a priority above the
+ *  // priority of the currently executing task was unblocked and a context
+ *  // switch should be performed to ensure the ISR returns to the unblocked
+ *  // task.  In most FreeRTOS ports this is done by simply passing
+ *  // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+ *  // variables value, and perform the context switch if necessary.  Check the
+ *  // documentation for the port in use for port specific instructions.
+ *  taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ * }
+ * 
* \defgroup xStreamBufferSendFromISR xStreamBufferSendFromISR * \ingroup StreamBufferManagement */ size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, - const void *pvTxData, - size_t xDataLengthBytes, - BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + const void * pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
-                             void *pvRxData,
-                             size_t xBufferLengthBytes,
-                             TickType_t xTicksToWait );
-
+ *
+ * size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
+ *                           void *pvRxData,
+ *                           size_t xBufferLengthBytes,
+ *                           TickType_t xTicksToWait );
+ * 
* * Receives bytes from a stream buffer. * @@ -473,46 +475,46 @@ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, * out before xBufferLengthBytes were available. * * Example use: -
-void vAFunction( StreamBuffer_t xStreamBuffer )
-{
-uint8_t ucRxData[ 20 ];
-size_t xReceivedBytes;
-const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
-
-    // Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
-    // Wait in the Blocked state (so not using any CPU processing time) for a
-    // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
-    // available.
-    xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
-                                           ( void * ) ucRxData,
-                                           sizeof( ucRxData ),
-                                           xBlockTime );
-
-    if( xReceivedBytes > 0 )
-    {
-        // A ucRxData contains another xRecievedBytes bytes of data, which can
-        // be processed here....
-    }
-}
-
+ *
+ * void vAFunction( StreamBuffer_t xStreamBuffer )
+ * {
+ * uint8_t ucRxData[ 20 ];
+ * size_t xReceivedBytes;
+ * const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
+ *
+ *  // Receive up to another sizeof( ucRxData ) bytes from the stream buffer.
+ *  // Wait in the Blocked state (so not using any CPU processing time) for a
+ *  // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be
+ *  // available.
+ *  xReceivedBytes = xStreamBufferReceive( xStreamBuffer,
+ *                                         ( void * ) ucRxData,
+ *                                         sizeof( ucRxData ),
+ *                                         xBlockTime );
+ *
+ *  if( xReceivedBytes > 0 )
+ *  {
+ *      // A ucRxData contains another xRecievedBytes bytes of data, which can
+ *      // be processed here....
+ *  }
+ * }
+ * 
* \defgroup xStreamBufferReceive xStreamBufferReceive * \ingroup StreamBufferManagement */ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, - void *pvRxData, - size_t xBufferLengthBytes, - TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + void * pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
-                                    void *pvRxData,
-                                    size_t xBufferLengthBytes,
-                                    BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
+ *                                  void *pvRxData,
+ *                                  size_t xBufferLengthBytes,
+ *                                  BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * An interrupt safe version of the API function that receives bytes from a * stream buffer. @@ -549,53 +551,53 @@ size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, * @return The number of bytes read from the stream buffer, if any. * * Example use: -
-// A stream buffer that has already been created.
-StreamBuffer_t xStreamBuffer;
-
-void vAnInterruptServiceRoutine( void )
-{
-uint8_t ucRxData[ 20 ];
-size_t xReceivedBytes;
-BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
-
-    // Receive the next stream from the stream buffer.
-    xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
-                                                  ( void * ) ucRxData,
-                                                  sizeof( ucRxData ),
-                                                  &xHigherPriorityTaskWoken );
-
-    if( xReceivedBytes > 0 )
-    {
-        // ucRxData contains xReceivedBytes read from the stream buffer.
-        // Process the stream here....
-    }
-
-    // If xHigherPriorityTaskWoken was set to pdTRUE inside
-    // xStreamBufferReceiveFromISR() then a task that has a priority above the
-    // priority of the currently executing task was unblocked and a context
-    // switch should be performed to ensure the ISR returns to the unblocked
-    // task.  In most FreeRTOS ports this is done by simply passing
-    // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
-    // variables value, and perform the context switch if necessary.  Check the
-    // documentation for the port in use for port specific instructions.
-    taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
-}
-
+ *
+ * // A stream buffer that has already been created.
+ * StreamBuffer_t xStreamBuffer;
+ *
+ * void vAnInterruptServiceRoutine( void )
+ * {
+ * uint8_t ucRxData[ 20 ];
+ * size_t xReceivedBytes;
+ * BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // Initialised to pdFALSE.
+ *
+ *  // Receive the next stream from the stream buffer.
+ *  xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer,
+ *                                                ( void * ) ucRxData,
+ *                                                sizeof( ucRxData ),
+ *                                                &xHigherPriorityTaskWoken );
+ *
+ *  if( xReceivedBytes > 0 )
+ *  {
+ *      // ucRxData contains xReceivedBytes read from the stream buffer.
+ *      // Process the stream here....
+ *  }
+ *
+ *  // If xHigherPriorityTaskWoken was set to pdTRUE inside
+ *  // xStreamBufferReceiveFromISR() then a task that has a priority above the
+ *  // priority of the currently executing task was unblocked and a context
+ *  // switch should be performed to ensure the ISR returns to the unblocked
+ *  // task.  In most FreeRTOS ports this is done by simply passing
+ *  // xHigherPriorityTaskWoken into taskYIELD_FROM_ISR(), which will test the
+ *  // variables value, and perform the context switch if necessary.  Check the
+ *  // documentation for the port in use for port specific instructions.
+ *  taskYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+ * }
+ * 
* \defgroup xStreamBufferReceiveFromISR xStreamBufferReceiveFromISR * \ingroup StreamBufferManagement */ size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, - void *pvRxData, - size_t xBufferLengthBytes, - BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + void * pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Deletes a stream buffer that was previously created using a call to * xStreamBufferCreate() or xStreamBufferCreateStatic(). If the stream @@ -615,9 +617,9 @@ void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTI /** * stream_buffer.h * -
-BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Queries a stream buffer to see if it is full. A stream buffer is full if it * does not have any free space, and therefore cannot accept any more data. @@ -635,9 +637,9 @@ BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_ /** * stream_buffer.h * -
-BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Queries a stream buffer to see if it is empty. A stream buffer is empty if * it does not contain any data. @@ -655,9 +657,9 @@ BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED /** * stream_buffer.h * -
-BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Resets a stream buffer to its initial, empty, state. Any data that was in * the stream buffer is discarded. A stream buffer can only be reset if there @@ -678,9 +680,9 @@ BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_F /** * stream_buffer.h * -
-size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Queries a stream buffer to see how much free space it contains, which is * equal to the amount of data that can be sent to the stream buffer before it @@ -699,9 +701,9 @@ size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVIL /** * stream_buffer.h * -
-size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );
-
+ *
+ * size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer );
+ * 
* * Queries a stream buffer to see how much data it contains, which is equal to * the number of bytes that can be read from the stream buffer before the stream @@ -720,9 +722,9 @@ size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILE /** * stream_buffer.h * -
-BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel );
-
+ *
+ * BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel );
+ * 
* * A stream buffer's trigger level is the number of bytes that must be in the * stream buffer before a task that is blocked on the stream buffer to @@ -752,14 +754,15 @@ BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, siz * \defgroup xStreamBufferSetTriggerLevel xStreamBufferSetTriggerLevel * \ingroup StreamBufferManagement */ -BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) PRIVILEGED_FUNCTION; +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, + size_t xTriggerLevel ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * For advanced users only. * @@ -791,14 +794,15 @@ BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer * \defgroup xStreamBufferSendCompletedFromISR xStreamBufferSendCompletedFromISR * \ingroup StreamBufferManagement */ -BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /** * stream_buffer.h * -
-BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
-
+ *
+ * BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
+ * 
* * For advanced users only. * @@ -831,29 +835,33 @@ BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuf * \defgroup xStreamBufferReceiveCompletedFromISR xStreamBufferReceiveCompletedFromISR * \ingroup StreamBufferManagement */ -BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; /* Functions below here are not part of the public API. */ StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, - size_t xTriggerLevelBytes, - BaseType_t xIsMessageBuffer ) PRIVILEGED_FUNCTION; + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer ) PRIVILEGED_FUNCTION; StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, - size_t xTriggerLevelBytes, - BaseType_t xIsMessageBuffer, - uint8_t * const pucStreamBufferStorageArea, - StaticStreamBuffer_t * const pxStaticStreamBuffer ) PRIVILEGED_FUNCTION; + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) PRIVILEGED_FUNCTION; size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; -#if( configUSE_TRACE_FACILITY == 1 ) - void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, UBaseType_t uxStreamBufferNumber ) PRIVILEGED_FUNCTION; - UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; - uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; +#if ( configUSE_TRACE_FACILITY == 1 ) + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, + UBaseType_t uxStreamBufferNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; #endif +/* *INDENT-OFF* */ #if defined( __cplusplus ) -} + } #endif +/* *INDENT-ON* */ -#endif /* !defined( STREAM_BUFFER_H ) */ +#endif /* !defined( STREAM_BUFFER_H ) */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/task.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/task.h index 4b8639cb27..730deec5a0 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/task.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/task.h @@ -1,2543 +1,3037 @@ -/* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef INC_TASK_H -#define INC_TASK_H - -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include task.h" -#endif - -#include "list.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/*----------------------------------------------------------- - * MACROS AND DEFINITIONS - *----------------------------------------------------------*/ - -#define tskKERNEL_VERSION_NUMBER "V10.3.1" -#define tskKERNEL_VERSION_MAJOR 10 -#define tskKERNEL_VERSION_MINOR 3 -#define tskKERNEL_VERSION_BUILD 1 - -/* MPU region parameters passed in ulParameters - * of MemoryRegion_t struct. */ -#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) -#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) -#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) -#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) -#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) - -/** - * task. h - * - * Type by which tasks are referenced. For example, a call to xTaskCreate - * returns (via a pointer parameter) an TaskHandle_t variable that can then - * be used as a parameter to vTaskDelete to delete the task. - * - * \defgroup TaskHandle_t TaskHandle_t - * \ingroup Tasks - */ -struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ -typedef struct tskTaskControlBlock* TaskHandle_t; - -/* - * Defines the prototype to which the application task hook function must - * conform. - */ -typedef BaseType_t (*TaskHookFunction_t)( void * ); - -/* Task states returned by eTaskGetState. */ -typedef enum -{ - eRunning = 0, /* A task is querying the state of itself, so must be running. */ - eReady, /* The task being queried is in a read or pending ready list. */ - eBlocked, /* The task being queried is in the Blocked state. */ - eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ - eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ - eInvalid /* Used as an 'invalid state' value. */ -} eTaskState; - -/* Actions that can be performed when vTaskNotify() is called. */ -typedef enum -{ - eNoAction = 0, /* Notify the task without updating its notify value. */ - eSetBits, /* Set bits in the task's notification value. */ - eIncrement, /* Increment the task's notification value. */ - eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ - eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ -} eNotifyAction; - -/* - * Used internally only. - */ -typedef struct xTIME_OUT -{ - BaseType_t xOverflowCount; - TickType_t xTimeOnEntering; -} TimeOut_t; - -/* - * Defines the memory ranges allocated to the task when an MPU is used. - */ -typedef struct xMEMORY_REGION -{ - void *pvBaseAddress; - uint32_t ulLengthInBytes; - uint32_t ulParameters; -} MemoryRegion_t; - -/* - * Parameters required to create an MPU protected task. - */ -typedef struct xTASK_PARAMETERS -{ - TaskFunction_t pvTaskCode; - const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - configSTACK_DEPTH_TYPE usStackDepth; - void *pvParameters; - UBaseType_t uxPriority; - StackType_t *puxStackBuffer; - MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; - #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - StaticTask_t * const pxTaskBuffer; - #endif -} TaskParameters_t; - -/* Used with the uxTaskGetSystemState() function to return the state of each task -in the system. */ -typedef struct xTASK_STATUS -{ - TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ - const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - UBaseType_t xTaskNumber; /* A number unique to the task. */ - eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ - UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ - UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ - uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ - StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ - configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ -} TaskStatus_t; - -/* Possible return values for eTaskConfirmSleepModeStatus(). */ -typedef enum -{ - eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ - eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ - eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ -} eSleepModeStatus; - -/** - * Defines the priority used by the idle task. This must not be modified. - * - * \ingroup TaskUtils - */ -#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) - -/** - * task. h - * - * Macro for forcing a context switch. - * - * \defgroup taskYIELD taskYIELD - * \ingroup SchedulerControl - */ -#define taskYIELD() portYIELD() - -/** - * task. h - * - * Macro to mark the start of a critical code region. Preemptive context - * switches cannot occur when in a critical region. - * - * NOTE: This may alter the stack (depending on the portable implementation) - * so must be used with care! - * - * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL - * \ingroup SchedulerControl - */ -#define taskENTER_CRITICAL() portENTER_CRITICAL() -#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() - -/** - * task. h - * - * Macro to mark the end of a critical code region. Preemptive context - * switches cannot occur when in a critical region. - * - * NOTE: This may alter the stack (depending on the portable implementation) - * so must be used with care! - * - * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL - * \ingroup SchedulerControl - */ -#define taskEXIT_CRITICAL() portEXIT_CRITICAL() -#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) -/** - * task. h - * - * Macro to disable all maskable interrupts. - * - * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS - * \ingroup SchedulerControl - */ -#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() - -/** - * task. h - * - * Macro to enable microcontroller interrupts. - * - * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS - * \ingroup SchedulerControl - */ -#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() - -/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is -0 to generate more optimal code when configASSERT() is defined as the constant -is used in assert() statements. */ -#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) -#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) -#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) - - -/*----------------------------------------------------------- - * TASK CREATION API - *----------------------------------------------------------*/ - -/** - * task. h - *
- BaseType_t xTaskCreate(
-							  TaskFunction_t pvTaskCode,
-							  const char * const pcName,
-							  configSTACK_DEPTH_TYPE usStackDepth,
-							  void *pvParameters,
-							  UBaseType_t uxPriority,
-							  TaskHandle_t *pvCreatedTask
-						  );
- * - * Create a new task and add it to the list of tasks that are ready to run. - * - * Internally, within the FreeRTOS implementation, tasks use two blocks of - * memory. The first block is used to hold the task's data structures. The - * second block is used by the task as its stack. If a task is created using - * xTaskCreate() then both blocks of memory are automatically dynamically - * allocated inside the xTaskCreate() function. (see - * http://www.freertos.org/a00111.html). If a task is created using - * xTaskCreateStatic() then the application writer must provide the required - * memory. xTaskCreateStatic() therefore allows a task to be created without - * using any dynamic memory allocation. - * - * See xTaskCreateStatic() for a version that does not use any dynamic memory - * allocation. - * - * xTaskCreate() can only be used to create a task that has unrestricted - * access to the entire microcontroller memory map. Systems that include MPU - * support can alternatively create an MPU constrained task using - * xTaskCreateRestricted(). - * - * @param pvTaskCode Pointer to the task entry function. Tasks - * must be implemented to never return (i.e. continuous loop). - * - * @param pcName A descriptive name for the task. This is mainly used to - * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default - * is 16. - * - * @param usStackDepth The size of the task stack specified as the number of - * variables the stack can hold - not the number of bytes. For example, if - * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes - * will be allocated for stack storage. - * - * @param pvParameters Pointer that will be used as the parameter for the task - * being created. - * - * @param uxPriority The priority at which the task should run. Systems that - * include MPU support can optionally create tasks in a privileged (system) - * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For - * example, to create a privileged task at priority 2 the uxPriority parameter - * should be set to ( 2 | portPRIVILEGE_BIT ). - * - * @param pvCreatedTask Used to pass back a handle by which the created task - * can be referenced. - * - * @return pdPASS if the task was successfully created and added to a ready - * list, otherwise an error code defined in the file projdefs.h - * - * Example usage: -
- // Task to be created.
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-	 }
- }
-
- // Function that creates a task.
- void vOtherFunction( void )
- {
- static uint8_t ucParameterToPass;
- TaskHandle_t xHandle = NULL;
-
-	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
-	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
-	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
-	 // the new task attempts to access it.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
-	 configASSERT( xHandle );
-
-	 // Use the handle to delete the task.
-	 if( xHandle != NULL )
-	 {
-	 	vTaskDelete( xHandle );
-	 }
- }
-   
- * \defgroup xTaskCreate xTaskCreate - * \ingroup Tasks - */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const configSTACK_DEPTH_TYPE usStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; -#endif - -/** - * task. h - *
- TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
-								 const char * const pcName,
-								 uint32_t ulStackDepth,
-								 void *pvParameters,
-								 UBaseType_t uxPriority,
-								 StackType_t *pxStackBuffer,
-								 StaticTask_t *pxTaskBuffer );
- * - * Create a new task and add it to the list of tasks that are ready to run. - * - * Internally, within the FreeRTOS implementation, tasks use two blocks of - * memory. The first block is used to hold the task's data structures. The - * second block is used by the task as its stack. If a task is created using - * xTaskCreate() then both blocks of memory are automatically dynamically - * allocated inside the xTaskCreate() function. (see - * http://www.freertos.org/a00111.html). If a task is created using - * xTaskCreateStatic() then the application writer must provide the required - * memory. xTaskCreateStatic() therefore allows a task to be created without - * using any dynamic memory allocation. - * - * @param pvTaskCode Pointer to the task entry function. Tasks - * must be implemented to never return (i.e. continuous loop). - * - * @param pcName A descriptive name for the task. This is mainly used to - * facilitate debugging. The maximum length of the string is defined by - * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. - * - * @param ulStackDepth The size of the task stack specified as the number of - * variables the stack can hold - not the number of bytes. For example, if - * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes - * will be allocated for stack storage. - * - * @param pvParameters Pointer that will be used as the parameter for the task - * being created. - * - * @param uxPriority The priority at which the task will run. - * - * @param pxStackBuffer Must point to a StackType_t array that has at least - * ulStackDepth indexes - the array will then be used as the task's stack, - * removing the need for the stack to be allocated dynamically. - * - * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will - * then be used to hold the task's data structures, removing the need for the - * memory to be allocated dynamically. - * - * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will - * be created and a handle to the created task is returned. If either - * pxStackBuffer or pxTaskBuffer are NULL then the task will not be created and - * NULL is returned. - * - * Example usage: -
-
-    // Dimensions the buffer that the task being created will use as its stack.
-    // NOTE:  This is the number of words the stack will hold, not the number of
-    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
-    // then 400 bytes (100 * 32-bits) will be allocated.
-    #define STACK_SIZE 200
-
-    // Structure that will hold the TCB of the task being created.
-    StaticTask_t xTaskBuffer;
-
-    // Buffer that the task being created will use as its stack.  Note this is
-    // an array of StackType_t variables.  The size of StackType_t is dependent on
-    // the RTOS port.
-    StackType_t xStack[ STACK_SIZE ];
-
-    // Function that implements the task being created.
-    void vTaskCode( void * pvParameters )
-    {
-        // The parameter value is expected to be 1 as 1 is passed in the
-        // pvParameters value in the call to xTaskCreateStatic().
-        configASSERT( ( uint32_t ) pvParameters == 1UL );
-
-        for( ;; )
-        {
-            // Task code goes here.
-        }
-    }
-
-    // Function that creates a task.
-    void vOtherFunction( void )
-    {
-        TaskHandle_t xHandle = NULL;
-
-        // Create the task without using any dynamic memory allocation.
-        xHandle = xTaskCreateStatic(
-                      vTaskCode,       // Function that implements the task.
-                      "NAME",          // Text name for the task.
-                      STACK_SIZE,      // Stack size in words, not bytes.
-                      ( void * ) 1,    // Parameter passed into the task.
-                      tskIDLE_PRIORITY,// Priority at which the task is created.
-                      xStack,          // Array to use as the task's stack.
-                      &xTaskBuffer );  // Variable to hold the task's data structure.
-
-        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
-        // been created, and xHandle will be the task's handle.  Use the handle
-        // to suspend the task.
-        vTaskSuspend( xHandle );
-    }
-   
- * \defgroup xTaskCreateStatic xTaskCreateStatic - * \ingroup Tasks - */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION; -#endif /* configSUPPORT_STATIC_ALLOCATION */ - -/** - * task. h - *
- BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
- * - * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. - * - * xTaskCreateRestricted() should only be used in systems that include an MPU - * implementation. - * - * Create a new task and add it to the list of tasks that are ready to run. - * The function parameters define the memory regions and associated access - * permissions allocated to the task. - * - * See xTaskCreateRestrictedStatic() for a version that does not use any - * dynamic memory allocation. - * - * @param pxTaskDefinition Pointer to a structure that contains a member - * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API - * documentation) plus an optional stack buffer and the memory region - * definitions. - * - * @param pxCreatedTask Used to pass back a handle by which the created task - * can be referenced. - * - * @return pdPASS if the task was successfully created and added to a ready - * list, otherwise an error code defined in the file projdefs.h - * - * Example usage: -
-// Create an TaskParameters_t structure that defines the task to be created.
-static const TaskParameters_t xCheckTaskParameters =
-{
-	vATask,		// pvTaskCode - the function that implements the task.
-	"ATask",	// pcName - just a text name for the task to assist debugging.
-	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
-	NULL,		// pvParameters - passed into the task function as the function parameters.
-	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
-	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
-
-	// xRegions - Allocate up to three separate memory regions for access by
-	// the task, with appropriate access permissions.  Different processors have
-	// different memory alignment requirements - refer to the FreeRTOS documentation
-	// for full information.
-	{
-		// Base address					Length	Parameters
-		{ cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
-		{ cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
-		{ cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
-	}
-};
-
-int main( void )
-{
-TaskHandle_t xHandle;
-
-	// Create a task from the const structure defined above.  The task handle
-	// is requested (the second parameter is not NULL) but in this case just for
-	// demonstration purposes as its not actually used.
-	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
-
-	// Start the scheduler.
-	vTaskStartScheduler();
-
-	// Will only get here if there was insufficient memory to create the idle
-	// and/or timer task.
-	for( ;; );
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted - * \ingroup Tasks - */ -#if( portUSING_MPU_WRAPPERS == 1 ) - BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; -#endif - -/** - * task. h - *
- BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
- * - * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. - * - * xTaskCreateRestrictedStatic() should only be used in systems that include an - * MPU implementation. - * - * Internally, within the FreeRTOS implementation, tasks use two blocks of - * memory. The first block is used to hold the task's data structures. The - * second block is used by the task as its stack. If a task is created using - * xTaskCreateRestricted() then the stack is provided by the application writer, - * and the memory used to hold the task's data structure is automatically - * dynamically allocated inside the xTaskCreateRestricted() function. If a task - * is created using xTaskCreateRestrictedStatic() then the application writer - * must provide the memory used to hold the task's data structures too. - * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be - * created without using any dynamic memory allocation. - * - * @param pxTaskDefinition Pointer to a structure that contains a member - * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API - * documentation) plus an optional stack buffer and the memory region - * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure - * contains an additional member, which is used to point to a variable of type - * StaticTask_t - which is then used to hold the task's data structure. - * - * @param pxCreatedTask Used to pass back a handle by which the created task - * can be referenced. - * - * @return pdPASS if the task was successfully created and added to a ready - * list, otherwise an error code defined in the file projdefs.h - * - * Example usage: -
-// Create an TaskParameters_t structure that defines the task to be created.
-// The StaticTask_t variable is only included in the structure when
-// configSUPPORT_STATIC_ALLOCATION is set to 1.  The PRIVILEGED_DATA macro can
-// be used to force the variable into the RTOS kernel's privileged data area.
-static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
-static const TaskParameters_t xCheckTaskParameters =
-{
-	vATask,		// pvTaskCode - the function that implements the task.
-	"ATask",	// pcName - just a text name for the task to assist debugging.
-	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
-	NULL,		// pvParameters - passed into the task function as the function parameters.
-	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
-	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
-
-	// xRegions - Allocate up to three separate memory regions for access by
-	// the task, with appropriate access permissions.  Different processors have
-	// different memory alignment requirements - refer to the FreeRTOS documentation
-	// for full information.
-	{
-		// Base address					Length	Parameters
-		{ cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
-		{ cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
-		{ cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
-	}
-
-	&xTaskBuffer; // Holds the task's data structure.
-};
-
-int main( void )
-{
-TaskHandle_t xHandle;
-
-	// Create a task from the const structure defined above.  The task handle
-	// is requested (the second parameter is not NULL) but in this case just for
-	// demonstration purposes as its not actually used.
-	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
-
-	// Start the scheduler.
-	vTaskStartScheduler();
-
-	// Will only get here if there was insufficient memory to create the idle
-	// and/or timer task.
-	for( ;; );
-}
-   
- * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic - * \ingroup Tasks - */ -#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; -#endif - -/** - * task. h - *
- void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
- * - * Memory regions are assigned to a restricted task when the task is created by - * a call to xTaskCreateRestricted(). These regions can be redefined using - * vTaskAllocateMPURegions(). - * - * @param xTask The handle of the task being updated. - * - * @param xRegions A pointer to an MemoryRegion_t structure that contains the - * new memory region definitions. - * - * Example usage: -
-// Define an array of MemoryRegion_t structures that configures an MPU region
-// allowing read/write access for 1024 bytes starting at the beginning of the
-// ucOneKByte array.  The other two of the maximum 3 definable regions are
-// unused so set to zero.
-static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
-{
-	// Base address		Length		Parameters
-	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
-	{ 0,				0,			0 },
-	{ 0,				0,			0 }
-};
-
-void vATask( void *pvParameters )
-{
-	// This task was created such that it has access to certain regions of
-	// memory as defined by the MPU configuration.  At some point it is
-	// desired that these MPU regions are replaced with that defined in the
-	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
-	// for this purpose.  NULL is used as the task handle to indicate that this
-	// function should modify the MPU regions of the calling task.
-	vTaskAllocateMPURegions( NULL, xAltRegions );
-
-	// Now the task can continue its function, but from this point on can only
-	// access its stack and the ucOneKByte array (unless any other statically
-	// defined or shared regions have been declared elsewhere).
-}
-   
- * \defgroup xTaskCreateRestricted xTaskCreateRestricted - * \ingroup Tasks - */ -void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskDelete( TaskHandle_t xTask );
- * - * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Remove a task from the RTOS real time kernel's management. The task being - * deleted will be removed from all ready, blocked, suspended and event lists. - * - * NOTE: The idle task is responsible for freeing the kernel allocated - * memory from tasks that have been deleted. It is therefore important that - * the idle task is not starved of microcontroller processing time if your - * application makes any calls to vTaskDelete (). Memory allocated by the - * task code is not automatically freed, and should be freed before the task - * is deleted. - * - * See the demo application file death.c for sample code that utilises - * vTaskDelete (). - * - * @param xTask The handle of the task to be deleted. Passing NULL will - * cause the calling task to be deleted. - * - * Example usage: -
- void vOtherFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create the task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // Use the handle to delete the task.
-	 vTaskDelete( xHandle );
- }
-   
- * \defgroup vTaskDelete vTaskDelete - * \ingroup Tasks - */ -void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; - -/*----------------------------------------------------------- - * TASK CONTROL API - *----------------------------------------------------------*/ - -/** - * task. h - *
void vTaskDelay( const TickType_t xTicksToDelay );
- * - * Delay a task for a given number of ticks. The actual time that the - * task remains blocked depends on the tick rate. The constant - * portTICK_PERIOD_MS can be used to calculate real time from the tick - * rate - with the resolution of one tick period. - * - * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * - * vTaskDelay() specifies a time at which the task wishes to unblock relative to - * the time at which vTaskDelay() is called. For example, specifying a block - * period of 100 ticks will cause the task to unblock 100 ticks after - * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method - * of controlling the frequency of a periodic task as the path taken through the - * code, as well as other task and interrupt activity, will effect the frequency - * at which vTaskDelay() gets called and therefore the time at which the task - * next executes. See vTaskDelayUntil() for an alternative API function designed - * to facilitate fixed frequency execution. It does this by specifying an - * absolute time (rather than a relative time) at which the calling task should - * unblock. - * - * @param xTicksToDelay The amount of time, in tick periods, that - * the calling task should block. - * - * Example usage: - - void vTaskFunction( void * pvParameters ) - { - // Block for 500ms. - const TickType_t xDelay = 500 / portTICK_PERIOD_MS; - - for( ;; ) - { - // Simply toggle the LED every 500ms, blocking between each toggle. - vToggleLED(); - vTaskDelay( xDelay ); - } - } - - * \defgroup vTaskDelay vTaskDelay - * \ingroup TaskCtrl - */ -void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
- * - * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Delay a task until a specified time. This function can be used by periodic - * tasks to ensure a constant execution frequency. - * - * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will - * cause a task to block for the specified number of ticks from the time vTaskDelay () is - * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed - * execution frequency as the time between a task starting to execute and that task - * calling vTaskDelay () may not be fixed [the task may take a different path though the - * code between calls, or may get interrupted or preempted a different number of times - * each time it executes]. - * - * Whereas vTaskDelay () specifies a wake time relative to the time at which the function - * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to - * unblock. - * - * The constant portTICK_PERIOD_MS can be used to calculate real time from the tick - * rate - with the resolution of one tick period. - * - * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the - * task was last unblocked. The variable must be initialised with the current time - * prior to its first use (see the example below). Following this the variable is - * automatically updated within vTaskDelayUntil (). - * - * @param xTimeIncrement The cycle time period. The task will be unblocked at - * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the - * same xTimeIncrement parameter value will cause the task to execute with - * a fixed interface period. - * - * Example usage: -
- // Perform an action every 10 ticks.
- void vTaskFunction( void * pvParameters )
- {
- TickType_t xLastWakeTime;
- const TickType_t xFrequency = 10;
-
-	 // Initialise the xLastWakeTime variable with the current time.
-	 xLastWakeTime = xTaskGetTickCount ();
-	 for( ;; )
-	 {
-		 // Wait for the next cycle.
-		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
-
-		 // Perform action here.
-	 }
- }
-   
- * \defgroup vTaskDelayUntil vTaskDelayUntil - * \ingroup TaskCtrl - */ -void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
BaseType_t xTaskAbortDelay( TaskHandle_t xTask );
- * - * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this - * function to be available. - * - * A task will enter the Blocked state when it is waiting for an event. The - * event it is waiting for can be a temporal event (waiting for a time), such - * as when vTaskDelay() is called, or an event on an object, such as when - * xQueueReceive() or ulTaskNotifyTake() is called. If the handle of a task - * that is in the Blocked state is used in a call to xTaskAbortDelay() then the - * task will leave the Blocked state, and return from whichever function call - * placed the task into the Blocked state. - * - * There is no 'FromISR' version of this function as an interrupt would need to - * know which object a task was blocked on in order to know which actions to - * take. For example, if the task was blocked on a queue the interrupt handler - * would then need to know if the queue was locked. - * - * @param xTask The handle of the task to remove from the Blocked state. - * - * @return If the task referenced by xTask was not in the Blocked state then - * pdFAIL is returned. Otherwise pdPASS is returned. - * - * \defgroup xTaskAbortDelay xTaskAbortDelay - * \ingroup TaskCtrl - */ -BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
- * - * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Obtain the priority of any task. - * - * @param xTask Handle of the task to be queried. Passing a NULL - * handle results in the priority of the calling task being returned. - * - * @return The priority of xTask. - * - * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to obtain the priority of the created task.
-	 // It was created with tskIDLE_PRIORITY, but may have changed
-	 // it itself.
-	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
-	 {
-		 // The task has changed it's priority.
-	 }
-
-	 // ...
-
-	 // Is our priority higher than the created task?
-	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
-	 {
-		 // Our priority (obtained using NULL handle) is higher.
-	 }
- }
-   
- * \defgroup uxTaskPriorityGet uxTaskPriorityGet - * \ingroup TaskCtrl - */ -UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask );
- * - * A version of uxTaskPriorityGet() that can be used from an ISR. - */ -UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
eTaskState eTaskGetState( TaskHandle_t xTask );
- * - * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Obtain the state of any task. States are encoded by the eTaskState - * enumerated type. - * - * @param xTask Handle of the task to be queried. - * - * @return The state of xTask at the time the function was called. Note the - * state of the task might change between the function being called, and the - * functions return value being tested by the calling task. - */ -eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState );
- * - * configUSE_TRACE_FACILITY must be defined as 1 for this function to be - * available. See the configuration section for more information. - * - * Populates a TaskStatus_t structure with information about a task. - * - * @param xTask Handle of the task being queried. If xTask is NULL then - * information will be returned about the calling task. - * - * @param pxTaskStatus A pointer to the TaskStatus_t structure that will be - * filled with information about the task referenced by the handle passed using - * the xTask parameter. - * - * @xGetFreeStackSpace The TaskStatus_t structure contains a member to report - * the stack high water mark of the task being queried. Calculating the stack - * high water mark takes a relatively long time, and can make the system - * temporarily unresponsive - so the xGetFreeStackSpace parameter is provided to - * allow the high water mark checking to be skipped. The high watermark value - * will only be written to the TaskStatus_t structure if xGetFreeStackSpace is - * not set to pdFALSE; - * - * @param eState The TaskStatus_t structure contains a member to report the - * state of the task being queried. Obtaining the task state is not as fast as - * a simple assignment - so the eState parameter is provided to allow the state - * information to be omitted from the TaskStatus_t structure. To obtain state - * information then set eState to eInvalid - otherwise the value passed in - * eState will be reported as the task state in the TaskStatus_t structure. - * - * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
- TaskStatus_t xTaskDetails;
-
-    // Obtain the handle of a task from its name.
-    xHandle = xTaskGetHandle( "Task_Name" );
-
-    // Check the handle is not NULL.
-    configASSERT( xHandle );
-
-    // Use the handle to obtain further information about the task.
-    vTaskGetInfo( xHandle,
-                  &xTaskDetails,
-                  pdTRUE, // Include the high water mark in xTaskDetails.
-                  eInvalid ); // Include the task state in xTaskDetails.
- }
-   
- * \defgroup vTaskGetInfo vTaskGetInfo - * \ingroup TaskCtrl - */ -void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
- * - * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Set the priority of any task. - * - * A context switch will occur before the function returns if the priority - * being set is higher than the currently executing task. - * - * @param xTask Handle to the task for which the priority is being set. - * Passing a NULL handle results in the priority of the calling task being set. - * - * @param uxNewPriority The priority to which the task will be set. - * - * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to raise the priority of the created task.
-	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
-
-	 // ...
-
-	 // Use a NULL handle to raise our priority to the same value.
-	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
- }
-   
- * \defgroup vTaskPrioritySet vTaskPrioritySet - * \ingroup TaskCtrl - */ -void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
- * - * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Suspend any task. When suspended a task will never get any microcontroller - * processing time, no matter what its priority. - * - * Calls to vTaskSuspend are not accumulative - - * i.e. calling vTaskSuspend () twice on the same task still only requires one - * call to vTaskResume () to ready the suspended task. - * - * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL - * handle will cause the calling task to be suspended. - * - * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Suspend ourselves.
-	 vTaskSuspend( NULL );
-
-	 // We cannot get here unless another task calls vTaskResume
-	 // with our handle as the parameter.
- }
-   
- * \defgroup vTaskSuspend vTaskSuspend - * \ingroup TaskCtrl - */ -void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskResume( TaskHandle_t xTaskToResume );
- * - * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. - * See the configuration section for more information. - * - * Resumes a suspended task. - * - * A task that has been suspended by one or more calls to vTaskSuspend () - * will be made available for running again by a single call to - * vTaskResume (). - * - * @param xTaskToResume Handle to the task being readied. - * - * Example usage: -
- void vAFunction( void )
- {
- TaskHandle_t xHandle;
-
-	 // Create a task, storing the handle.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
-
-	 // ...
-
-	 // Use the handle to suspend the created task.
-	 vTaskSuspend( xHandle );
-
-	 // ...
-
-	 // The created task will not run during this period, unless
-	 // another task calls vTaskResume( xHandle ).
-
-	 //...
-
-
-	 // Resume the suspended task ourselves.
-	 vTaskResume( xHandle );
-
-	 // The created task will once again get microcontroller processing
-	 // time in accordance with its priority within the system.
- }
-   
- * \defgroup vTaskResume vTaskResume - * \ingroup TaskCtrl - */ -void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
- * - * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be - * available. See the configuration section for more information. - * - * An implementation of vTaskResume() that can be called from within an ISR. - * - * A task that has been suspended by one or more calls to vTaskSuspend () - * will be made available for running again by a single call to - * xTaskResumeFromISR (). - * - * xTaskResumeFromISR() should not be used to synchronise a task with an - * interrupt if there is a chance that the interrupt could arrive prior to the - * task being suspended - as this can lead to interrupts being missed. Use of a - * semaphore as a synchronisation mechanism would avoid this eventuality. - * - * @param xTaskToResume Handle to the task being readied. - * - * @return pdTRUE if resuming the task should result in a context switch, - * otherwise pdFALSE. This is used by the ISR to determine if a context switch - * may be required following the ISR. - * - * \defgroup vTaskResumeFromISR vTaskResumeFromISR - * \ingroup TaskCtrl - */ -BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; - -/*----------------------------------------------------------- - * SCHEDULER CONTROL - *----------------------------------------------------------*/ - -/** - * task. h - *
void vTaskStartScheduler( void );
- * - * Starts the real time kernel tick processing. After calling the kernel - * has control over which tasks are executed and when. - * - * See the demo application file main.c for an example of creating - * tasks and starting the kernel. - * - * Example usage: -
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will not get here unless a task calls vTaskEndScheduler ()
- }
-   
- * - * \defgroup vTaskStartScheduler vTaskStartScheduler - * \ingroup SchedulerControl - */ -void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskEndScheduler( void );
- * - * NOTE: At the time of writing only the x86 real mode port, which runs on a PC - * in place of DOS, implements this function. - * - * Stops the real time kernel tick. All created tasks will be automatically - * deleted and multitasking (either preemptive or cooperative) will - * stop. Execution then resumes from the point where vTaskStartScheduler () - * was called, as if vTaskStartScheduler () had just returned. - * - * See the demo application file main. c in the demo/PC directory for an - * example that uses vTaskEndScheduler (). - * - * vTaskEndScheduler () requires an exit function to be defined within the - * portable layer (see vPortEndScheduler () in port. c for the PC port). This - * performs hardware specific operations such as stopping the kernel tick. - * - * vTaskEndScheduler () will cause all of the resources allocated by the - * kernel to be freed - but will not free resources allocated by application - * tasks. - * - * Example usage: -
- void vTaskCode( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // At some point we want to end the real time kernel processing
-		 // so call ...
-		 vTaskEndScheduler ();
-	 }
- }
-
- void vAFunction( void )
- {
-	 // Create at least one task before starting the kernel.
-	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
-
-	 // Start the real time kernel with preemption.
-	 vTaskStartScheduler ();
-
-	 // Will only get here when the vTaskCode () task has called
-	 // vTaskEndScheduler ().  When we get here we are back to single task
-	 // execution.
- }
-   
- * - * \defgroup vTaskEndScheduler vTaskEndScheduler - * \ingroup SchedulerControl - */ -void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskSuspendAll( void );
- * - * Suspends the scheduler without disabling interrupts. Context switches will - * not occur while the scheduler is suspended. - * - * After calling vTaskSuspendAll () the calling task will continue to execute - * without risk of being swapped out until a call to xTaskResumeAll () has been - * made. - * - * API functions that have the potential to cause a context switch (for example, - * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler - * is suspended. - * - * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the kernel
-		 // tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.
-		 xTaskResumeAll ();
-	 }
- }
-   
- * \defgroup vTaskSuspendAll vTaskSuspendAll - * \ingroup SchedulerControl - */ -void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
BaseType_t xTaskResumeAll( void );
- * - * Resumes scheduler activity after it was suspended by a call to - * vTaskSuspendAll(). - * - * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks - * that were previously suspended by a call to vTaskSuspend(). - * - * @return If resuming the scheduler caused a context switch then pdTRUE is - * returned, otherwise pdFALSE is returned. - * - * Example usage: -
- void vTask1( void * pvParameters )
- {
-	 for( ;; )
-	 {
-		 // Task code goes here.
-
-		 // ...
-
-		 // At some point the task wants to perform a long operation during
-		 // which it does not want to get swapped out.  It cannot use
-		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
-		 // operation may cause interrupts to be missed - including the
-		 // ticks.
-
-		 // Prevent the real time kernel swapping out the task.
-		 vTaskSuspendAll ();
-
-		 // Perform the operation here.  There is no need to use critical
-		 // sections as we have all the microcontroller processing time.
-		 // During this time interrupts will still operate and the real
-		 // time kernel tick count will be maintained.
-
-		 // ...
-
-		 // The operation is complete.  Restart the kernel.  We want to force
-		 // a context switch - but there is no point if resuming the scheduler
-		 // caused a context switch already.
-		 if( !xTaskResumeAll () )
-		 {
-			  taskYIELD ();
-		 }
-	 }
- }
-   
- * \defgroup xTaskResumeAll xTaskResumeAll - * \ingroup SchedulerControl - */ -BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; - -/*----------------------------------------------------------- - * TASK UTILITIES - *----------------------------------------------------------*/ - -/** - * task. h - *
TickType_t xTaskGetTickCount( void );
- * - * @return The count of ticks since vTaskStartScheduler was called. - * - * \defgroup xTaskGetTickCount xTaskGetTickCount - * \ingroup TaskUtils - */ -TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
TickType_t xTaskGetTickCountFromISR( void );
- * - * @return The count of ticks since vTaskStartScheduler was called. - * - * This is a version of xTaskGetTickCount() that is safe to be called from an - * ISR - provided that TickType_t is the natural word size of the - * microcontroller being used or interrupt nesting is either not supported or - * not being used. - * - * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR - * \ingroup TaskUtils - */ -TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
uint16_t uxTaskGetNumberOfTasks( void );
- * - * @return The number of tasks that the real time kernel is currently managing. - * This includes all ready, blocked and suspended tasks. A task that - * has been deleted but not yet freed by the idle task will also be - * included in the count. - * - * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks - * \ingroup TaskUtils - */ -UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
char *pcTaskGetName( TaskHandle_t xTaskToQuery );
- * - * @return The text (human readable) name of the task referenced by the handle - * xTaskToQuery. A task can query its own name by either passing in its own - * handle, or by setting xTaskToQuery to NULL. - * - * \defgroup pcTaskGetName pcTaskGetName - * \ingroup TaskUtils - */ -char *pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - -/** - * task. h - *
TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );
- * - * NOTE: This function takes a relatively long time to complete and should be - * used sparingly. - * - * @return The handle of the task that has the human readable name pcNameToQuery. - * NULL is returned if no matching name is found. INCLUDE_xTaskGetHandle - * must be set to 1 in FreeRTOSConfig.h for pcTaskGetHandle() to be available. - * - * \defgroup pcTaskGetHandle pcTaskGetHandle - * \ingroup TaskUtils - */ -TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - -/** - * task.h - *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
- * - * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for - * this function to be available. - * - * Returns the high water mark of the stack associated with xTask. That is, - * the minimum free stack space there has been (in words, so on a 32 bit machine - * a value of 1 means 4 bytes) since the task started. The smaller the returned - * number the closer the task has come to overflowing its stack. - * - * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the - * same except for their return type. Using configSTACK_DEPTH_TYPE allows the - * user to determine the return type. It gets around the problem of the value - * overflowing on 8-bit types without breaking backward compatibility for - * applications that expect an 8-bit return type. - * - * @param xTask Handle of the task associated with the stack to be checked. - * Set xTask to NULL to check the stack of the calling task. - * - * @return The smallest amount of free stack space there has been (in words, so - * actual spaces on the stack rather than bytes) since the task referenced by - * xTask was created. - */ -UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/** - * task.h - *
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );
- * - * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for - * this function to be available. - * - * Returns the high water mark of the stack associated with xTask. That is, - * the minimum free stack space there has been (in words, so on a 32 bit machine - * a value of 1 means 4 bytes) since the task started. The smaller the returned - * number the closer the task has come to overflowing its stack. - * - * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the - * same except for their return type. Using configSTACK_DEPTH_TYPE allows the - * user to determine the return type. It gets around the problem of the value - * overflowing on 8-bit types without breaking backward compatibility for - * applications that expect an 8-bit return type. - * - * @param xTask Handle of the task associated with the stack to be checked. - * Set xTask to NULL to check the stack of the calling task. - * - * @return The smallest amount of free stack space there has been (in words, so - * actual spaces on the stack rather than bytes) since the task referenced by - * xTask was created. - */ -configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/* When using trace macros it is sometimes necessary to include task.h before -FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, -so the following two prototypes will cause a compilation error. This can be -fixed by simply guarding against the inclusion of these two prototypes unless -they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration -constant. */ -#ifdef configUSE_APPLICATION_TASK_TAG - #if configUSE_APPLICATION_TASK_TAG == 1 - /** - * task.h - *
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
- * - * Sets pxHookFunction to be the task hook function used by the task xTask. - * Passing xTask as NULL has the effect of setting the calling tasks hook - * function. - */ - void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; - - /** - * task.h - *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
- * - * Returns the pxHookFunction value assigned to the task xTask. Do not - * call from an interrupt service routine - call - * xTaskGetApplicationTaskTagFromISR() instead. - */ - TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - - /** - * task.h - *
void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask );
- * - * Returns the pxHookFunction value assigned to the task xTask. Can - * be called from an interrupt service routine. - */ - TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ -#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ - -#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - - /* Each task contains an array of pointers that is dimensioned by the - configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The - kernel does not use the pointers itself, so the application writer can use - the pointers for any purpose they wish. The following two functions are - used to set and query a pointer respectively. */ - void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; - void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; - -#endif - -/** - * task.h - *
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
- * - * Calls the hook function associated with xTask. Passing xTask as NULL has - * the effect of calling the Running tasks (the calling task) hook function. - * - * pvParameter is passed to the hook function for the task to interpret as it - * wants. The return value is the value returned by the task hook function - * registered by the user. - */ -BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; - -/** - * xTaskGetIdleTaskHandle() is only available if - * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. - * - * Simply returns the handle of the idle task. It is not valid to call - * xTaskGetIdleTaskHandle() before the scheduler has been started. - */ -TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; - -/** - * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for - * uxTaskGetSystemState() to be available. - * - * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in - * the system. TaskStatus_t structures contain, among other things, members - * for the task handle, task name, task priority, task state, and total amount - * of run time consumed by the task. See the TaskStatus_t structure - * definition in this file for the full member list. - * - * NOTE: This function is intended for debugging use only as its use results in - * the scheduler remaining suspended for an extended period. - * - * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. - * The array must contain at least one TaskStatus_t structure for each task - * that is under the control of the RTOS. The number of tasks under the control - * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function. - * - * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray - * parameter. The size is specified as the number of indexes in the array, or - * the number of TaskStatus_t structures contained in the array, not by the - * number of bytes in the array. - * - * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in - * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the - * total run time (as defined by the run time stats clock, see - * http://www.freertos.org/rtos-run-time-stats.html) since the target booted. - * pulTotalRunTime can be set to NULL to omit the total run time information. - * - * @return The number of TaskStatus_t structures that were populated by - * uxTaskGetSystemState(). This should equal the number returned by the - * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed - * in the uxArraySize parameter was too small. - * - * Example usage: -
-    // This example demonstrates how a human readable table of run time stats
-	// information is generated from raw data provided by uxTaskGetSystemState().
-	// The human readable table is written to pcWriteBuffer
-	void vTaskGetRunTimeStats( char *pcWriteBuffer )
-	{
-	TaskStatus_t *pxTaskStatusArray;
-	volatile UBaseType_t uxArraySize, x;
-	uint32_t ulTotalRunTime, ulStatsAsPercentage;
-
-		// Make sure the write buffer does not contain a string.
-		*pcWriteBuffer = 0x00;
-
-		// Take a snapshot of the number of tasks in case it changes while this
-		// function is executing.
-		uxArraySize = uxTaskGetNumberOfTasks();
-
-		// Allocate a TaskStatus_t structure for each task.  An array could be
-		// allocated statically at compile time.
-		pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
-
-		if( pxTaskStatusArray != NULL )
-		{
-			// Generate raw status information about each task.
-			uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
-
-			// For percentage calculations.
-			ulTotalRunTime /= 100UL;
-
-			// Avoid divide by zero errors.
-			if( ulTotalRunTime > 0 )
-			{
-				// For each populated position in the pxTaskStatusArray array,
-				// format the raw data as human readable ASCII data
-				for( x = 0; x < uxArraySize; x++ )
-				{
-					// What percentage of the total run time has the task used?
-					// This will always be rounded down to the nearest integer.
-					// ulTotalRunTimeDiv100 has already been divided by 100.
-					ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
-
-					if( ulStatsAsPercentage > 0UL )
-					{
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
-					}
-					else
-					{
-						// If the percentage is zero here then the task has
-						// consumed less than 1% of the total run time.
-						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
-					}
-
-					pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
-				}
-			}
-
-			// The array is no longer needed, free the memory it consumes.
-			vPortFree( pxTaskStatusArray );
-		}
-	}
-	
- */ -UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
void vTaskList( char *pcWriteBuffer );
- * - * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must - * both be defined as 1 for this function to be available. See the - * configuration section of the FreeRTOS.org website for more information. - * - * NOTE 1: This function will disable interrupts for its duration. It is - * not intended for normal application runtime use but as a debug aid. - * - * Lists all the current tasks, along with their current state and stack - * usage high water mark. - * - * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or - * suspended ('S'). - * - * PLEASE NOTE: - * - * This function is provided for convenience only, and is used by many of the - * demo applications. Do not consider it to be part of the scheduler. - * - * vTaskList() calls uxTaskGetSystemState(), then formats part of the - * uxTaskGetSystemState() output into a human readable table that displays task - * names, states and stack usage. - * - * vTaskList() has a dependency on the sprintf() C library function that might - * bloat the code size, use a lot of stack, and provide different results on - * different platforms. An alternative, tiny, third party, and limited - * functionality implementation of sprintf() is provided in many of the - * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note - * printf-stdarg.c does not provide a full snprintf() implementation!). - * - * It is recommended that production systems call uxTaskGetSystemState() - * directly to get access to raw stats data, rather than indirectly through a - * call to vTaskList(). - * - * @param pcWriteBuffer A buffer into which the above mentioned details - * will be written, in ASCII form. This buffer is assumed to be large - * enough to contain the generated report. Approximately 40 bytes per - * task should be sufficient. - * - * \defgroup vTaskList vTaskList - * \ingroup TaskUtils - */ -void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - -/** - * task. h - *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
- * - * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS - * must both be defined as 1 for this function to be available. The application - * must also then provide definitions for - * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() - * to configure a peripheral timer/counter and return the timers current count - * value respectively. The counter should be at least 10 times the frequency of - * the tick count. - * - * NOTE 1: This function will disable interrupts for its duration. It is - * not intended for normal application runtime use but as a debug aid. - * - * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total - * accumulated execution time being stored for each task. The resolution - * of the accumulated time value depends on the frequency of the timer - * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. - * Calling vTaskGetRunTimeStats() writes the total execution time of each - * task into a buffer, both as an absolute count value and as a percentage - * of the total system execution time. - * - * NOTE 2: - * - * This function is provided for convenience only, and is used by many of the - * demo applications. Do not consider it to be part of the scheduler. - * - * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the - * uxTaskGetSystemState() output into a human readable table that displays the - * amount of time each task has spent in the Running state in both absolute and - * percentage terms. - * - * vTaskGetRunTimeStats() has a dependency on the sprintf() C library function - * that might bloat the code size, use a lot of stack, and provide different - * results on different platforms. An alternative, tiny, third party, and - * limited functionality implementation of sprintf() is provided in many of the - * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note - * printf-stdarg.c does not provide a full snprintf() implementation!). - * - * It is recommended that production systems call uxTaskGetSystemState() directly - * to get access to raw stats data, rather than indirectly through a call to - * vTaskGetRunTimeStats(). - * - * @param pcWriteBuffer A buffer into which the execution times will be - * written, in ASCII form. This buffer is assumed to be large enough to - * contain the generated report. Approximately 40 bytes per task should - * be sufficient. - * - * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats - * \ingroup TaskUtils - */ -void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - -/** -* task. h -*
uint32_t ulTaskGetIdleRunTimeCounter( void );
-* -* configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS -* must both be defined as 1 for this function to be available. The application -* must also then provide definitions for -* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() -* to configure a peripheral timer/counter and return the timers current count -* value respectively. The counter should be at least 10 times the frequency of -* the tick count. -* -* Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total -* accumulated execution time being stored for each task. The resolution -* of the accumulated time value depends on the frequency of the timer -* configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. -* While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total -* execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() -* returns the total execution time of just the idle task. -* -* @return The total run time of the idle task. This is the amount of time the -* idle task has actually been executing. The unit of time is dependent on the -* frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and -* portGET_RUN_TIME_COUNTER_VALUE() macros. -* -* \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter -* \ingroup TaskUtils -*/ -uint32_t ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
- * - * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this - * function to be available. - * - * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private - * "notification value", which is a 32-bit unsigned integer (uint32_t). - * - * Events can be sent to a task using an intermediary object. Examples of such - * objects are queues, semaphores, mutexes and event groups. Task notifications - * are a method of sending an event directly to a task without the need for such - * an intermediary object. - * - * A notification sent to a task can optionally perform an action, such as - * update, overwrite or increment the task's notification value. In that way - * task notifications can be used to send data to a task, or be used as light - * weight and fast binary or counting semaphores. - * - * A notification sent to a task will remain pending until it is cleared by the - * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was - * already in the Blocked state to wait for a notification when the notification - * arrives then the task will automatically be removed from the Blocked state - * (unblocked) and the notification cleared. - * - * A task can use xTaskNotifyWait() to [optionally] block to wait for a - * notification to be pending, or ulTaskNotifyTake() to [optionally] block - * to wait for its notification value to have a non-zero value. The task does - * not consume any CPU time while it is in the Blocked state. - * - * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. - * - * @param xTaskToNotify The handle of the task being notified. The handle to a - * task can be returned from the xTaskCreate() API function used to create the - * task, and the handle of the currently running task can be obtained by calling - * xTaskGetCurrentTaskHandle(). - * - * @param ulValue Data that can be sent with the notification. How the data is - * used depends on the value of the eAction parameter. - * - * @param eAction Specifies how the notification updates the task's notification - * value, if at all. Valid values for eAction are as follows: - * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. - * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. - * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. - * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. - * - * pulPreviousNotificationValue - - * Can be used to pass out the subject task's notification value before any - * bits are modified by the notify function. - * - * @return Dependent on the value of eAction. See the description of the - * eAction parameter. - * - * \defgroup xTaskNotify xTaskNotify - * \ingroup TaskNotifications - */ -BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; -#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL ) -#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) - -/** - * task. h - *
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
- * - * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this - * function to be available. - * - * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private - * "notification value", which is a 32-bit unsigned integer (uint32_t). - * - * A version of xTaskNotify() that can be used from an interrupt service routine - * (ISR). - * - * Events can be sent to a task using an intermediary object. Examples of such - * objects are queues, semaphores, mutexes and event groups. Task notifications - * are a method of sending an event directly to a task without the need for such - * an intermediary object. - * - * A notification sent to a task can optionally perform an action, such as - * update, overwrite or increment the task's notification value. In that way - * task notifications can be used to send data to a task, or be used as light - * weight and fast binary or counting semaphores. - * - * A notification sent to a task will remain pending until it is cleared by the - * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was - * already in the Blocked state to wait for a notification when the notification - * arrives then the task will automatically be removed from the Blocked state - * (unblocked) and the notification cleared. - * - * A task can use xTaskNotifyWait() to [optionally] block to wait for a - * notification to be pending, or ulTaskNotifyTake() to [optionally] block - * to wait for its notification value to have a non-zero value. The task does - * not consume any CPU time while it is in the Blocked state. - * - * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. - * - * @param xTaskToNotify The handle of the task being notified. The handle to a - * task can be returned from the xTaskCreate() API function used to create the - * task, and the handle of the currently running task can be obtained by calling - * xTaskGetCurrentTaskHandle(). - * - * @param ulValue Data that can be sent with the notification. How the data is - * used depends on the value of the eAction parameter. - * - * @param eAction Specifies how the notification updates the task's notification - * value, if at all. Valid values for eAction are as follows: - * - * eSetBits - - * The task's notification value is bitwise ORed with ulValue. xTaskNofify() - * always returns pdPASS in this case. - * - * eIncrement - - * The task's notification value is incremented. ulValue is not used and - * xTaskNotify() always returns pdPASS in this case. - * - * eSetValueWithOverwrite - - * The task's notification value is set to the value of ulValue, even if the - * task being notified had not yet processed the previous notification (the - * task already had a notification pending). xTaskNotify() always returns - * pdPASS in this case. - * - * eSetValueWithoutOverwrite - - * If the task being notified did not already have a notification pending then - * the task's notification value is set to ulValue and xTaskNotify() will - * return pdPASS. If the task being notified already had a notification - * pending then no action is performed and pdFAIL is returned. - * - * eNoAction - - * The task receives a notification without its notification value being - * updated. ulValue is not used and xTaskNotify() always returns pdPASS in - * this case. - * - * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set - * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the - * task to which the notification was sent to leave the Blocked state, and the - * unblocked task has a priority higher than the currently running task. If - * xTaskNotifyFromISR() sets this value to pdTRUE then a context switch should - * be requested before the interrupt is exited. How a context switch is - * requested from an ISR is dependent on the port - see the documentation page - * for the port in use. - * - * @return Dependent on the value of eAction. See the description of the - * eAction parameter. - * - * \defgroup xTaskNotify xTaskNotify - * \ingroup TaskNotifications - */ -BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; -#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) -#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) - -/** - * task. h - *
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
- * - * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this - * function to be available. - * - * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private - * "notification value", which is a 32-bit unsigned integer (uint32_t). - * - * Events can be sent to a task using an intermediary object. Examples of such - * objects are queues, semaphores, mutexes and event groups. Task notifications - * are a method of sending an event directly to a task without the need for such - * an intermediary object. - * - * A notification sent to a task can optionally perform an action, such as - * update, overwrite or increment the task's notification value. In that way - * task notifications can be used to send data to a task, or be used as light - * weight and fast binary or counting semaphores. - * - * A notification sent to a task will remain pending until it is cleared by the - * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was - * already in the Blocked state to wait for a notification when the notification - * arrives then the task will automatically be removed from the Blocked state - * (unblocked) and the notification cleared. - * - * A task can use xTaskNotifyWait() to [optionally] block to wait for a - * notification to be pending, or ulTaskNotifyTake() to [optionally] block - * to wait for its notification value to have a non-zero value. The task does - * not consume any CPU time while it is in the Blocked state. - * - * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. - * - * @param ulBitsToClearOnEntry Bits that are set in ulBitsToClearOnEntry value - * will be cleared in the calling task's notification value before the task - * checks to see if any notifications are pending, and optionally blocks if no - * notifications are pending. Setting ulBitsToClearOnEntry to ULONG_MAX (if - * limits.h is included) or 0xffffffffUL (if limits.h is not included) will have - * the effect of resetting the task's notification value to 0. Setting - * ulBitsToClearOnEntry to 0 will leave the task's notification value unchanged. - * - * @param ulBitsToClearOnExit If a notification is pending or received before - * the calling task exits the xTaskNotifyWait() function then the task's - * notification value (see the xTaskNotify() API function) is passed out using - * the pulNotificationValue parameter. Then any bits that are set in - * ulBitsToClearOnExit will be cleared in the task's notification value (note - * *pulNotificationValue is set before any bits are cleared). Setting - * ulBitsToClearOnExit to ULONG_MAX (if limits.h is included) or 0xffffffffUL - * (if limits.h is not included) will have the effect of resetting the task's - * notification value to 0 before the function exits. Setting - * ulBitsToClearOnExit to 0 will leave the task's notification value unchanged - * when the function exits (in which case the value passed out in - * pulNotificationValue will match the task's notification value). - * - * @param pulNotificationValue Used to pass the task's notification value out - * of the function. Note the value passed out will not be effected by the - * clearing of any bits caused by ulBitsToClearOnExit being non-zero. - * - * @param xTicksToWait The maximum amount of time that the task should wait in - * the Blocked state for a notification to be received, should a notification - * not already be pending when xTaskNotifyWait() was called. The task - * will not consume any processing time while it is in the Blocked state. This - * is specified in kernel ticks, the macro pdMS_TO_TICSK( value_in_ms ) can be - * used to convert a time specified in milliseconds to a time specified in - * ticks. - * - * @return If a notification was received (including notifications that were - * already pending when xTaskNotifyWait was called) then pdPASS is - * returned. Otherwise pdFAIL is returned. - * - * \defgroup xTaskNotifyWait xTaskNotifyWait - * \ingroup TaskNotifications - */ -BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
- * - * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro - * to be available. - * - * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private - * "notification value", which is a 32-bit unsigned integer (uint32_t). - * - * Events can be sent to a task using an intermediary object. Examples of such - * objects are queues, semaphores, mutexes and event groups. Task notifications - * are a method of sending an event directly to a task without the need for such - * an intermediary object. - * - * A notification sent to a task can optionally perform an action, such as - * update, overwrite or increment the task's notification value. In that way - * task notifications can be used to send data to a task, or be used as light - * weight and fast binary or counting semaphores. - * - * xTaskNotifyGive() is a helper macro intended for use when task notifications - * are used as light weight and faster binary or counting semaphore equivalents. - * Actual FreeRTOS semaphores are given using the xSemaphoreGive() API function, - * the equivalent action that instead uses a task notification is - * xTaskNotifyGive(). - * - * When task notifications are being used as a binary or counting semaphore - * equivalent then the task being notified should wait for the notification - * using the ulTaskNotificationTake() API function rather than the - * xTaskNotifyWait() API function. - * - * See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details. - * - * @param xTaskToNotify The handle of the task being notified. The handle to a - * task can be returned from the xTaskCreate() API function used to create the - * task, and the handle of the currently running task can be obtained by calling - * xTaskGetCurrentTaskHandle(). - * - * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the - * eAction parameter set to eIncrement - so pdPASS is always returned. - * - * \defgroup xTaskNotifyGive xTaskNotifyGive - * \ingroup TaskNotifications - */ -#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL ) - -/** - * task. h - *
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
- *
- * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
- * to be available.
- *
- * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private
- * "notification value", which is a 32-bit unsigned integer (uint32_t).
- *
- * A version of xTaskNotifyGive() that can be called from an interrupt service
- * routine (ISR).
- *
- * Events can be sent to a task using an intermediary object.  Examples of such
- * objects are queues, semaphores, mutexes and event groups.  Task notifications
- * are a method of sending an event directly to a task without the need for such
- * an intermediary object.
- *
- * A notification sent to a task can optionally perform an action, such as
- * update, overwrite or increment the task's notification value.  In that way
- * task notifications can be used to send data to a task, or be used as light
- * weight and fast binary or counting semaphores.
- *
- * vTaskNotifyGiveFromISR() is intended for use when task notifications are
- * used as light weight and faster binary or counting semaphore equivalents.
- * Actual FreeRTOS semaphores are given from an ISR using the
- * xSemaphoreGiveFromISR() API function, the equivalent action that instead uses
- * a task notification is vTaskNotifyGiveFromISR().
- *
- * When task notifications are being used as a binary or counting semaphore
- * equivalent then the task being notified should wait for the notification
- * using the ulTaskNotificationTake() API function rather than the
- * xTaskNotifyWait() API function.
- *
- * See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details.
- *
- * @param xTaskToNotify The handle of the task being notified.  The handle to a
- * task can be returned from the xTaskCreate() API function used to create the
- * task, and the handle of the currently running task can be obtained by calling
- * xTaskGetCurrentTaskHandle().
- *
- * @param pxHigherPriorityTaskWoken  vTaskNotifyGiveFromISR() will set
- * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the
- * task to which the notification was sent to leave the Blocked state, and the
- * unblocked task has a priority higher than the currently running task.  If
- * vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch
- * should be requested before the interrupt is exited.  How a context switch is
- * requested from an ISR is dependent on the port - see the documentation page
- * for the port in use.
- *
- * \defgroup xTaskNotifyWait xTaskNotifyWait
- * \ingroup TaskNotifications
- */
-void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
-
-/**
- * task. h
- * 
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
- * - * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this - * function to be available. - * - * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private - * "notification value", which is a 32-bit unsigned integer (uint32_t). - * - * Events can be sent to a task using an intermediary object. Examples of such - * objects are queues, semaphores, mutexes and event groups. Task notifications - * are a method of sending an event directly to a task without the need for such - * an intermediary object. - * - * A notification sent to a task can optionally perform an action, such as - * update, overwrite or increment the task's notification value. In that way - * task notifications can be used to send data to a task, or be used as light - * weight and fast binary or counting semaphores. - * - * ulTaskNotifyTake() is intended for use when a task notification is used as a - * faster and lighter weight binary or counting semaphore alternative. Actual - * FreeRTOS semaphores are taken using the xSemaphoreTake() API function, the - * equivalent action that instead uses a task notification is - * ulTaskNotifyTake(). - * - * When a task is using its notification value as a binary or counting semaphore - * other tasks should send notifications to it using the xTaskNotifyGive() - * macro, or xTaskNotify() function with the eAction parameter set to - * eIncrement. - * - * ulTaskNotifyTake() can either clear the task's notification value to - * zero on exit, in which case the notification value acts like a binary - * semaphore, or decrement the task's notification value on exit, in which case - * the notification value acts like a counting semaphore. - * - * A task can use ulTaskNotifyTake() to [optionally] block to wait for a - * the task's notification value to be non-zero. The task does not consume any - * CPU time while it is in the Blocked state. - * - * Where as xTaskNotifyWait() will return when a notification is pending, - * ulTaskNotifyTake() will return when the task's notification value is - * not zero. - * - * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. - * - * @param xClearCountOnExit if xClearCountOnExit is pdFALSE then the task's - * notification value is decremented when the function exits. In this way the - * notification value acts like a counting semaphore. If xClearCountOnExit is - * not pdFALSE then the task's notification value is cleared to zero when the - * function exits. In this way the notification value acts like a binary - * semaphore. - * - * @param xTicksToWait The maximum amount of time that the task should wait in - * the Blocked state for the task's notification value to be greater than zero, - * should the count not already be greater than zero when - * ulTaskNotifyTake() was called. The task will not consume any processing - * time while it is in the Blocked state. This is specified in kernel ticks, - * the macro pdMS_TO_TICSK( value_in_ms ) can be used to convert a time - * specified in milliseconds to a time specified in ticks. - * - * @return The task's notification count before it is either cleared to zero or - * decremented (see the xClearCountOnExit parameter). - * - * \defgroup ulTaskNotifyTake ulTaskNotifyTake - * \ingroup TaskNotifications - */ -uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * task. h - *
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
- * - * If the notification state of the task referenced by the handle xTask is - * eNotified, then set the task's notification state to eNotWaitingNotification. - * The task's notification value is not altered. Set xTask to NULL to clear the - * notification state of the calling task. - * - * @return pdTRUE if the task's notification state was set to - * eNotWaitingNotification, otherwise pdFALSE. - * \defgroup xTaskNotifyStateClear xTaskNotifyStateClear - * \ingroup TaskNotifications - */ -BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); - -/** -* task. h -*
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
-* -* Clears the bits specified by the ulBitsToClear bit mask in the notification -* value of the task referenced by xTask. -* -* Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear -* the notification value to 0. Set ulBitsToClear to 0 to query the task's -* notification value without clearing any bits. -* -* @return The value of the target task's notification value before the bits -* specified by ulBitsToClear were cleared. -* \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear -* \ingroup TaskNotifications -*/ -uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; - -/** - * task.h - *
void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
- * - * Capture the current time for future use with xTaskCheckForTimeOut(). - * - * @param pxTimeOut Pointer to a timeout object into which the current time - * is to be captured. The captured time includes the tick count and the number - * of times the tick count has overflowed since the system first booted. - * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState - * \ingroup TaskCtrl - */ -void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; - -/** - * task.h - *
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
- * - * Determines if pxTicksToWait ticks has passed since a time was captured - * using a call to vTaskSetTimeOutState(). The captured time includes the tick - * count and the number of times the tick count has overflowed. - * - * @param pxTimeOut The time status as captured previously using - * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated - * to reflect the current time status. - * @param pxTicksToWait The number of ticks to check for timeout i.e. if - * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by - * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. - * If the timeout has not occurred, pxTIcksToWait is updated to reflect the - * number of remaining ticks. - * - * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is - * returned and pxTicksToWait is updated to reflect the number of remaining - * ticks. - * - * @see https://www.freertos.org/xTaskCheckForTimeOut.html - * - * Example Usage: - *
-	// Driver library function used to receive uxWantedBytes from an Rx buffer
-	// that is filled by a UART interrupt. If there are not enough bytes in the
-	// Rx buffer then the task enters the Blocked state until it is notified that
-	// more data has been placed into the buffer. If there is still not enough
-	// data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
-	// is used to re-calculate the Block time to ensure the total amount of time
-	// spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
-	// continues until either the buffer contains at least uxWantedBytes bytes,
-	// or the total amount of time spent in the Blocked state reaches
-	// MAX_TIME_TO_WAIT – at which point the task reads however many bytes are
-	// available up to a maximum of uxWantedBytes.
-
-	size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
-	{
-	size_t uxReceived = 0;
-	TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
-	TimeOut_t xTimeOut;
-
-		// Initialize xTimeOut.  This records the time at which this function
-		// was entered.
-		vTaskSetTimeOutState( &xTimeOut );
-
-		// Loop until the buffer contains the wanted number of bytes, or a
-		// timeout occurs.
-		while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
-		{
-			// The buffer didn't contain enough data so this task is going to
-			// enter the Blocked state. Adjusting xTicksToWait to account for
-			// any time that has been spent in the Blocked state within this
-			// function so far to ensure the total amount of time spent in the
-			// Blocked state does not exceed MAX_TIME_TO_WAIT.
-			if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
-			{
-				//Timed out before the wanted number of bytes were available,
-				// exit the loop.
-				break;
-			}
-
-			// Wait for a maximum of xTicksToWait ticks to be notified that the
-			// receive interrupt has placed more data into the buffer.
-			ulTaskNotifyTake( pdTRUE, xTicksToWait );
-		}
-
-		// Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
-		// The actual number of bytes read (which might be less than
-		// uxWantedBytes) is returned.
-		uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
-													pucBuffer,
-													uxWantedBytes );
-
-		return uxReceived;
-	}
- 
- * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut - * \ingroup TaskCtrl - */ -BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; - -/*----------------------------------------------------------- - * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES - *----------------------------------------------------------*/ - -/* - * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY - * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS - * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. - * - * Called from the real time kernel tick (either preemptive or cooperative), - * this increments the tick count and checks if any tasks that are blocked - * for a finite period required removing from a blocked list and placing on - * a ready list. If a non-zero value is returned then a context switch is - * required because either: - * + A task was removed from a blocked list because its timeout had expired, - * or - * + Time slicing is in use and there is a task of equal priority to the - * currently running task. - */ -BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; - -/* - * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN - * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. - * - * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. - * - * Removes the calling task from the ready list and places it both - * on the list of tasks waiting for a particular event, and the - * list of delayed tasks. The task will be removed from both lists - * and replaced on the ready list should either the event occur (and - * there be no higher priority tasks waiting on the same event) or - * the delay period expires. - * - * The 'unordered' version replaces the event list item value with the - * xItemValue value, and inserts the list item at the end of the list. - * - * The 'ordered' version uses the existing event list item value (which is the - * owning tasks priority) to insert the list item into the event list is task - * priority order. - * - * @param pxEventList The list containing tasks that are blocked waiting - * for the event to occur. - * - * @param xItemValue The item value to use for the event list item when the - * event list is not ordered by task priority. - * - * @param xTicksToWait The maximum amount of time that the task should wait - * for the event to occur. This is specified in kernel ticks,the constant - * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time - * period. - */ -void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; -void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/* - * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN - * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. - * - * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. - * - * This function performs nearly the same function as vTaskPlaceOnEventList(). - * The difference being that this function does not permit tasks to block - * indefinitely, whereas vTaskPlaceOnEventList() does. - * - */ -void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; - -/* - * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN - * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. - * - * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. - * - * Removes a task from both the specified event list and the list of blocked - * tasks, and places it on a ready queue. - * - * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called - * if either an event occurs to unblock a task, or the block timeout period - * expires. - * - * xTaskRemoveFromEventList() is used when the event list is in task priority - * order. It removes the list item from the head of the event list as that will - * have the highest priority owning task of all the tasks on the event list. - * vTaskRemoveFromUnorderedEventList() is used when the event list is not - * ordered and the event list items hold something other than the owning tasks - * priority. In this case the event list item value is updated to the value - * passed in the xItemValue parameter. - * - * @return pdTRUE if the task being removed has a higher priority than the task - * making the call, otherwise pdFALSE. - */ -BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; -void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; - -/* - * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY - * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS - * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. - * - * Sets the pointer to the current TCB to the TCB of the highest priority task - * that is ready to run. - */ -portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; - -/* - * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY - * THE EVENT BITS MODULE. - */ -TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; - -/* - * Return the handle of the calling task. - */ -TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; - -/* - * Shortcut used by the queue implementation to prevent unnecessary call to - * taskYIELD(); - */ -void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; - -/* - * Returns the scheduler state as taskSCHEDULER_RUNNING, - * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. - */ -BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; - -/* - * Raises the priority of the mutex holder to that of the calling task should - * the mutex holder have a priority less than the calling task. - */ -BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; - -/* - * Set the priority of a task back to its proper priority in the case that it - * inherited a higher priority while it was holding a semaphore. - */ -BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; - -/* - * If a higher priority task attempting to obtain a mutex caused a lower - * priority task to inherit the higher priority task's priority - but the higher - * priority task then timed out without obtaining the mutex, then the lower - * priority task will disinherit the priority again - but only down as far as - * the highest priority task that is still waiting for the mutex (if there were - * more than one task waiting for the mutex). - */ -void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; - -/* - * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. - */ -UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; - -/* - * Set the uxTaskNumber of the task referenced by the xTask parameter to - * uxHandle. - */ -void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; - -/* - * Only available when configUSE_TICKLESS_IDLE is set to 1. - * If tickless mode is being used, or a low power mode is implemented, then - * the tick interrupt will not execute during idle periods. When this is the - * case, the tick count value maintained by the scheduler needs to be kept up - * to date with the actual execution time by being skipped forward by a time - * equal to the idle period. - */ -void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; - -/* Correct the tick count value after the application code has held -interrupts disabled for an extended period. xTicksToCatchUp is the number -of tick interrupts that have been missed due to interrupts being disabled. -Its value is not computed automatically, so must be computed by the -application writer. - -This function is similar to vTaskStepTick(), however, unlike -vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a -time at which a task should be removed from the blocked state. That means -tasks may have to be removed from the blocked state as the tick count is -moved. */ -BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; - -/* - * Only available when configUSE_TICKLESS_IDLE is set to 1. - * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port - * specific sleep function to determine if it is ok to proceed with the sleep, - * and if it is ok to proceed, if it is ok to sleep indefinitely. - * - * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only - * called with the scheduler suspended, not from within a critical section. It - * is therefore possible for an interrupt to request a context switch between - * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being - * entered. eTaskConfirmSleepModeStatus() should be called from a short - * critical section between the timer being stopped and the sleep mode being - * entered to ensure it is ok to proceed into the sleep mode. - */ -eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; - -/* - * For internal use only. Increment the mutex held count when a mutex is - * taken and return the handle of the task that has taken the mutex. - */ -TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; - -/* - * For internal use only. Same as vTaskSetTimeOutState(), but without a critial - * section. - */ -void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; - - -#ifdef __cplusplus -} -#endif -#endif /* INC_TASK_H */ - - - +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + + +#ifndef INC_TASK_H +#define INC_TASK_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "list.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- +* MACROS AND DEFINITIONS +*----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V10.4.1" +#define tskKERNEL_VERSION_MAJOR 10 +#define tskKERNEL_VERSION_MINOR 4 +#define tskKERNEL_VERSION_BUILD 1 + +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) +#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) + +/* The direct to task notification feature used to have only a single notification + * per task. Now there is an array of notifications per task that is dimensioned by + * configTASK_NOTIFICATION_ARRAY_ENTRIES. For backward compatibility, any use of the + * original direct to task notification defaults to using the first index in the + * array. */ +#define tskDEFAULT_INDEX_TO_NOTIFY ( 0 ) + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an TaskHandle_t variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \defgroup TaskHandle_t TaskHandle_t + * \ingroup Tasks + */ +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock * TaskHandle_t; + +/* + * Defines the prototype to which the application task hook function must + * conform. + */ +typedef BaseType_t (* TaskHookFunction_t)( void * ); + +/* Task states returned by eTaskGetState. */ +typedef enum +{ + eRunning = 0, /* A task is querying the state of itself, so must be running. */ + eReady, /* The task being queried is in a read or pending ready list. */ + eBlocked, /* The task being queried is in the Blocked state. */ + eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ + eInvalid /* Used as an 'invalid state' value. */ +} eTaskState; + +/* Actions that can be performed when vTaskNotify() is called. */ +typedef enum +{ + eNoAction = 0, /* Notify the task without updating its notify value. */ + eSetBits, /* Set bits in the task's notification value. */ + eIncrement, /* Increment the task's notification value. */ + eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ + eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ +} eNotifyAction; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + BaseType_t xOverflowCount; + TickType_t xTimeOnEntering; +} TimeOut_t; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void * pvBaseAddress; + uint32_t ulLengthInBytes; + uint32_t ulParameters; +} MemoryRegion_t; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMETERS +{ + TaskFunction_t pvTaskCode; + const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + configSTACK_DEPTH_TYPE usStackDepth; + void * pvParameters; + UBaseType_t uxPriority; + StackType_t * puxStackBuffer; + MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; + #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; + #endif +} TaskParameters_t; + +/* Used with the uxTaskGetSystemState() function to return the state of each task + * in the system. */ +typedef struct xTASK_STATUS +{ + TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ + const char * pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /* A number unique to the task. */ + eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See https://www.FreeRTOS.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t * pxStackBase; /* Points to the lowest address of the task's stack area. */ + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ +} TaskStatus_t; + +/* Possible return values for eTaskConfirmSleepModeStatus(). */ +typedef enum +{ + eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ + eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ +} eSleepModeStatus; + +/** + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \defgroup taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() +#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() +#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is + * 0 to generate more optimal code when configASSERT() is defined as the constant + * is used in assert() statements. */ +#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) +#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) +#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) + + +/*----------------------------------------------------------- +* TASK CREATION API +*----------------------------------------------------------*/ + +/** + * task. h + *
+ * BaseType_t xTaskCreate(
+ *                            TaskFunction_t pvTaskCode,
+ *                            const char * const pcName,
+ *                            configSTACK_DEPTH_TYPE usStackDepth,
+ *                            void *pvParameters,
+ *                            UBaseType_t uxPriority,
+ *                            TaskHandle_t *pvCreatedTask
+ *                        );
+ * 
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + *
+ * // Task to be created.
+ * void vTaskCode( void * pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *       // Task code goes here.
+ *   }
+ * }
+ *
+ * // Function that creates a task.
+ * void vOtherFunction( void )
+ * {
+ * static uint8_t ucParameterToPass;
+ * TaskHandle_t xHandle = NULL;
+ *
+ *   // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
+ *   // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
+ *   // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
+ *   // the new task attempts to access it.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+ *   configASSERT( xHandle );
+ *
+ *   // Use the handle to delete the task.
+ *   if( xHandle != NULL )
+ *   {
+ *      vTaskDelete( xHandle );
+ *   }
+ * }
+ * 
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ * TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
+ *                               const char * const pcName,
+ *                               uint32_t ulStackDepth,
+ *                               void *pvParameters,
+ *                               UBaseType_t uxPriority,
+ *                               StackType_t *pxStackBuffer,
+ *                               StaticTask_t *pxTaskBuffer );
+ * 
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and a handle to the created task is returned. If either + * pxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. + * + * Example usage: + *
+ *
+ *  // Dimensions the buffer that the task being created will use as its stack.
+ *  // NOTE:  This is the number of words the stack will hold, not the number of
+ *  // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
+ *  // then 400 bytes (100 * 32-bits) will be allocated.
+ #define STACK_SIZE 200
+ *
+ *  // Structure that will hold the TCB of the task being created.
+ *  StaticTask_t xTaskBuffer;
+ *
+ *  // Buffer that the task being created will use as its stack.  Note this is
+ *  // an array of StackType_t variables.  The size of StackType_t is dependent on
+ *  // the RTOS port.
+ *  StackType_t xStack[ STACK_SIZE ];
+ *
+ *  // Function that implements the task being created.
+ *  void vTaskCode( void * pvParameters )
+ *  {
+ *      // The parameter value is expected to be 1 as 1 is passed in the
+ *      // pvParameters value in the call to xTaskCreateStatic().
+ *      configASSERT( ( uint32_t ) pvParameters == 1UL );
+ *
+ *      for( ;; )
+ *      {
+ *          // Task code goes here.
+ *      }
+ *  }
+ *
+ *  // Function that creates a task.
+ *  void vOtherFunction( void )
+ *  {
+ *      TaskHandle_t xHandle = NULL;
+ *
+ *      // Create the task without using any dynamic memory allocation.
+ *      xHandle = xTaskCreateStatic(
+ *                    vTaskCode,       // Function that implements the task.
+ *                    "NAME",          // Text name for the task.
+ *                    STACK_SIZE,      // Stack size in words, not bytes.
+ *                    ( void * ) 1,    // Parameter passed into the task.
+ *                    tskIDLE_PRIORITY,// Priority at which the task is created.
+ *                    xStack,          // Array to use as the task's stack.
+ *                    &xTaskBuffer );  // Variable to hold the task's data structure.
+ *
+ *      // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
+ *      // been created, and xHandle will be the task's handle.  Use the handle
+ *      // to suspend the task.
+ *      vTaskSuspend( xHandle );
+ *  }
+ * 
+ * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * task. h + *
+ * BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * 
+ * + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + *
+ * // Create an TaskParameters_t structure that defines the task to be created.
+ * static const TaskParameters_t xCheckTaskParameters =
+ * {
+ *  vATask,     // pvTaskCode - the function that implements the task.
+ *  "ATask",    // pcName - just a text name for the task to assist debugging.
+ *  100,        // usStackDepth - the stack size DEFINED IN WORDS.
+ *  NULL,       // pvParameters - passed into the task function as the function parameters.
+ *  ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+ *  cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+ *
+ *  // xRegions - Allocate up to three separate memory regions for access by
+ *  // the task, with appropriate access permissions.  Different processors have
+ *  // different memory alignment requirements - refer to the FreeRTOS documentation
+ *  // for full information.
+ *  {
+ *      // Base address                 Length  Parameters
+ *      { cReadWriteArray,              32,     portMPU_REGION_READ_WRITE },
+ *      { cReadOnlyArray,               32,     portMPU_REGION_READ_ONLY },
+ *      { cPrivilegedOnlyAccessArray,   128,    portMPU_REGION_PRIVILEGED_READ_WRITE }
+ *  }
+ * };
+ *
+ * int main( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *  // Create a task from the const structure defined above.  The task handle
+ *  // is requested (the second parameter is not NULL) but in this case just for
+ *  // demonstration purposes as its not actually used.
+ *  xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+ *
+ *  // Start the scheduler.
+ *  vTaskStartScheduler();
+ *
+ *  // Will only get here if there was insufficient memory to create the idle
+ *  // and/or timer task.
+ *  for( ;; );
+ * }
+ * 
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#if ( portUSING_MPU_WRAPPERS == 1 ) + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ * BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * 
+ * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + *
+ * // Create an TaskParameters_t structure that defines the task to be created.
+ * // The StaticTask_t variable is only included in the structure when
+ * // configSUPPORT_STATIC_ALLOCATION is set to 1.  The PRIVILEGED_DATA macro can
+ * // be used to force the variable into the RTOS kernel's privileged data area.
+ * static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
+ * static const TaskParameters_t xCheckTaskParameters =
+ * {
+ *  vATask,     // pvTaskCode - the function that implements the task.
+ *  "ATask",    // pcName - just a text name for the task to assist debugging.
+ *  100,        // usStackDepth - the stack size DEFINED IN WORDS.
+ *  NULL,       // pvParameters - passed into the task function as the function parameters.
+ *  ( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+ *  cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+ *
+ *  // xRegions - Allocate up to three separate memory regions for access by
+ *  // the task, with appropriate access permissions.  Different processors have
+ *  // different memory alignment requirements - refer to the FreeRTOS documentation
+ *  // for full information.
+ *  {
+ *      // Base address                 Length  Parameters
+ *      { cReadWriteArray,              32,     portMPU_REGION_READ_WRITE },
+ *      { cReadOnlyArray,               32,     portMPU_REGION_READ_ONLY },
+ *      { cPrivilegedOnlyAccessArray,   128,    portMPU_REGION_PRIVILEGED_READ_WRITE }
+ *  }
+ *
+ *  &xTaskBuffer; // Holds the task's data structure.
+ * };
+ *
+ * int main( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *  // Create a task from the const structure defined above.  The task handle
+ *  // is requested (the second parameter is not NULL) but in this case just for
+ *  // demonstration purposes as its not actually used.
+ *  xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+ *
+ *  // Start the scheduler.
+ *  vTaskStartScheduler();
+ *
+ *  // Will only get here if there was insufficient memory to create the idle
+ *  // and/or timer task.
+ *  for( ;; );
+ * }
+ * 
+ * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ * void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
+ * 
+ * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param xRegions A pointer to an MemoryRegion_t structure that contains the + * new memory region definitions. + * + * Example usage: + *
+ * // Define an array of MemoryRegion_t structures that configures an MPU region
+ * // allowing read/write access for 1024 bytes starting at the beginning of the
+ * // ucOneKByte array.  The other two of the maximum 3 definable regions are
+ * // unused so set to zero.
+ * static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
+ * {
+ *  // Base address     Length      Parameters
+ *  { ucOneKByte,       1024,       portMPU_REGION_READ_WRITE },
+ *  { 0,                0,          0 },
+ *  { 0,                0,          0 }
+ * };
+ *
+ * void vATask( void *pvParameters )
+ * {
+ *  // This task was created such that it has access to certain regions of
+ *  // memory as defined by the MPU configuration.  At some point it is
+ *  // desired that these MPU regions are replaced with that defined in the
+ *  // xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
+ *  // for this purpose.  NULL is used as the task handle to indicate that this
+ *  // function should modify the MPU regions of the calling task.
+ *  vTaskAllocateMPURegions( NULL, xAltRegions );
+ *
+ *  // Now the task can continue its function, but from this point on can only
+ *  // access its stack and the ucOneKByte array (unless any other statically
+ *  // defined or shared regions have been declared elsewhere).
+ * }
+ * 
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( TaskHandle_t xTask, + const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskDelete( TaskHandle_t xTask );
+ * 
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernel's management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param xTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: + *
+ * void vOtherFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *   // Create the task, storing the handle.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+ *
+ *   // Use the handle to delete the task.
+ *   vTaskDelete( xHandle );
+ * }
+ * 
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- +* TASK CONTROL API +*----------------------------------------------------------*/ + +/** + * task. h + *
+ * void vTaskDelay( const TickType_t xTicksToDelay );
+ * 
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a periodic task as the path taken through the + * code, as well as other task and interrupt activity, will effect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See vTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + * + * void vTaskFunction( void * pvParameters ) + * { + * // Block for 500ms. + * const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Simply toggle the LED every 500ms, blocking between each toggle. + * vToggleLED(); + * vTaskDelay( xDelay ); + * } + * } + * + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
+ * 
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: + *
+ * // Perform an action every 10 ticks.
+ * void vTaskFunction( void * pvParameters )
+ * {
+ * TickType_t xLastWakeTime;
+ * const TickType_t xFrequency = 10;
+ *
+ *   // Initialise the xLastWakeTime variable with the current time.
+ *   xLastWakeTime = xTaskGetTickCount ();
+ *   for( ;; )
+ *   {
+ *       // Wait for the next cycle.
+ *       vTaskDelayUntil( &xLastWakeTime, xFrequency );
+ *
+ *       // Perform action here.
+ *   }
+ * }
+ * 
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * BaseType_t xTaskAbortDelay( TaskHandle_t xTask );
+ * 
+ * + * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this + * function to be available. + * + * A task will enter the Blocked state when it is waiting for an event. The + * event it is waiting for can be a temporal event (waiting for a time), such + * as when vTaskDelay() is called, or an event on an object, such as when + * xQueueReceive() or ulTaskNotifyTake() is called. If the handle of a task + * that is in the Blocked state is used in a call to xTaskAbortDelay() then the + * task will leave the Blocked state, and return from whichever function call + * placed the task into the Blocked state. + * + * There is no 'FromISR' version of this function as an interrupt would need to + * know which object a task was blocked on in order to know which actions to + * take. For example, if the task was blocked on a queue the interrupt handler + * would then need to know if the queue was locked. + * + * @param xTask The handle of the task to remove from the Blocked state. + * + * @return If the task referenced by xTask was not in the Blocked state then + * pdFAIL is returned. Otherwise pdPASS is returned. + * + * \defgroup xTaskAbortDelay xTaskAbortDelay + * \ingroup TaskCtrl + */ +BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
+ * 
+ * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *   // Create a task, storing the handle.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+ *
+ *   // ...
+ *
+ *   // Use the handle to obtain the priority of the created task.
+ *   // It was created with tskIDLE_PRIORITY, but may have changed
+ *   // it itself.
+ *   if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+ *   {
+ *       // The task has changed it's priority.
+ *   }
+ *
+ *   // ...
+ *
+ *   // Is our priority higher than the created task?
+ *   if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+ *   {
+ *       // Our priority (obtained using NULL handle) is higher.
+ *   }
+ * }
+ * 
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask );
+ * 
+ * + * A version of uxTaskPriorityGet() that can be used from an ISR. + */ +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * eTaskState eTaskGetState( TaskHandle_t xTask );
+ * 
+ * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the eTaskState + * enumerated type. + * + * @param xTask Handle of the task to be queried. + * + * @return The state of xTask at the time the function was called. Note the + * state of the task might change between the function being called, and the + * functions return value being tested by the calling task. + */ +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState );
+ * 
+ * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Populates a TaskStatus_t structure with information about a task. + * + * @param xTask Handle of the task being queried. If xTask is NULL then + * information will be returned about the calling task. + * + * @param pxTaskStatus A pointer to the TaskStatus_t structure that will be + * filled with information about the task referenced by the handle passed using + * the xTask parameter. + * + * @xGetFreeStackSpace The TaskStatus_t structure contains a member to report + * the stack high water mark of the task being queried. Calculating the stack + * high water mark takes a relatively long time, and can make the system + * temporarily unresponsive - so the xGetFreeStackSpace parameter is provided to + * allow the high water mark checking to be skipped. The high watermark value + * will only be written to the TaskStatus_t structure if xGetFreeStackSpace is + * not set to pdFALSE; + * + * @param eState The TaskStatus_t structure contains a member to report the + * state of the task being queried. Obtaining the task state is not as fast as + * a simple assignment - so the eState parameter is provided to allow the state + * information to be omitted from the TaskStatus_t structure. To obtain state + * information then set eState to eInvalid - otherwise the value passed in + * eState will be reported as the task state in the TaskStatus_t structure. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ * TaskStatus_t xTaskDetails;
+ *
+ *  // Obtain the handle of a task from its name.
+ *  xHandle = xTaskGetHandle( "Task_Name" );
+ *
+ *  // Check the handle is not NULL.
+ *  configASSERT( xHandle );
+ *
+ *  // Use the handle to obtain further information about the task.
+ *  vTaskGetInfo( xHandle,
+ *                &xTaskDetails,
+ *                pdTRUE, // Include the high water mark in xTaskDetails.
+ *                eInvalid ); // Include the task state in xTaskDetails.
+ * }
+ * 
+ * \defgroup vTaskGetInfo vTaskGetInfo + * \ingroup TaskCtrl + */ +void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
+ * 
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param xTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *   // Create a task, storing the handle.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+ *
+ *   // ...
+ *
+ *   // Use the handle to raise the priority of the created task.
+ *   vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+ *
+ *   // ...
+ *
+ *   // Use a NULL handle to raise our priority to the same value.
+ *   vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ * }
+ * 
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskSuspend( TaskHandle_t xTaskToSuspend );
+ * 
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *   // Create a task, storing the handle.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+ *
+ *   // ...
+ *
+ *   // Use the handle to suspend the created task.
+ *   vTaskSuspend( xHandle );
+ *
+ *   // ...
+ *
+ *   // The created task will not run during this period, unless
+ *   // another task calls vTaskResume( xHandle ).
+ *
+ *   //...
+ *
+ *
+ *   // Suspend ourselves.
+ *   vTaskSuspend( NULL );
+ *
+ *   // We cannot get here unless another task calls vTaskResume
+ *   // with our handle as the parameter.
+ * }
+ * 
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskResume( TaskHandle_t xTaskToResume );
+ * 
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param xTaskToResume Handle to the task being readied. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ * TaskHandle_t xHandle;
+ *
+ *   // Create a task, storing the handle.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+ *
+ *   // ...
+ *
+ *   // Use the handle to suspend the created task.
+ *   vTaskSuspend( xHandle );
+ *
+ *   // ...
+ *
+ *   // The created task will not run during this period, unless
+ *   // another task calls vTaskResume( xHandle ).
+ *
+ *   //...
+ *
+ *
+ *   // Resume the suspended task ourselves.
+ *   vTaskResume( xHandle );
+ *
+ *   // The created task will once again get microcontroller processing
+ *   // time in accordance with its priority within the system.
+ * }
+ * 
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
+ * 
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * xTaskResumeFromISR() should not be used to synchronise a task with an + * interrupt if there is a chance that the interrupt could arrive prior to the + * task being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @param xTaskToResume Handle to the task being readied. + * + * @return pdTRUE if resuming the task should result in a context switch, + * otherwise pdFALSE. This is used by the ISR to determine if a context switch + * may be required following the ISR. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- +* SCHEDULER CONTROL +*----------------------------------------------------------*/ + +/** + * task. h + *
+ * void vTaskStartScheduler( void );
+ * 
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: + *
+ * void vAFunction( void )
+ * {
+ *   // Create at least one task before starting the kernel.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+ *
+ *   // Start the real time kernel with preemption.
+ *   vTaskStartScheduler ();
+ *
+ *   // Will not get here unless a task calls vTaskEndScheduler ()
+ * }
+ * 
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskEndScheduler( void );
+ * 
+ * + * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: + *
+ * void vTaskCode( void * pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *       // Task code goes here.
+ *
+ *       // At some point we want to end the real time kernel processing
+ *       // so call ...
+ *       vTaskEndScheduler ();
+ *   }
+ * }
+ *
+ * void vAFunction( void )
+ * {
+ *   // Create at least one task before starting the kernel.
+ *   xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+ *
+ *   // Start the real time kernel with preemption.
+ *   vTaskStartScheduler ();
+ *
+ *   // Will only get here when the vTaskCode () task has called
+ *   // vTaskEndScheduler ().  When we get here we are back to single task
+ *   // execution.
+ * }
+ * 
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * void vTaskSuspendAll( void );
+ * 
+ * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: + *
+ * void vTask1( void * pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *       // Task code goes here.
+ *
+ *       // ...
+ *
+ *       // At some point the task wants to perform a long operation during
+ *       // which it does not want to get swapped out.  It cannot use
+ *       // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+ *       // operation may cause interrupts to be missed - including the
+ *       // ticks.
+ *
+ *       // Prevent the real time kernel swapping out the task.
+ *       vTaskSuspendAll ();
+ *
+ *       // Perform the operation here.  There is no need to use critical
+ *       // sections as we have all the microcontroller processing time.
+ *       // During this time interrupts will still operate and the kernel
+ *       // tick count will be maintained.
+ *
+ *       // ...
+ *
+ *       // The operation is complete.  Restart the kernel.
+ *       xTaskResumeAll ();
+ *   }
+ * }
+ * 
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
+ * BaseType_t xTaskResumeAll( void );
+ * 
+ * + * Resumes scheduler activity after it was suspended by a call to + * vTaskSuspendAll(). + * + * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks + * that were previously suspended by a call to vTaskSuspend(). + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: + *
+ * void vTask1( void * pvParameters )
+ * {
+ *   for( ;; )
+ *   {
+ *       // Task code goes here.
+ *
+ *       // ...
+ *
+ *       // At some point the task wants to perform a long operation during
+ *       // which it does not want to get swapped out.  It cannot use
+ *       // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+ *       // operation may cause interrupts to be missed - including the
+ *       // ticks.
+ *
+ *       // Prevent the real time kernel swapping out the task.
+ *       vTaskSuspendAll ();
+ *
+ *       // Perform the operation here.  There is no need to use critical
+ *       // sections as we have all the microcontroller processing time.
+ *       // During this time interrupts will still operate and the real
+ *       // time kernel tick count will be maintained.
+ *
+ *       // ...
+ *
+ *       // The operation is complete.  Restart the kernel.  We want to force
+ *       // a context switch - but there is no point if resuming the scheduler
+ *       // caused a context switch already.
+ *       if( !xTaskResumeAll () )
+ *       {
+ *            taskYIELD ();
+ *       }
+ *   }
+ * }
+ * 
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- +* TASK UTILITIES +*----------------------------------------------------------*/ + +/** + * task. h + *
TickType_t xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \defgroup xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
TickType_t xTaskGetTickCountFromISR( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
uint16_t uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
char *pcTaskGetName( TaskHandle_t xTaskToQuery );
+ * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQuery. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. + * + * \defgroup pcTaskGetName pcTaskGetName + * \ingroup TaskUtils + */ +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );
+ * + * NOTE: This function takes a relatively long time to complete and should be + * used sparingly. + * + * @return The handle of the task that has the human readable name pcNameToQuery. + * NULL is returned if no matching name is found. INCLUDE_xTaskGetHandle + * must be set to 1 in FreeRTOSConfig.h for pcTaskGetHandle() to be available. + * + * \defgroup pcTaskGetHandle pcTaskGetHandle + * \ingroup TaskUtils + */ +TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task.h + *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* When using trace macros it is sometimes necessary to include task.h before + * FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, + * so the following two prototypes will cause a compilation error. This can be + * fixed by simply guarding against the inclusion of these two prototypes unless + * they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration + * constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + +/** + * task.h + *
+ * void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
+ * 
+ * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
+ * void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
+ * 
+ * + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. + */ + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
+ * void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask );
+ * 
+ * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. + */ + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + +/* Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The + * kernel does not use the pointers itself, so the application writer can use + * the pointers for any purpose they wish. The following two functions are + * used to set and query a pointer respectively. */ + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) PRIVILEGED_FUNCTION; + void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) PRIVILEGED_FUNCTION; + +#endif + +#if ( configCHECK_FOR_STACK_OVERFLOW > 0 ) + + /** + * task.h + *
void vApplicationStackOverflowHook( TaskHandle_t xTask char *pcTaskName); 
+ * + * The application stack overflow hook is called when a stack overflow is detected for a task. + * + * Details on stack overflow detection can be found here: https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html + * + * @param xTask the task that just exceeded its stack boundaries. + * @param pcTaskName A character string containing the name of the offending task. + */ + void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ); + +#endif + +#if ( configUSE_TICK_HOOK > 0 ) + /** + * task.h + *
void vApplicationTickHook( void ); 
+ * + * This hook function is called in the system tick handler after any OS work is completed. + */ + void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an application callback. */ + +#endif + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + /** + * task.h + *
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, StackType_t ** ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) 
+ * + * This function is used to provide a statically allocated block of memory to FreeRTOS to hold the Idle Task TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * @param ppxIdleTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxIdleTaskStackBuffer A handle to a statically allocated Stack buffer for thie idle task + * @param pulIdleTaskStackSize A pointer to the number of elements that will fit in the allocated stack buffer + */ + void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + uint32_t * pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is an application callback. */ +#endif + +/** + * task.h + *
+ * BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
+ * 
+ * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. The return value is the value returned by the task hook function + * registered by the user. + */ +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) PRIVILEGED_FUNCTION; + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * xTaskGetIdleTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for + * uxTaskGetSystemState() to be available. + * + * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in + * the system. TaskStatus_t structures contain, among other things, members + * for the task handle, task name, task priority, task state, and total amount + * of run time consumed by the task. See the TaskStatus_t structure + * definition in this file for the full member list. + * + * NOTE: This function is intended for debugging use only as its use results in + * the scheduler remaining suspended for an extended period. + * + * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. + * The array must contain at least one TaskStatus_t structure for each task + * that is under the control of the RTOS. The number of tasks under the control + * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function. + * + * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray + * parameter. The size is specified as the number of indexes in the array, or + * the number of TaskStatus_t structures contained in the array, not by the + * number of bytes in the array. + * + * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in + * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the + * total run time (as defined by the run time stats clock, see + * https://www.FreeRTOS.org/rtos-run-time-stats.html) since the target booted. + * pulTotalRunTime can be set to NULL to omit the total run time information. + * + * @return The number of TaskStatus_t structures that were populated by + * uxTaskGetSystemState(). This should equal the number returned by the + * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed + * in the uxArraySize parameter was too small. + * + * Example usage: + *
+ *  // This example demonstrates how a human readable table of run time stats
+ *  // information is generated from raw data provided by uxTaskGetSystemState().
+ *  // The human readable table is written to pcWriteBuffer
+ *  void vTaskGetRunTimeStats( char *pcWriteBuffer )
+ *  {
+ *  TaskStatus_t *pxTaskStatusArray;
+ *  volatile UBaseType_t uxArraySize, x;
+ *  uint32_t ulTotalRunTime, ulStatsAsPercentage;
+ *
+ *      // Make sure the write buffer does not contain a string.
+ * pcWriteBuffer = 0x00;
+ *
+ *      // Take a snapshot of the number of tasks in case it changes while this
+ *      // function is executing.
+ *      uxArraySize = uxTaskGetNumberOfTasks();
+ *
+ *      // Allocate a TaskStatus_t structure for each task.  An array could be
+ *      // allocated statically at compile time.
+ *      pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
+ *
+ *      if( pxTaskStatusArray != NULL )
+ *      {
+ *          // Generate raw status information about each task.
+ *          uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
+ *
+ *          // For percentage calculations.
+ *          ulTotalRunTime /= 100UL;
+ *
+ *          // Avoid divide by zero errors.
+ *          if( ulTotalRunTime > 0 )
+ *          {
+ *              // For each populated position in the pxTaskStatusArray array,
+ *              // format the raw data as human readable ASCII data
+ *              for( x = 0; x < uxArraySize; x++ )
+ *              {
+ *                  // What percentage of the total run time has the task used?
+ *                  // This will always be rounded down to the nearest integer.
+ *                  // ulTotalRunTimeDiv100 has already been divided by 100.
+ *                  ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
+ *
+ *                  if( ulStatsAsPercentage > 0UL )
+ *                  {
+ *                      sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
+ *                  }
+ *                  else
+ *                  {
+ *                      // If the percentage is zero here then the task has
+ *                      // consumed less than 1% of the total run time.
+ *                      sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
+ *                  }
+ *
+ *                  pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
+ *              }
+ *          }
+ *
+ *          // The array is no longer needed, free the memory it consumes.
+ *          vPortFree( pxTaskStatusArray );
+ *      }
+ *  }
+ *  
+ */ +UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskList( char *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays task + * names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that might + * bloat the code size, use a lot of stack, and provide different results on + * different platforms. An alternative, tiny, third party, and limited + * functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly through a + * call to vTaskList(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \defgroup vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays the + * amount of time each task has spent in the Running state in both absolute and + * percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library function + * that might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, and + * limited functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() directly + * to get access to raw stats data, rather than indirectly through a call to + * vTaskGetRunTimeStats(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
uint32_t ulTaskGetIdleRunTimeCounter( void );
+ * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total + * execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() + * returns the total execution time of just the idle task. + * + * @return The total run time of the idle task. This is the amount of time the + * idle task has actually been executing. The unit of time is dependent on the + * frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() macros. + * + * \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + * \ingroup TaskUtils + */ +uint32_t ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction );
+ *
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Sends a direct to task notification to a task, with an optional value and + * action. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] block + * to wait for a notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotify() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotify() is equivalent to calling xTaskNotifyIndexed() + * with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotify() does + * not have this parameter and always sends notifications to index 0. + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The target notification value is bitwise ORed with ulValue. + * xTaskNofifyIndexed() always returns pdPASS in this case. + * + * eIncrement - + * The target notification value is incremented. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The target notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification at the + * same array index (the task already had a notification pending at that index). + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending at the + * same array index then the target notification value is set to ulValue and + * xTaskNotifyIndexed() will return pdPASS. If the task being notified already + * had a notification pending at the same array index then no action is + * performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification at the specified array index without the + * notification value at that index being updated. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * pulPreviousNotificationValue - + * Can be used to pass out the subject task's notification value before any + * bits are modified by the notify function. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexed xTaskNotifyIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; +#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL ) +#define xTaskNotifyIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL ) + +/** + * task. h + *
BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
+ *
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue );
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexed() performs the same operation as + * xTaskNotifyIndexed() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than when the function returns) in the additional + * pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQuery() performs the same operation as xTaskNotify() with the + * addition that it also returns the subject task's prior notification value + * (the notification value as it was at the time the function is called, rather + * than when the function returns) in the additional pulPreviousNotifyValue + * parameter. + * + * \defgroup xTaskNotifyAndQueryIndexed xTaskNotifyAndQueryIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) +#define xTaskNotifyAndQueryIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) + +/** + * task. h + *
BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
+ *
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * A version of xTaskNotifyIndexed() that can be used from an interrupt service + * routine (ISR). + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] block + * to wait for a notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyFromISR() is equivalent to calling + * xTaskNotifyIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyFromISR() + * does not have this parameter and always sends notifications to index 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. + * + * eIncrement - + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. + * + * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * xTaskNotifyFromISR() sets this value to pdTRUE then a context switch should + * be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexedFromISR xTaskNotifyIndexedFromISR + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + *
BaseType_t xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken );
+ *
BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken );
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexedFromISR() performs the same operation as + * xTaskNotifyIndexedFromISR() with the addition that it also returns the + * subject task's prior notification value (the notification value at the time + * the function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQueryFromISR() performs the same operation as + * xTaskNotifyFromISR() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * \defgroup xTaskNotifyAndQueryIndexedFromISR xTaskNotifyAndQueryIndexedFromISR + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQueryIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + *
+ * BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
+ *
+ * BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
+ * 
+ * + * Waits for a direct to task notification to be pending at a given index within + * an array of direct to task notifications. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] block + * to wait for a notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyWait() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyWait() is equivalent to calling + * xTaskNotifyWaitIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be received. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyWait() does + * not have this parameter and always waits for notifications on index 0. + * + * @param ulBitsToClearOnEntry Bits that are set in ulBitsToClearOnEntry value + * will be cleared in the calling task's notification value before the task + * checks to see if any notifications are pending, and optionally blocks if no + * notifications are pending. Setting ulBitsToClearOnEntry to ULONG_MAX (if + * limits.h is included) or 0xffffffffUL (if limits.h is not included) will have + * the effect of resetting the task's notification value to 0. Setting + * ulBitsToClearOnEntry to 0 will leave the task's notification value unchanged. + * + * @param ulBitsToClearOnExit If a notification is pending or received before + * the calling task exits the xTaskNotifyWait() function then the task's + * notification value (see the xTaskNotify() API function) is passed out using + * the pulNotificationValue parameter. Then any bits that are set in + * ulBitsToClearOnExit will be cleared in the task's notification value (note + * *pulNotificationValue is set before any bits are cleared). Setting + * ulBitsToClearOnExit to ULONG_MAX (if limits.h is included) or 0xffffffffUL + * (if limits.h is not included) will have the effect of resetting the task's + * notification value to 0 before the function exits. Setting + * ulBitsToClearOnExit to 0 will leave the task's notification value unchanged + * when the function exits (in which case the value passed out in + * pulNotificationValue will match the task's notification value). + * + * @param pulNotificationValue Used to pass the task's notification value out + * of the function. Note the value passed out will not be effected by the + * clearing of any bits caused by ulBitsToClearOnExit being non-zero. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for a notification to be received, should a notification + * not already be pending when xTaskNotifyWait() was called. The task + * will not consume any processing time while it is in the Blocked state. This + * is specified in kernel ticks, the macro pdMS_TO_TICSK( value_in_ms ) can be + * used to convert a time specified in milliseconds to a time specified in + * ticks. + * + * @return If a notification was received (including notifications that were + * already pending when xTaskNotifyWait was called) then pdPASS is + * returned. Otherwise pdFAIL is returned. + * + * \defgroup xTaskNotifyWaitIndexed xTaskNotifyWaitIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \ + xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) ) +#define xTaskNotifyWaitIndexed( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \ + xTaskGenericNotifyWait( ( uxIndexToWaitOn ), ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) ) + +/** + * task. h + *
BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify );
+ *
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
+ * + * Sends a direct to task notification to a particular index in the target + * task's notification array in a manner similar to giving a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * macros to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * xTaskNotifyGiveIndexed() is a helper macro intended for use when task + * notifications are used as light weight and faster binary or counting + * semaphore equivalents. Actual FreeRTOS semaphores are given using the + * xSemaphoreGive() API function, the equivalent action that instead uses a task + * notification is xTaskNotifyGiveIndexed(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotificationTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyGive() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyGive() is equivalent to calling + * xTaskNotifyGiveIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyGive() + * does not have this parameter and always sends notifications to index 0. + * + * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the + * eAction parameter set to eIncrement - so pdPASS is always returned. + * + * \defgroup xTaskNotifyGiveIndexed xTaskNotifyGiveIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyGive( xTaskToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL ) +#define xTaskNotifyGiveIndexed( xTaskToNotify, uxIndexToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( 0 ), eIncrement, NULL ) + +/** + * task. h + *
void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken );
+ *
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
+ * + * A version of xTaskNotifyGiveIndexed() that can be called from an interrupt + * service routine (ISR). + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro + * to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * vTaskNotifyGiveIndexedFromISR() is intended for use when task notifications + * are used as light weight and faster binary or counting semaphore equivalents. + * Actual FreeRTOS semaphores are given from an ISR using the + * xSemaphoreGiveFromISR() API function, the equivalent action that instead uses + * a task notification is vTaskNotifyGiveIndexedFromISR(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotificationTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyGiveFromISR() is equivalent to calling + * xTaskNotifyGiveIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * xTaskNotifyGiveFromISR() does not have this parameter and always sends + * notifications to index 0. + * + * @param pxHigherPriorityTaskWoken vTaskNotifyGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch + * should be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * \defgroup vTaskNotifyGiveIndexedFromISR vTaskNotifyGiveIndexedFromISR + * \ingroup TaskNotifications + */ +void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( pxHigherPriorityTaskWoken ) ); +#define vTaskNotifyGiveIndexedFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( pxHigherPriorityTaskWoken ) ); + +/** + * task. h + *
+ * uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
+ * 
+ * uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
+ * 
+ * + * Waits for a direct to task notification on a particular index in the calling + * task's notification array in a manner similar to taking a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * ulTaskNotifyTakeIndexed() is intended for use when a task notification is + * used as a faster and lighter weight binary or counting semaphore alternative. + * Actual FreeRTOS semaphores are taken using the xSemaphoreTake() API function, + * the equivalent action that instead uses a task notification is + * ulTaskNotifyTakeIndexed(). + * + * When a task is using its notification value as a binary or counting semaphore + * other tasks should send notifications to it using the xTaskNotifyGiveIndexed() + * macro, or xTaskNotifyIndex() function with the eAction parameter set to + * eIncrement. + * + * ulTaskNotifyTakeIndexed() can either clear the task's notification value at + * the array index specified by the uxIndexToWaitOn parameter to zero on exit, + * in which case the notification value acts like a binary semaphore, or + * decrement the notification value on exit, in which case the notification + * value acts like a counting semaphore. + * + * A task can use ulTaskNotifyTakeIndexed() to [optionally] block to wait for + * the task's notification value to be non-zero. The task does not consume any + * CPU time while it is in the Blocked state. + * + * Where as xTaskNotifyWaitIndexed() will return when a notification is pending, + * ulTaskNotifyTakeIndexed() will return when the task's notification value is + * not zero. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyTake() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling ulTaskNotifyTake() is equivalent to calling + * ulTaskNotifyTakeIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be non-zero. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyTake() does + * not have this parameter and always waits for notifications on index 0. + * + * @param xClearCountOnExit if xClearCountOnExit is pdFALSE then the task's + * notification value is decremented when the function exits. In this way the + * notification value acts like a counting semaphore. If xClearCountOnExit is + * not pdFALSE then the task's notification value is cleared to zero when the + * function exits. In this way the notification value acts like a binary + * semaphore. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for the task's notification value to be greater than zero, + * should the count not already be greater than zero when + * ulTaskNotifyTake() was called. The task will not consume any processing + * time while it is in the Blocked state. This is specified in kernel ticks, + * the macro pdMS_TO_TICSK( value_in_ms ) can be used to convert a time + * specified in milliseconds to a time specified in ticks. + * + * @return The task's notification count before it is either cleared to zero or + * decremented (see the xClearCountOnExit parameter). + * + * \defgroup ulTaskNotifyTakeIndexed ulTaskNotifyTakeIndexed + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \ + ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) ) +#define ulTaskNotifyTakeIndexed( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ) \ + ulTaskGenericNotifyTake( ( uxIndexToWaitOn ), ( xClearCountOnExit ), ( xTicksToWait ) ) + +/** + * task. h + *
+ * BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToCLear );
+ * 
+ * BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
+ * 
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * If a notification is sent to an index within the array of notifications then + * the notification at that index is said to be 'pending' until it is read or + * explicitly cleared by the receiving task. xTaskNotifyStateClearIndexed() + * is the function that clears a pending notification without reading the + * notification value. The notification value at the same array index is not + * altered. Set xTask to NULL to clear the notification state of the calling + * task. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyStateClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyStateClear() is equivalent to calling + * xTaskNotifyStateClearIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have a notification state + * cleared. Set xTask to NULL to clear a notification state in the calling + * task. To obtain a task's handle create the task using xTaskCreate() and + * make use of the pxCreatedTask parameter, or create the task using + * xTaskCreateStatic() and store the returned value, or use the task's name in + * a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values to act upon. For example, setting uxIndexToClear to 1 + * will clear the state of the notification at index 1 within the array. + * uxIndexToClear must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyStateClear() does not have this parameter and always acts on the + * notification at index 0. + * + * @return pdTRUE if the task's notification state was set to + * eNotWaitingNotification, otherwise pdFALSE. + * + * \defgroup xTaskNotifyStateClearIndexed xTaskNotifyStateClearIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) PRIVILEGED_FUNCTION; +#define xTaskNotifyStateClear( xTask ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ) ) +#define xTaskNotifyStateClearIndexed( xTask, uxIndexToClear ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( uxIndexToClear ) ) + +/** + * task. h + *
+ * uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear, uint32_t ulBitsToClear );
+ * 
+ * uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
+ * 
+ * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * ulTaskNotifyValueClearIndexed() clears the bits specified by the + * ulBitsToClear bit mask in the notification value at array index uxIndexToClear + * of the task referenced by xTask. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyValueClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling ulTaskNotifyValueClear() is equivalent to calling + * ulTaskNotifyValueClearIndexed() with the uxIndexToClear parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have bits in one of its + * notification values cleared. Set xTask to NULL to clear bits in a + * notification value of the calling task. To obtain a task's handle create the + * task using xTaskCreate() and make use of the pxCreatedTask parameter, or + * create the task using xTaskCreateStatic() and store the returned value, or + * use the task's name in a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values in which to clear the bits. uxIndexToClear + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyValueClear() does not have this parameter and always clears bits + * in the notification value at index 0. + * + * @param ulBitsToClear Bit mask of the bits to clear in the notification value of + * xTask. Set a bit to 1 to clear the corresponding bits in the task's notification + * value. Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear + * the notification value to 0. Set ulBitsToClear to 0 to query the task's + * notification value without clearing any bits. + * + * + * @return The value of the target task's notification value before the bits + * specified by ulBitsToClear were cleared. + * \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; +#define ulTaskNotifyValueClear( xTask, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulBitsToClear ) ) +#define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), ( uxIndexToClear ), ( ulBitsToClear ) ) + +/** + * task.h + *
+ * void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut );
+ * 
+ * + * Capture the current time for future use with xTaskCheckForTimeOut(). + * + * @param pxTimeOut Pointer to a timeout object into which the current time + * is to be captured. The captured time includes the tick count and the number + * of times the tick count has overflowed since the system first booted. + * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState + * \ingroup TaskCtrl + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
+ * BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
+ * 
+ * + * Determines if pxTicksToWait ticks has passed since a time was captured + * using a call to vTaskSetTimeOutState(). The captured time includes the tick + * count and the number of times the tick count has overflowed. + * + * @param pxTimeOut The time status as captured previously using + * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated + * to reflect the current time status. + * @param pxTicksToWait The number of ticks to check for timeout i.e. if + * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by + * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. + * If the timeout has not occurred, pxTIcksToWait is updated to reflect the + * number of remaining ticks. + * + * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is + * returned and pxTicksToWait is updated to reflect the number of remaining + * ticks. + * + * @see https://www.FreeRTOS.org/xTaskCheckForTimeOut.html + * + * Example Usage: + *
+ *  // Driver library function used to receive uxWantedBytes from an Rx buffer
+ *  // that is filled by a UART interrupt. If there are not enough bytes in the
+ *  // Rx buffer then the task enters the Blocked state until it is notified that
+ *  // more data has been placed into the buffer. If there is still not enough
+ *  // data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
+ *  // is used to re-calculate the Block time to ensure the total amount of time
+ *  // spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
+ *  // continues until either the buffer contains at least uxWantedBytes bytes,
+ *  // or the total amount of time spent in the Blocked state reaches
+ *  // MAX_TIME_TO_WAIT – at which point the task reads however many bytes are
+ *  // available up to a maximum of uxWantedBytes.
+ *
+ *  size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
+ *  {
+ *  size_t uxReceived = 0;
+ *  TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
+ *  TimeOut_t xTimeOut;
+ *
+ *      // Initialize xTimeOut.  This records the time at which this function
+ *      // was entered.
+ *      vTaskSetTimeOutState( &xTimeOut );
+ *
+ *      // Loop until the buffer contains the wanted number of bytes, or a
+ *      // timeout occurs.
+ *      while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
+ *      {
+ *          // The buffer didn't contain enough data so this task is going to
+ *          // enter the Blocked state. Adjusting xTicksToWait to account for
+ *          // any time that has been spent in the Blocked state within this
+ *          // function so far to ensure the total amount of time spent in the
+ *          // Blocked state does not exceed MAX_TIME_TO_WAIT.
+ *          if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
+ *          {
+ *              //Timed out before the wanted number of bytes were available,
+ *              // exit the loop.
+ *              break;
+ *          }
+ *
+ *          // Wait for a maximum of xTicksToWait ticks to be notified that the
+ *          // receive interrupt has placed more data into the buffer.
+ *          ulTaskNotifyTake( pdTRUE, xTicksToWait );
+ *      }
+ *
+ *      // Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
+ *      // The actual number of bytes read (which might be less than
+ *      // uxWantedBytes) is returned.
+ *      uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
+ *                                                  pucBuffer,
+ *                                                  uxWantedBytes );
+ *
+ *      return uxReceived;
+ *  }
+ * 
+ * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut + * \ingroup TaskCtrl + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
+ * BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp );
+ * 
+ * + * This function corrects the tick count value after the application code has held + * interrupts disabled for an extended period resulting in tick interrupts having + * been missed. + * + * This function is similar to vTaskStepTick(), however, unlike + * vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a + * time at which a task should be removed from the blocked state. That means + * tasks may have to be removed from the blocked state as the tick count is + * moved. + * + * @param xTicksToCatchUp The number of tick interrupts that have been missed due to + * interrupts being disabled. Its value is not computed automatically, so must be + * computed by the application writer. + * + * @return pdTRUE if moving the tick count forward resulted in a task leaving the + * blocked state and a context switch being performed. Otherwise pdFALSE. + * + * \defgroup xTaskCatchUpTicks xTaskCatchUpTicks + * \ingroup TaskCtrl + */ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; + + +/*----------------------------------------------------------- +* SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES +*----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. If a non-zero value is returned then a context switch is + * required because either: + * + A task was removed from a blocked list because its timeout had expired, + * or + * + Time slicing is in use and there is a task of equal priority to the + * currently running task. + */ +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * The 'unordered' version replaces the event list item value with the + * xItemValue value, and inserts the list item at the end of the list. + * + * The 'ordered' version uses the existing event list item value (which is the + * owning tasks priority) to insert the list item into the event list is task + * priority order. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xItemValue The item value to use for the event list item when the + * event list is not ordered by task priority. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + */ +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called + * if either an event occurs to unblock a task, or the block timeout period + * expires. + * + * xTaskRemoveFromEventList() is used when the event list is in task priority + * order. It removes the list item from the head of the event list as that will + * have the highest priority owning task of all the tasks on the event list. + * vTaskRemoveFromUnorderedEventList() is used when the event list is not + * ordered and the event list items hold something other than the owning tasks + * priority. In this case the event list item value is updated to the value + * passed in the xItemValue parameter. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; + +/* + * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY + * THE EVENT BITS MODULE. + */ +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; + +/* + * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. + */ +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * Set the uxTaskNumber of the task referenced by the xTask parameter to + * uxHandle. + */ +void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * If tickless mode is being used, or a low power mode is implemented, then + * the tick interrupt will not execute during idle periods. When this is the + * case, the tick count value maintained by the scheduler needs to be kept up + * to date with the actual execution time by being skipped forward by a time + * equal to the idle period. + */ +void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port + * specific sleep function to determine if it is ok to proceed with the sleep, + * and if it is ok to proceed, if it is ok to sleep indefinitely. + * + * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only + * called with the scheduler suspended, not from within a critical section. It + * is therefore possible for an interrupt to request a context switch between + * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being + * entered. eTaskConfirmSleepModeStatus() should be called from a short + * critical section between the timer being stopped and the sleep mode being + * entered to ensure it is ok to proceed into the sleep mode. + */ +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Increment the mutex held count when a mutex is + * taken and return the handle of the task that has taken the mutex. + */ +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a critical + * section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ +#endif /* INC_TASK_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h b/source/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h index 3c787287f9..ba5c413260 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/include/timers.h @@ -1,1309 +1,1351 @@ -/* - * FreeRTOS Kernel V10.3.1 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef TIMERS_H -#define TIMERS_H - -#ifndef INC_FREERTOS_H - #error "include FreeRTOS.h must appear in source files before include timers.h" -#endif - -/*lint -save -e537 This headers are only multiply included if the application code -happens to also be including task.h. */ -#include "task.h" -/*lint -restore */ - -#ifdef __cplusplus -extern "C" { -#endif - -/*----------------------------------------------------------- - * MACROS AND DEFINITIONS - *----------------------------------------------------------*/ - -/* IDs for commands that can be sent/received on the timer queue. These are to -be used solely through the macros that make up the public software timer API, -as defined below. The commands that are sent from interrupts must use the -highest numbers as tmrFIRST_FROM_ISR_COMMAND is used to determine if the task -or interrupt version of the queue send function should be used. */ -#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 ) -#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 ) -#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 ) -#define tmrCOMMAND_START ( ( BaseType_t ) 1 ) -#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 ) -#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 ) -#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 ) -#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 ) - -#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 ) -#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 6 ) -#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 7 ) -#define tmrCOMMAND_STOP_FROM_ISR ( ( BaseType_t ) 8 ) -#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 ) - - -/** - * Type by which software timers are referenced. For example, a call to - * xTimerCreate() returns an TimerHandle_t variable that can then be used to - * reference the subject timer in calls to other software timer API functions - * (for example, xTimerStart(), xTimerReset(), etc.). - */ -struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ -typedef struct tmrTimerControl * TimerHandle_t; - -/* - * Defines the prototype to which timer callback functions must conform. - */ -typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); - -/* - * Defines the prototype to which functions used with the - * xTimerPendFunctionCallFromISR() function must conform. - */ -typedef void (*PendedFunction_t)( void *, uint32_t ); - -/** - * TimerHandle_t xTimerCreate( const char * const pcTimerName, - * TickType_t xTimerPeriodInTicks, - * UBaseType_t uxAutoReload, - * void * pvTimerID, - * TimerCallbackFunction_t pxCallbackFunction ); - * - * Creates a new software timer instance, and returns a handle by which the - * created software timer can be referenced. - * - * Internally, within the FreeRTOS implementation, software timers use a block - * of memory, in which the timer data structure is stored. If a software timer - * is created using xTimerCreate() then the required memory is automatically - * dynamically allocated inside the xTimerCreate() function. (see - * http://www.freertos.org/a00111.html). If a software timer is created using - * xTimerCreateStatic() then the application writer must provide the memory that - * will get used by the software timer. xTimerCreateStatic() therefore allows a - * software timer to be created without using any dynamic memory allocation. - * - * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), - * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and - * xTimerChangePeriodFromISR() API functions can all be used to transition a - * timer into the active state. - * - * @param pcTimerName A text name that is assigned to the timer. This is done - * purely to assist debugging. The kernel itself only ever references a timer - * by its handle, and never by its name. - * - * @param xTimerPeriodInTicks The timer period. The time is defined in tick - * periods so the constant portTICK_PERIOD_MS can be used to convert a time that - * has been specified in milliseconds. For example, if the timer must expire - * after 100 ticks, then xTimerPeriodInTicks should be set to 100. - * Alternatively, if the timer must expire after 500ms, then xPeriod can be set - * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or - * equal to 1000. - * - * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will - * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. - * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and - * enter the dormant state after it expires. - * - * @param pvTimerID An identifier that is assigned to the timer being created. - * Typically this would be used in the timer callback function to identify which - * timer expired when the same callback function is assigned to more than one - * timer. - * - * @param pxCallbackFunction The function to call when the timer expires. - * Callback functions must have the prototype defined by TimerCallbackFunction_t, - * which is "void vCallbackFunction( TimerHandle_t xTimer );". - * - * @return If the timer is successfully created then a handle to the newly - * created timer is returned. If the timer cannot be created (because either - * there is insufficient FreeRTOS heap remaining to allocate the timer - * structures, or the timer period was set to 0) then NULL is returned. - * - * Example usage: - * @verbatim - * #define NUM_TIMERS 5 - * - * // An array to hold handles to the created timers. - * TimerHandle_t xTimers[ NUM_TIMERS ]; - * - * // An array to hold a count of the number of times each timer expires. - * int32_t lExpireCounters[ NUM_TIMERS ] = { 0 }; - * - * // Define a callback function that will be used by multiple timer instances. - * // The callback function does nothing but count the number of times the - * // associated timer expires, and stop the timer once the timer has expired - * // 10 times. - * void vTimerCallback( TimerHandle_t pxTimer ) - * { - * int32_t lArrayIndex; - * const int32_t xMaxExpiryCountBeforeStopping = 10; - * - * // Optionally do something if the pxTimer parameter is NULL. - * configASSERT( pxTimer ); - * - * // Which timer expired? - * lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer ); - * - * // Increment the number of times that pxTimer has expired. - * lExpireCounters[ lArrayIndex ] += 1; - * - * // If the timer has expired 10 times then stop it from running. - * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) - * { - * // Do not use a block time if calling a timer API function from a - * // timer callback function, as doing so could cause a deadlock! - * xTimerStop( pxTimer, 0 ); - * } - * } - * - * void main( void ) - * { - * int32_t x; - * - * // Create then start some timers. Starting the timers before the scheduler - * // has been started means the timers will start running immediately that - * // the scheduler starts. - * for( x = 0; x < NUM_TIMERS; x++ ) - * { - * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. - * ( 100 * x ), // The timer period in ticks. - * pdTRUE, // The timers will auto-reload themselves when they expire. - * ( void * ) x, // Assign each timer a unique id equal to its array index. - * vTimerCallback // Each timer calls the same callback when it expires. - * ); - * - * if( xTimers[ x ] == NULL ) - * { - * // The timer was not created. - * } - * else - * { - * // Start the timer. No block time is specified, and even if one was - * // it would be ignored because the scheduler has not yet been - * // started. - * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) - * { - * // The timer could not be set into the Active state. - * } - * } - * } - * - * // ... - * // Create tasks here. - * // ... - * - * // Starting the scheduler will start the timers running as they have already - * // been set into the active state. - * vTaskStartScheduler(); - * - * // Should not reach here. - * for( ;; ); - * } - * @endverbatim - */ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; -#endif - -/** - * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, - * TickType_t xTimerPeriodInTicks, - * UBaseType_t uxAutoReload, - * void * pvTimerID, - * TimerCallbackFunction_t pxCallbackFunction, - * StaticTimer_t *pxTimerBuffer ); - * - * Creates a new software timer instance, and returns a handle by which the - * created software timer can be referenced. - * - * Internally, within the FreeRTOS implementation, software timers use a block - * of memory, in which the timer data structure is stored. If a software timer - * is created using xTimerCreate() then the required memory is automatically - * dynamically allocated inside the xTimerCreate() function. (see - * http://www.freertos.org/a00111.html). If a software timer is created using - * xTimerCreateStatic() then the application writer must provide the memory that - * will get used by the software timer. xTimerCreateStatic() therefore allows a - * software timer to be created without using any dynamic memory allocation. - * - * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), - * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and - * xTimerChangePeriodFromISR() API functions can all be used to transition a - * timer into the active state. - * - * @param pcTimerName A text name that is assigned to the timer. This is done - * purely to assist debugging. The kernel itself only ever references a timer - * by its handle, and never by its name. - * - * @param xTimerPeriodInTicks The timer period. The time is defined in tick - * periods so the constant portTICK_PERIOD_MS can be used to convert a time that - * has been specified in milliseconds. For example, if the timer must expire - * after 100 ticks, then xTimerPeriodInTicks should be set to 100. - * Alternatively, if the timer must expire after 500ms, then xPeriod can be set - * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or - * equal to 1000. - * - * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will - * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. - * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and - * enter the dormant state after it expires. - * - * @param pvTimerID An identifier that is assigned to the timer being created. - * Typically this would be used in the timer callback function to identify which - * timer expired when the same callback function is assigned to more than one - * timer. - * - * @param pxCallbackFunction The function to call when the timer expires. - * Callback functions must have the prototype defined by TimerCallbackFunction_t, - * which is "void vCallbackFunction( TimerHandle_t xTimer );". - * - * @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which - * will be then be used to hold the software timer's data structures, removing - * the need for the memory to be allocated dynamically. - * - * @return If the timer is created then a handle to the created timer is - * returned. If pxTimerBuffer was NULL then NULL is returned. - * - * Example usage: - * @verbatim - * - * // The buffer used to hold the software timer's data structure. - * static StaticTimer_t xTimerBuffer; - * - * // A variable that will be incremented by the software timer's callback - * // function. - * UBaseType_t uxVariableToIncrement = 0; - * - * // A software timer callback function that increments a variable passed to - * // it when the software timer was created. After the 5th increment the - * // callback function stops the software timer. - * static void prvTimerCallback( TimerHandle_t xExpiredTimer ) - * { - * UBaseType_t *puxVariableToIncrement; - * BaseType_t xReturned; - * - * // Obtain the address of the variable to increment from the timer ID. - * puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); - * - * // Increment the variable to show the timer callback has executed. - * ( *puxVariableToIncrement )++; - * - * // If this callback has executed the required number of times, stop the - * // timer. - * if( *puxVariableToIncrement == 5 ) - * { - * // This is called from a timer callback so must not block. - * xTimerStop( xExpiredTimer, staticDONT_BLOCK ); - * } - * } - * - * - * void main( void ) - * { - * // Create the software time. xTimerCreateStatic() has an extra parameter - * // than the normal xTimerCreate() API function. The parameter is a pointer - * // to the StaticTimer_t structure that will hold the software timer - * // structure. If the parameter is passed as NULL then the structure will be - * // allocated dynamically, just as if xTimerCreate() had been called. - * xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS. - * xTimerPeriod, // The period of the timer in ticks. - * pdTRUE, // This is an auto-reload timer. - * ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function - * prvTimerCallback, // The function to execute when the timer expires. - * &xTimerBuffer ); // The buffer that will hold the software timer structure. - * - * // The scheduler has not started yet so a block time is not used. - * xReturned = xTimerStart( xTimer, 0 ); - * - * // ... - * // Create tasks here. - * // ... - * - * // Starting the scheduler will start the timers running as they have already - * // been set into the active state. - * vTaskStartScheduler(); - * - * // Should not reach here. - * for( ;; ); - * } - * @endverbatim - */ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; -#endif /* configSUPPORT_STATIC_ALLOCATION */ - -/** - * void *pvTimerGetTimerID( TimerHandle_t xTimer ); - * - * Returns the ID assigned to the timer. - * - * IDs are assigned to timers using the pvTimerID parameter of the call to - * xTimerCreated() that was used to create the timer, and by calling the - * vTimerSetTimerID() API function. - * - * If the same callback function is assigned to multiple timers then the timer - * ID can be used as time specific (timer local) storage. - * - * @param xTimer The timer being queried. - * - * @return The ID assigned to the timer being queried. - * - * Example usage: - * - * See the xTimerCreate() API function example usage scenario. - */ -void *pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - -/** - * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); - * - * Sets the ID assigned to the timer. - * - * IDs are assigned to timers using the pvTimerID parameter of the call to - * xTimerCreated() that was used to create the timer. - * - * If the same callback function is assigned to multiple timers then the timer - * ID can be used as time specific (timer local) storage. - * - * @param xTimer The timer being updated. - * - * @param pvNewID The ID to assign to the timer. - * - * Example usage: - * - * See the xTimerCreate() API function example usage scenario. - */ -void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; - -/** - * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); - * - * Queries a timer to see if it is active or dormant. - * - * A timer will be dormant if: - * 1) It has been created but not started, or - * 2) It is an expired one-shot timer that has not been restarted. - * - * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), - * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and - * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the - * active state. - * - * @param xTimer The timer being queried. - * - * @return pdFALSE will be returned if the timer is dormant. A value other than - * pdFALSE will be returned if the timer is active. - * - * Example usage: - * @verbatim - * // This function assumes xTimer has already been created. - * void vAFunction( TimerHandle_t xTimer ) - * { - * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" - * { - * // xTimer is active, do something. - * } - * else - * { - * // xTimer is not active, do something else. - * } - * } - * @endverbatim - */ -BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - -/** - * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); - * - * Simply returns the handle of the timer service/daemon task. It it not valid - * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. - */ -TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; - -/** - * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * - * Timer functionality is provided by a timer service/daemon task. Many of the - * public FreeRTOS timer API functions send commands to the timer service task - * through a queue called the timer command queue. The timer command queue is - * private to the kernel itself and is not directly accessible to application - * code. The length of the timer command queue is set by the - * configTIMER_QUEUE_LENGTH configuration constant. - * - * xTimerStart() starts a timer that was previously created using the - * xTimerCreate() API function. If the timer had already been started and was - * already in the active state, then xTimerStart() has equivalent functionality - * to the xTimerReset() API function. - * - * Starting a timer ensures the timer is in the active state. If the timer - * is not stopped, deleted, or reset in the mean time, the callback function - * associated with the timer will get called 'n' ticks after xTimerStart() was - * called, where 'n' is the timers defined period. - * - * It is valid to call xTimerStart() before the scheduler has been started, but - * when this is done the timer will not actually start until the scheduler is - * started, and the timers expiry time will be relative to when the scheduler is - * started, not relative to when xTimerStart() was called. - * - * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() - * to be available. - * - * @param xTimer The handle of the timer being started/restarted. - * - * @param xTicksToWait Specifies the time, in ticks, that the calling task should - * be held in the Blocked state to wait for the start command to be successfully - * sent to the timer command queue, should the queue already be full when - * xTimerStart() was called. xTicksToWait is ignored if xTimerStart() is called - * before the scheduler is started. - * - * @return pdFAIL will be returned if the start command could not be sent to - * the timer command queue even after xTicksToWait ticks had passed. pdPASS will - * be returned if the command was successfully sent to the timer command queue. - * When the command is actually processed will depend on the priority of the - * timer service/daemon task relative to other tasks in the system, although the - * timers expiry time is relative to when xTimerStart() is actually called. The - * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY - * configuration constant. - * - * Example usage: - * - * See the xTimerCreate() API function example usage scenario. - * - */ -#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) - -/** - * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * - * Timer functionality is provided by a timer service/daemon task. Many of the - * public FreeRTOS timer API functions send commands to the timer service task - * through a queue called the timer command queue. The timer command queue is - * private to the kernel itself and is not directly accessible to application - * code. The length of the timer command queue is set by the - * configTIMER_QUEUE_LENGTH configuration constant. - * - * xTimerStop() stops a timer that was previously started using either of the - * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), - * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. - * - * Stopping a timer ensures the timer is not in the active state. - * - * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() - * to be available. - * - * @param xTimer The handle of the timer being stopped. - * - * @param xTicksToWait Specifies the time, in ticks, that the calling task should - * be held in the Blocked state to wait for the stop command to be successfully - * sent to the timer command queue, should the queue already be full when - * xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called - * before the scheduler is started. - * - * @return pdFAIL will be returned if the stop command could not be sent to - * the timer command queue even after xTicksToWait ticks had passed. pdPASS will - * be returned if the command was successfully sent to the timer command queue. - * When the command is actually processed will depend on the priority of the - * timer service/daemon task relative to other tasks in the system. The timer - * service/daemon task priority is set by the configTIMER_TASK_PRIORITY - * configuration constant. - * - * Example usage: - * - * See the xTimerCreate() API function example usage scenario. - * - */ -#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) - -/** - * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, - * TickType_t xNewPeriod, - * TickType_t xTicksToWait ); - * - * Timer functionality is provided by a timer service/daemon task. Many of the - * public FreeRTOS timer API functions send commands to the timer service task - * through a queue called the timer command queue. The timer command queue is - * private to the kernel itself and is not directly accessible to application - * code. The length of the timer command queue is set by the - * configTIMER_QUEUE_LENGTH configuration constant. - * - * xTimerChangePeriod() changes the period of a timer that was previously - * created using the xTimerCreate() API function. - * - * xTimerChangePeriod() can be called to change the period of an active or - * dormant state timer. - * - * The configUSE_TIMERS configuration constant must be set to 1 for - * xTimerChangePeriod() to be available. - * - * @param xTimer The handle of the timer that is having its period changed. - * - * @param xNewPeriod The new period for xTimer. Timer periods are specified in - * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time - * that has been specified in milliseconds. For example, if the timer must - * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, - * if the timer must expire after 500ms, then xNewPeriod can be set to - * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than - * or equal to 1000. - * - * @param xTicksToWait Specifies the time, in ticks, that the calling task should - * be held in the Blocked state to wait for the change period command to be - * successfully sent to the timer command queue, should the queue already be - * full when xTimerChangePeriod() was called. xTicksToWait is ignored if - * xTimerChangePeriod() is called before the scheduler is started. - * - * @return pdFAIL will be returned if the change period command could not be - * sent to the timer command queue even after xTicksToWait ticks had passed. - * pdPASS will be returned if the command was successfully sent to the timer - * command queue. When the command is actually processed will depend on the - * priority of the timer service/daemon task relative to other tasks in the - * system. The timer service/daemon task priority is set by the - * configTIMER_TASK_PRIORITY configuration constant. - * - * Example usage: - * @verbatim - * // This function assumes xTimer has already been created. If the timer - * // referenced by xTimer is already active when it is called, then the timer - * // is deleted. If the timer referenced by xTimer is not active when it is - * // called, then the period of the timer is set to 500ms and the timer is - * // started. - * void vAFunction( TimerHandle_t xTimer ) - * { - * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" - * { - * // xTimer is already active - delete it. - * xTimerDelete( xTimer ); - * } - * else - * { - * // xTimer is not active, change its period to 500ms. This will also - * // cause the timer to start. Block for a maximum of 100 ticks if the - * // change period command cannot immediately be sent to the timer - * // command queue. - * if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS ) - * { - * // The command was successfully sent. - * } - * else - * { - * // The command could not be sent, even after waiting for 100 ticks - * // to pass. Take appropriate action here. - * } - * } - * } - * @endverbatim - */ - #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) - -/** - * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * - * Timer functionality is provided by a timer service/daemon task. Many of the - * public FreeRTOS timer API functions send commands to the timer service task - * through a queue called the timer command queue. The timer command queue is - * private to the kernel itself and is not directly accessible to application - * code. The length of the timer command queue is set by the - * configTIMER_QUEUE_LENGTH configuration constant. - * - * xTimerDelete() deletes a timer that was previously created using the - * xTimerCreate() API function. - * - * The configUSE_TIMERS configuration constant must be set to 1 for - * xTimerDelete() to be available. - * - * @param xTimer The handle of the timer being deleted. - * - * @param xTicksToWait Specifies the time, in ticks, that the calling task should - * be held in the Blocked state to wait for the delete command to be - * successfully sent to the timer command queue, should the queue already be - * full when xTimerDelete() was called. xTicksToWait is ignored if xTimerDelete() - * is called before the scheduler is started. - * - * @return pdFAIL will be returned if the delete command could not be sent to - * the timer command queue even after xTicksToWait ticks had passed. pdPASS will - * be returned if the command was successfully sent to the timer command queue. - * When the command is actually processed will depend on the priority of the - * timer service/daemon task relative to other tasks in the system. The timer - * service/daemon task priority is set by the configTIMER_TASK_PRIORITY - * configuration constant. - * - * Example usage: - * - * See the xTimerChangePeriod() API function example usage scenario. - */ -#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) - -/** - * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); - * - * Timer functionality is provided by a timer service/daemon task. Many of the - * public FreeRTOS timer API functions send commands to the timer service task - * through a queue called the timer command queue. The timer command queue is - * private to the kernel itself and is not directly accessible to application - * code. The length of the timer command queue is set by the - * configTIMER_QUEUE_LENGTH configuration constant. - * - * xTimerReset() re-starts a timer that was previously created using the - * xTimerCreate() API function. If the timer had already been started and was - * already in the active state, then xTimerReset() will cause the timer to - * re-evaluate its expiry time so that it is relative to when xTimerReset() was - * called. If the timer was in the dormant state then xTimerReset() has - * equivalent functionality to the xTimerStart() API function. - * - * Resetting a timer ensures the timer is in the active state. If the timer - * is not stopped, deleted, or reset in the mean time, the callback function - * associated with the timer will get called 'n' ticks after xTimerReset() was - * called, where 'n' is the timers defined period. - * - * It is valid to call xTimerReset() before the scheduler has been started, but - * when this is done the timer will not actually start until the scheduler is - * started, and the timers expiry time will be relative to when the scheduler is - * started, not relative to when xTimerReset() was called. - * - * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() - * to be available. - * - * @param xTimer The handle of the timer being reset/started/restarted. - * - * @param xTicksToWait Specifies the time, in ticks, that the calling task should - * be held in the Blocked state to wait for the reset command to be successfully - * sent to the timer command queue, should the queue already be full when - * xTimerReset() was called. xTicksToWait is ignored if xTimerReset() is called - * before the scheduler is started. - * - * @return pdFAIL will be returned if the reset command could not be sent to - * the timer command queue even after xTicksToWait ticks had passed. pdPASS will - * be returned if the command was successfully sent to the timer command queue. - * When the command is actually processed will depend on the priority of the - * timer service/daemon task relative to other tasks in the system, although the - * timers expiry time is relative to when xTimerStart() is actually called. The - * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY - * configuration constant. - * - * Example usage: - * @verbatim - * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass - * // without a key being pressed, then the LCD back-light is switched off. In - * // this case, the timer is a one-shot timer. - * - * TimerHandle_t xBacklightTimer = NULL; - * - * // The callback function assigned to the one-shot timer. In this case the - * // parameter is not used. - * void vBacklightTimerCallback( TimerHandle_t pxTimer ) - * { - * // The timer expired, therefore 5 seconds must have passed since a key - * // was pressed. Switch off the LCD back-light. - * vSetBacklightState( BACKLIGHT_OFF ); - * } - * - * // The key press event handler. - * void vKeyPressEventHandler( char cKey ) - * { - * // Ensure the LCD back-light is on, then reset the timer that is - * // responsible for turning the back-light off after 5 seconds of - * // key inactivity. Wait 10 ticks for the command to be successfully sent - * // if it cannot be sent immediately. - * vSetBacklightState( BACKLIGHT_ON ); - * if( xTimerReset( xBacklightTimer, 100 ) != pdPASS ) - * { - * // The reset command was not executed successfully. Take appropriate - * // action here. - * } - * - * // Perform the rest of the key processing here. - * } - * - * void main( void ) - * { - * int32_t x; - * - * // Create then start the one-shot timer that is responsible for turning - * // the back-light off if no keys are pressed within a 5 second period. - * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. - * ( 5000 / portTICK_PERIOD_MS), // The timer period in ticks. - * pdFALSE, // The timer is a one-shot timer. - * 0, // The id is not used by the callback so can take any value. - * vBacklightTimerCallback // The callback function that switches the LCD back-light off. - * ); - * - * if( xBacklightTimer == NULL ) - * { - * // The timer was not created. - * } - * else - * { - * // Start the timer. No block time is specified, and even if one was - * // it would be ignored because the scheduler has not yet been - * // started. - * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) - * { - * // The timer could not be set into the Active state. - * } - * } - * - * // ... - * // Create tasks here. - * // ... - * - * // Starting the scheduler will start the timer running as it has already - * // been set into the active state. - * vTaskStartScheduler(); - * - * // Should not reach here. - * for( ;; ); - * } - * @endverbatim - */ -#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) - -/** - * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * A version of xTimerStart() that can be called from an interrupt service - * routine. - * - * @param xTimer The handle of the timer being started/restarted. - * - * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most - * of its time in the Blocked state, waiting for messages to arrive on the timer - * command queue. Calling xTimerStartFromISR() writes a message to the timer - * command queue, so has the potential to transition the timer service/daemon - * task out of the Blocked state. If calling xTimerStartFromISR() causes the - * timer service/daemon task to leave the Blocked state, and the timer service/ - * daemon task has a priority equal to or greater than the currently executing - * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will - * get set to pdTRUE internally within the xTimerStartFromISR() function. If - * xTimerStartFromISR() sets this value to pdTRUE then a context switch should - * be performed before the interrupt exits. - * - * @return pdFAIL will be returned if the start command could not be sent to - * the timer command queue. pdPASS will be returned if the command was - * successfully sent to the timer command queue. When the command is actually - * processed will depend on the priority of the timer service/daemon task - * relative to other tasks in the system, although the timers expiry time is - * relative to when xTimerStartFromISR() is actually called. The timer - * service/daemon task priority is set by the configTIMER_TASK_PRIORITY - * configuration constant. - * - * Example usage: - * @verbatim - * // This scenario assumes xBacklightTimer has already been created. When a - * // key is pressed, an LCD back-light is switched on. If 5 seconds pass - * // without a key being pressed, then the LCD back-light is switched off. In - * // this case, the timer is a one-shot timer, and unlike the example given for - * // the xTimerReset() function, the key press event handler is an interrupt - * // service routine. - * - * // The callback function assigned to the one-shot timer. In this case the - * // parameter is not used. - * void vBacklightTimerCallback( TimerHandle_t pxTimer ) - * { - * // The timer expired, therefore 5 seconds must have passed since a key - * // was pressed. Switch off the LCD back-light. - * vSetBacklightState( BACKLIGHT_OFF ); - * } - * - * // The key press interrupt service routine. - * void vKeyPressEventInterruptHandler( void ) - * { - * BaseType_t xHigherPriorityTaskWoken = pdFALSE; - * - * // Ensure the LCD back-light is on, then restart the timer that is - * // responsible for turning the back-light off after 5 seconds of - * // key inactivity. This is an interrupt service routine so can only - * // call FreeRTOS API functions that end in "FromISR". - * vSetBacklightState( BACKLIGHT_ON ); - * - * // xTimerStartFromISR() or xTimerResetFromISR() could be called here - * // as both cause the timer to re-calculate its expiry time. - * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was - * // declared (in this function). - * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) - * { - * // The start command was not executed successfully. Take appropriate - * // action here. - * } - * - * // Perform the rest of the key processing here. - * - * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch - * // should be performed. The syntax required to perform a context switch - * // from inside an ISR varies from port to port, and from compiler to - * // compiler. Inspect the demos for the port you are using to find the - * // actual syntax required. - * if( xHigherPriorityTaskWoken != pdFALSE ) - * { - * // Call the interrupt safe yield function here (actual function - * // depends on the FreeRTOS port being used). - * } - * } - * @endverbatim - */ -#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) - -/** - * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * A version of xTimerStop() that can be called from an interrupt service - * routine. - * - * @param xTimer The handle of the timer being stopped. - * - * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most - * of its time in the Blocked state, waiting for messages to arrive on the timer - * command queue. Calling xTimerStopFromISR() writes a message to the timer - * command queue, so has the potential to transition the timer service/daemon - * task out of the Blocked state. If calling xTimerStopFromISR() causes the - * timer service/daemon task to leave the Blocked state, and the timer service/ - * daemon task has a priority equal to or greater than the currently executing - * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will - * get set to pdTRUE internally within the xTimerStopFromISR() function. If - * xTimerStopFromISR() sets this value to pdTRUE then a context switch should - * be performed before the interrupt exits. - * - * @return pdFAIL will be returned if the stop command could not be sent to - * the timer command queue. pdPASS will be returned if the command was - * successfully sent to the timer command queue. When the command is actually - * processed will depend on the priority of the timer service/daemon task - * relative to other tasks in the system. The timer service/daemon task - * priority is set by the configTIMER_TASK_PRIORITY configuration constant. - * - * Example usage: - * @verbatim - * // This scenario assumes xTimer has already been created and started. When - * // an interrupt occurs, the timer should be simply stopped. - * - * // The interrupt service routine that stops the timer. - * void vAnExampleInterruptServiceRoutine( void ) - * { - * BaseType_t xHigherPriorityTaskWoken = pdFALSE; - * - * // The interrupt has occurred - simply stop the timer. - * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined - * // (within this function). As this is an interrupt service routine, only - * // FreeRTOS API functions that end in "FromISR" can be used. - * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) - * { - * // The stop command was not executed successfully. Take appropriate - * // action here. - * } - * - * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch - * // should be performed. The syntax required to perform a context switch - * // from inside an ISR varies from port to port, and from compiler to - * // compiler. Inspect the demos for the port you are using to find the - * // actual syntax required. - * if( xHigherPriorityTaskWoken != pdFALSE ) - * { - * // Call the interrupt safe yield function here (actual function - * // depends on the FreeRTOS port being used). - * } - * } - * @endverbatim - */ -#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) - -/** - * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, - * TickType_t xNewPeriod, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * A version of xTimerChangePeriod() that can be called from an interrupt - * service routine. - * - * @param xTimer The handle of the timer that is having its period changed. - * - * @param xNewPeriod The new period for xTimer. Timer periods are specified in - * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time - * that has been specified in milliseconds. For example, if the timer must - * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, - * if the timer must expire after 500ms, then xNewPeriod can be set to - * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than - * or equal to 1000. - * - * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most - * of its time in the Blocked state, waiting for messages to arrive on the timer - * command queue. Calling xTimerChangePeriodFromISR() writes a message to the - * timer command queue, so has the potential to transition the timer service/ - * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() - * causes the timer service/daemon task to leave the Blocked state, and the - * timer service/daemon task has a priority equal to or greater than the - * currently executing task (the task that was interrupted), then - * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the - * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets - * this value to pdTRUE then a context switch should be performed before the - * interrupt exits. - * - * @return pdFAIL will be returned if the command to change the timers period - * could not be sent to the timer command queue. pdPASS will be returned if the - * command was successfully sent to the timer command queue. When the command - * is actually processed will depend on the priority of the timer service/daemon - * task relative to other tasks in the system. The timer service/daemon task - * priority is set by the configTIMER_TASK_PRIORITY configuration constant. - * - * Example usage: - * @verbatim - * // This scenario assumes xTimer has already been created and started. When - * // an interrupt occurs, the period of xTimer should be changed to 500ms. - * - * // The interrupt service routine that changes the period of xTimer. - * void vAnExampleInterruptServiceRoutine( void ) - * { - * BaseType_t xHigherPriorityTaskWoken = pdFALSE; - * - * // The interrupt has occurred - change the period of xTimer to 500ms. - * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined - * // (within this function). As this is an interrupt service routine, only - * // FreeRTOS API functions that end in "FromISR" can be used. - * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) - * { - * // The command to change the timers period was not executed - * // successfully. Take appropriate action here. - * } - * - * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch - * // should be performed. The syntax required to perform a context switch - * // from inside an ISR varies from port to port, and from compiler to - * // compiler. Inspect the demos for the port you are using to find the - * // actual syntax required. - * if( xHigherPriorityTaskWoken != pdFALSE ) - * { - * // Call the interrupt safe yield function here (actual function - * // depends on the FreeRTOS port being used). - * } - * } - * @endverbatim - */ -#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) - -/** - * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * A version of xTimerReset() that can be called from an interrupt service - * routine. - * - * @param xTimer The handle of the timer that is to be started, reset, or - * restarted. - * - * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most - * of its time in the Blocked state, waiting for messages to arrive on the timer - * command queue. Calling xTimerResetFromISR() writes a message to the timer - * command queue, so has the potential to transition the timer service/daemon - * task out of the Blocked state. If calling xTimerResetFromISR() causes the - * timer service/daemon task to leave the Blocked state, and the timer service/ - * daemon task has a priority equal to or greater than the currently executing - * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will - * get set to pdTRUE internally within the xTimerResetFromISR() function. If - * xTimerResetFromISR() sets this value to pdTRUE then a context switch should - * be performed before the interrupt exits. - * - * @return pdFAIL will be returned if the reset command could not be sent to - * the timer command queue. pdPASS will be returned if the command was - * successfully sent to the timer command queue. When the command is actually - * processed will depend on the priority of the timer service/daemon task - * relative to other tasks in the system, although the timers expiry time is - * relative to when xTimerResetFromISR() is actually called. The timer service/daemon - * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. - * - * Example usage: - * @verbatim - * // This scenario assumes xBacklightTimer has already been created. When a - * // key is pressed, an LCD back-light is switched on. If 5 seconds pass - * // without a key being pressed, then the LCD back-light is switched off. In - * // this case, the timer is a one-shot timer, and unlike the example given for - * // the xTimerReset() function, the key press event handler is an interrupt - * // service routine. - * - * // The callback function assigned to the one-shot timer. In this case the - * // parameter is not used. - * void vBacklightTimerCallback( TimerHandle_t pxTimer ) - * { - * // The timer expired, therefore 5 seconds must have passed since a key - * // was pressed. Switch off the LCD back-light. - * vSetBacklightState( BACKLIGHT_OFF ); - * } - * - * // The key press interrupt service routine. - * void vKeyPressEventInterruptHandler( void ) - * { - * BaseType_t xHigherPriorityTaskWoken = pdFALSE; - * - * // Ensure the LCD back-light is on, then reset the timer that is - * // responsible for turning the back-light off after 5 seconds of - * // key inactivity. This is an interrupt service routine so can only - * // call FreeRTOS API functions that end in "FromISR". - * vSetBacklightState( BACKLIGHT_ON ); - * - * // xTimerStartFromISR() or xTimerResetFromISR() could be called here - * // as both cause the timer to re-calculate its expiry time. - * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was - * // declared (in this function). - * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) - * { - * // The reset command was not executed successfully. Take appropriate - * // action here. - * } - * - * // Perform the rest of the key processing here. - * - * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch - * // should be performed. The syntax required to perform a context switch - * // from inside an ISR varies from port to port, and from compiler to - * // compiler. Inspect the demos for the port you are using to find the - * // actual syntax required. - * if( xHigherPriorityTaskWoken != pdFALSE ) - * { - * // Call the interrupt safe yield function here (actual function - * // depends on the FreeRTOS port being used). - * } - * } - * @endverbatim - */ -#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) - - -/** - * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, - * void *pvParameter1, - * uint32_t ulParameter2, - * BaseType_t *pxHigherPriorityTaskWoken ); - * - * - * Used from application interrupt service routines to defer the execution of a - * function to the RTOS daemon task (the timer service task, hence this function - * is implemented in timers.c and is prefixed with 'Timer'). - * - * Ideally an interrupt service routine (ISR) is kept as short as possible, but - * sometimes an ISR either has a lot of processing to do, or needs to perform - * processing that is not deterministic. In these cases - * xTimerPendFunctionCallFromISR() can be used to defer processing of a function - * to the RTOS daemon task. - * - * A mechanism is provided that allows the interrupt to return directly to the - * task that will subsequently execute the pended callback function. This - * allows the callback function to execute contiguously in time with the - * interrupt - just as if the callback had executed in the interrupt itself. - * - * @param xFunctionToPend The function to execute from the timer service/ - * daemon task. The function must conform to the PendedFunction_t - * prototype. - * - * @param pvParameter1 The value of the callback function's first parameter. - * The parameter has a void * type to allow it to be used to pass any type. - * For example, unsigned longs can be cast to a void *, or the void * can be - * used to point to a structure. - * - * @param ulParameter2 The value of the callback function's second parameter. - * - * @param pxHigherPriorityTaskWoken As mentioned above, calling this function - * will result in a message being sent to the timer daemon task. If the - * priority of the timer daemon task (which is set using - * configTIMER_TASK_PRIORITY in FreeRTOSConfig.h) is higher than the priority of - * the currently running task (the task the interrupt interrupted) then - * *pxHigherPriorityTaskWoken will be set to pdTRUE within - * xTimerPendFunctionCallFromISR(), indicating that a context switch should be - * requested before the interrupt exits. For that reason - * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the - * example code below. - * - * @return pdPASS is returned if the message was successfully sent to the - * timer daemon task, otherwise pdFALSE is returned. - * - * Example usage: - * @verbatim - * - * // The callback function that will execute in the context of the daemon task. - * // Note callback functions must all use this same prototype. - * void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 ) - * { - * BaseType_t xInterfaceToService; - * - * // The interface that requires servicing is passed in the second - * // parameter. The first parameter is not used in this case. - * xInterfaceToService = ( BaseType_t ) ulParameter2; - * - * // ...Perform the processing here... - * } - * - * // An ISR that receives data packets from multiple interfaces - * void vAnISR( void ) - * { - * BaseType_t xInterfaceToService, xHigherPriorityTaskWoken; - * - * // Query the hardware to determine which interface needs processing. - * xInterfaceToService = prvCheckInterfaces(); - * - * // The actual processing is to be deferred to a task. Request the - * // vProcessInterface() callback function is executed, passing in the - * // number of the interface that needs processing. The interface to - * // service is passed in the second parameter. The first parameter is - * // not used in this case. - * xHigherPriorityTaskWoken = pdFALSE; - * xTimerPendFunctionCallFromISR( vProcessInterface, NULL, ( uint32_t ) xInterfaceToService, &xHigherPriorityTaskWoken ); - * - * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context - * // switch should be requested. The macro used is port specific and will - * // be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to - * // the documentation page for the port being used. - * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); - * - * } - * @endverbatim - */ -BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; - - /** - * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, - * void *pvParameter1, - * uint32_t ulParameter2, - * TickType_t xTicksToWait ); - * - * - * Used to defer the execution of a function to the RTOS daemon task (the timer - * service task, hence this function is implemented in timers.c and is prefixed - * with 'Timer'). - * - * @param xFunctionToPend The function to execute from the timer service/ - * daemon task. The function must conform to the PendedFunction_t - * prototype. - * - * @param pvParameter1 The value of the callback function's first parameter. - * The parameter has a void * type to allow it to be used to pass any type. - * For example, unsigned longs can be cast to a void *, or the void * can be - * used to point to a structure. - * - * @param ulParameter2 The value of the callback function's second parameter. - * - * @param xTicksToWait Calling this function will result in a message being - * sent to the timer daemon task on a queue. xTicksToWait is the amount of - * time the calling task should remain in the Blocked state (so not using any - * processing time) for space to become available on the timer queue if the - * queue is found to be full. - * - * @return pdPASS is returned if the message was successfully sent to the - * timer daemon task, otherwise pdFALSE is returned. - * - */ -BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -/** - * const char * const pcTimerGetName( TimerHandle_t xTimer ); - * - * Returns the name that was assigned to a timer when the timer was created. - * - * @param xTimer The handle of the timer being queried. - * - * @return The name assigned to the timer specified by the xTimer parameter. - */ -const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - -/** - * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); - * - * Updates a timer to be either an auto-reload timer, in which case the timer - * automatically resets itself each time it expires, or a one-shot timer, in - * which case the timer will only expire once unless it is manually restarted. - * - * @param xTimer The handle of the timer being updated. - * - * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will - * expire repeatedly with a frequency set by the timer's period (see the - * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If - * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and - * enter the dormant state after it expires. - */ -void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; - -/** -* UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); -* -* Queries a timer to determine if it is an auto-reload timer, in which case the timer -* automatically resets itself each time it expires, or a one-shot timer, in -* which case the timer will only expire once unless it is manually restarted. -* -* @param xTimer The handle of the timer being queried. -* -* @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise -* pdFALSE is returned. -*/ -UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - -/** - * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); - * - * Returns the period of a timer. - * - * @param xTimer The handle of the timer being queried. - * - * @return The period of the timer in ticks. - */ -TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - -/** -* TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); -* -* Returns the time in ticks at which the timer will expire. If this is less -* than the current tick count then the expiry time has overflowed from the -* current time. -* -* @param xTimer The handle of the timer being queried. -* -* @return If the timer is running then the time in ticks at which the timer -* will next expire is returned. If the timer is not running then the return -* value is undefined. -*/ -TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; - -/* - * Functions beyond this part are not part of the public API and are intended - * for use by the kernel only. - */ -BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; -BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; - -#if( configUSE_TRACE_FACILITY == 1 ) - void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; - UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; -#endif - -#ifdef __cplusplus -} -#endif -#endif /* TIMERS_H */ - - - +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + + +#ifndef TIMERS_H +#define TIMERS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include timers.h" +#endif + +/*lint -save -e537 This headers are only multiply included if the application code + * happens to also be including task.h. */ +#include "task.h" +/*lint -restore */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- +* MACROS AND DEFINITIONS +*----------------------------------------------------------*/ + +/* IDs for commands that can be sent/received on the timer queue. These are to + * be used solely through the macros that make up the public software timer API, + * as defined below. The commands that are sent from interrupts must use the + * highest numbers as tmrFIRST_FROM_ISR_COMMAND is used to determine if the task + * or interrupt version of the queue send function should be used. */ +#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 ) +#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 ) +#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 ) +#define tmrCOMMAND_START ( ( BaseType_t ) 1 ) +#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 ) +#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 ) +#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 ) +#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 ) + +#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 7 ) +#define tmrCOMMAND_STOP_FROM_ISR ( ( BaseType_t ) 8 ) +#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 ) + + +/** + * Type by which software timers are referenced. For example, a call to + * xTimerCreate() returns an TimerHandle_t variable that can then be used to + * reference the subject timer in calls to other software timer API functions + * (for example, xTimerStart(), xTimerReset(), etc.). + */ +struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tmrTimerControl * TimerHandle_t; + +/* + * Defines the prototype to which timer callback functions must conform. + */ +typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer ); + +/* + * Defines the prototype to which functions used with the + * xTimerPendFunctionCallFromISR() function must conform. + */ +typedef void (* PendedFunction_t)( void *, + uint32_t ); + +/** + * TimerHandle_t xTimerCreate( const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. Time timer period must be greater than 0. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @return If the timer is successfully created then a handle to the newly + * created timer is returned. If the timer cannot be created because there is + * insufficient FreeRTOS heap remaining to allocate the timer + * structures then NULL is returned. + * + * Example usage: + * @verbatim + * #define NUM_TIMERS 5 + * + * // An array to hold handles to the created timers. + * TimerHandle_t xTimers[ NUM_TIMERS ]; + * + * // An array to hold a count of the number of times each timer expires. + * int32_t lExpireCounters[ NUM_TIMERS ] = { 0 }; + * + * // Define a callback function that will be used by multiple timer instances. + * // The callback function does nothing but count the number of times the + * // associated timer expires, and stop the timer once the timer has expired + * // 10 times. + * void vTimerCallback( TimerHandle_t pxTimer ) + * { + * int32_t lArrayIndex; + * const int32_t xMaxExpiryCountBeforeStopping = 10; + * + * // Optionally do something if the pxTimer parameter is NULL. + * configASSERT( pxTimer ); + * + * // Which timer expired? + * lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer ); + * + * // Increment the number of times that pxTimer has expired. + * lExpireCounters[ lArrayIndex ] += 1; + * + * // If the timer has expired 10 times then stop it from running. + * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) + * { + * // Do not use a block time if calling a timer API function from a + * // timer callback function, as doing so could cause a deadlock! + * xTimerStop( pxTimer, 0 ); + * } + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start some timers. Starting the timers before the scheduler + * // has been started means the timers will start running immediately that + * // the scheduler starts. + * for( x = 0; x < NUM_TIMERS; x++ ) + * { + * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. + * ( 100 * x ), // The timer period in ticks. + * pdTRUE, // The timers will auto-reload themselves when they expire. + * ( void * ) x, // Assign each timer a unique id equal to its array index. + * vTimerCallback // Each timer calls the same callback when it expires. + * ); + * + * if( xTimers[ x ] == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; +#endif + +/** + * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction, + * StaticTimer_t *pxTimerBuffer ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. The timer period must be greater than 0. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which + * will be then be used to hold the software timer's data structures, removing + * the need for the memory to be allocated dynamically. + * + * @return If the timer is created then a handle to the created timer is + * returned. If pxTimerBuffer was NULL then NULL is returned. + * + * Example usage: + * @verbatim + * + * // The buffer used to hold the software timer's data structure. + * static StaticTimer_t xTimerBuffer; + * + * // A variable that will be incremented by the software timer's callback + * // function. + * UBaseType_t uxVariableToIncrement = 0; + * + * // A software timer callback function that increments a variable passed to + * // it when the software timer was created. After the 5th increment the + * // callback function stops the software timer. + * static void prvTimerCallback( TimerHandle_t xExpiredTimer ) + * { + * UBaseType_t *puxVariableToIncrement; + * BaseType_t xReturned; + * + * // Obtain the address of the variable to increment from the timer ID. + * puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); + * + * // Increment the variable to show the timer callback has executed. + * ( *puxVariableToIncrement )++; + * + * // If this callback has executed the required number of times, stop the + * // timer. + * if( *puxVariableToIncrement == 5 ) + * { + * // This is called from a timer callback so must not block. + * xTimerStop( xExpiredTimer, staticDONT_BLOCK ); + * } + * } + * + * + * void main( void ) + * { + * // Create the software time. xTimerCreateStatic() has an extra parameter + * // than the normal xTimerCreate() API function. The parameter is a pointer + * // to the StaticTimer_t structure that will hold the software timer + * // structure. If the parameter is passed as NULL then the structure will be + * // allocated dynamically, just as if xTimerCreate() had been called. + * xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS. + * xTimerPeriod, // The period of the timer in ticks. + * pdTRUE, // This is an auto-reload timer. + * ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function + * prvTimerCallback, // The function to execute when the timer expires. + * &xTimerBuffer ); // The buffer that will hold the software timer structure. + * + * // The scheduler has not started yet so a block time is not used. + * xReturned = xTimerStart( xTimer, 0 ); + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t * pxTimerBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * void *pvTimerGetTimerID( TimerHandle_t xTimer ); + * + * Returns the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer, and by calling the + * vTimerSetTimerID() API function. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being queried. + * + * @return The ID assigned to the timer being queried. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void * pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); + * + * Sets the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being updated. + * + * @param pvNewID The ID to assign to the timer. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void vTimerSetTimerID( TimerHandle_t xTimer, + void * pvNewID ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); + * + * Queries a timer to see if it is active or dormant. + * + * A timer will be dormant if: + * 1) It has been created but not started, or + * 2) It is an expired one-shot timer that has not been restarted. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param xTimer The timer being queried. + * + * @return pdFALSE will be returned if the timer is dormant. A value other than + * pdFALSE will be returned if the timer is active. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is active, do something. + * } + * else + * { + * // xTimer is not active, do something else. + * } + * } + * @endverbatim + */ +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); + * + * Simply returns the handle of the timer service/daemon task. It it not valid + * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStart() starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerStart() has equivalent functionality + * to the xTimerReset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerStart() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerStart() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerStart() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() + * to be available. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStart() was called. xTicksToWait is ignored if xTimerStart() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStart( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStop() stops a timer that was previously started using either of the + * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), + * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() + * to be available. + * + * @param xTimer The handle of the timer being stopped. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStop( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerChangePeriod() changes the period of a timer that was previously + * created using the xTimerCreate() API function. + * + * xTimerChangePeriod() can be called to change the period of an active or + * dormant state timer. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerChangePeriod() to be available. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerChangePeriod() was called. xTicksToWait is ignored if + * xTimerChangePeriod() is called before the scheduler is started. + * + * @return pdFAIL will be returned if the change period command could not be + * sent to the timer command queue even after xTicksToWait ticks had passed. + * pdPASS will be returned if the command was successfully sent to the timer + * command queue. When the command is actually processed will depend on the + * priority of the timer service/daemon task relative to other tasks in the + * system. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. If the timer + * // referenced by xTimer is already active when it is called, then the timer + * // is deleted. If the timer referenced by xTimer is not active when it is + * // called, then the period of the timer is set to 500ms and the timer is + * // started. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is already active - delete it. + * xTimerDelete( xTimer ); + * } + * else + * { + * // xTimer is not active, change its period to 500ms. This will also + * // cause the timer to start. Block for a maximum of 100 ticks if the + * // change period command cannot immediately be sent to the timer + * // command queue. + * if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS ) + * { + * // The command was successfully sent. + * } + * else + * { + * // The command could not be sent, even after waiting for 100 ticks + * // to pass. Take appropriate action here. + * } + * } + * } + * @endverbatim + */ +#define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerDelete() deletes a timer that was previously created using the + * xTimerCreate() API function. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerDelete() to be available. + * + * @param xTimer The handle of the timer being deleted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerDelete() was called. xTicksToWait is ignored if xTimerDelete() + * is called before the scheduler is started. + * + * @return pdFAIL will be returned if the delete command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerChangePeriod() API function example usage scenario. + */ +#define xTimerDelete( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerReset() re-starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerReset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when xTimerReset() was + * called. If the timer was in the dormant state then xTimerReset() has + * equivalent functionality to the xTimerStart() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerReset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerReset() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerReset() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() + * to be available. + * + * @param xTimer The handle of the timer being reset/started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerReset() was called. xTicksToWait is ignored if xTimerReset() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer. + * + * TimerHandle_t xBacklightTimer = NULL; + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press event handler. + * void vKeyPressEventHandler( char cKey ) + * { + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. Wait 10 ticks for the command to be successfully sent + * // if it cannot be sent immediately. + * vSetBacklightState( BACKLIGHT_ON ); + * if( xTimerReset( xBacklightTimer, 100 ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start the one-shot timer that is responsible for turning + * // the back-light off if no keys are pressed within a 5 second period. + * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. + * ( 5000 / portTICK_PERIOD_MS), // The timer period in ticks. + * pdFALSE, // The timer is a one-shot timer. + * 0, // The id is not used by the callback so can take any value. + * vBacklightTimerCallback // The callback function that switches the LCD back-light off. + * ); + * + * if( xBacklightTimer == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timer running as it has already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#define xTimerReset( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStart() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStartFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStartFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStartFromISR() function. If + * xTimerStartFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerStartFromISR() is actually called. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then restart the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The start command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStop() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being stopped. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStopFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStopFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStopFromISR() function. If + * xTimerStopFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the timer should be simply stopped. + * + * // The interrupt service routine that stops the timer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - simply stop the timer. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The stop command was not executed successfully. Take appropriate + * // action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerChangePeriod() that can be called from an interrupt + * service routine. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerChangePeriodFromISR() writes a message to the + * timer command queue, so has the potential to transition the timer service/ + * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() + * causes the timer service/daemon task to leave the Blocked state, and the + * timer service/daemon task has a priority equal to or greater than the + * currently executing task (the task that was interrupted), then + * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the + * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets + * this value to pdTRUE then a context switch should be performed before the + * interrupt exits. + * + * @return pdFAIL will be returned if the command to change the timers period + * could not be sent to the timer command queue. pdPASS will be returned if the + * command was successfully sent to the timer command queue. When the command + * is actually processed will depend on the priority of the timer service/daemon + * task relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the period of xTimer should be changed to 500ms. + * + * // The interrupt service routine that changes the period of xTimer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - change the period of xTimer to 500ms. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The command to change the timers period was not executed + * // successfully. Take appropriate action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerReset() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer that is to be started, reset, or + * restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerResetFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerResetFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerResetFromISR() function. If + * xTimerResetFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerResetFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + + +/** + * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * + * Used from application interrupt service routines to defer the execution of a + * function to the RTOS daemon task (the timer service task, hence this function + * is implemented in timers.c and is prefixed with 'Timer'). + * + * Ideally an interrupt service routine (ISR) is kept as short as possible, but + * sometimes an ISR either has a lot of processing to do, or needs to perform + * processing that is not deterministic. In these cases + * xTimerPendFunctionCallFromISR() can be used to defer processing of a function + * to the RTOS daemon task. + * + * A mechanism is provided that allows the interrupt to return directly to the + * task that will subsequently execute the pended callback function. This + * allows the callback function to execute contiguously in time with the + * interrupt - just as if the callback had executed in the interrupt itself. + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param pxHigherPriorityTaskWoken As mentioned above, calling this function + * will result in a message being sent to the timer daemon task. If the + * priority of the timer daemon task (which is set using + * configTIMER_TASK_PRIORITY in FreeRTOSConfig.h) is higher than the priority of + * the currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE within + * xTimerPendFunctionCallFromISR(), indicating that a context switch should be + * requested before the interrupt exits. For that reason + * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the + * example code below. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + * Example usage: + * @verbatim + * + * // The callback function that will execute in the context of the daemon task. + * // Note callback functions must all use this same prototype. + * void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 ) + * { + * BaseType_t xInterfaceToService; + * + * // The interface that requires servicing is passed in the second + * // parameter. The first parameter is not used in this case. + * xInterfaceToService = ( BaseType_t ) ulParameter2; + * + * // ...Perform the processing here... + * } + * + * // An ISR that receives data packets from multiple interfaces + * void vAnISR( void ) + * { + * BaseType_t xInterfaceToService, xHigherPriorityTaskWoken; + * + * // Query the hardware to determine which interface needs processing. + * xInterfaceToService = prvCheckInterfaces(); + * + * // The actual processing is to be deferred to a task. Request the + * // vProcessInterface() callback function is executed, passing in the + * // number of the interface that needs processing. The interface to + * // service is passed in the second parameter. The first parameter is + * // not used in this case. + * xHigherPriorityTaskWoken = pdFALSE; + * xTimerPendFunctionCallFromISR( vProcessInterface, NULL, ( uint32_t ) xInterfaceToService, &xHigherPriorityTaskWoken ); + * + * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context + * // switch should be requested. The macro used is port specific and will + * // be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to + * // the documentation page for the port being used. + * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + * + * } + * @endverbatim + */ +BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * TickType_t xTicksToWait ); + * + * + * Used to defer the execution of a function to the RTOS daemon task (the timer + * service task, hence this function is implemented in timers.c and is prefixed + * with 'Timer'). + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param xTicksToWait Calling this function will result in a message being + * sent to the timer daemon task on a queue. xTicksToWait is the amount of + * time the calling task should remain in the Blocked state (so not using any + * processing time) for space to become available on the timer queue if the + * queue is found to be full. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + */ +BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * const char * const pcTimerGetName( TimerHandle_t xTimer ); + * + * Returns the name that was assigned to a timer when the timer was created. + * + * @param xTimer The handle of the timer being queried. + * + * @return The name assigned to the timer specified by the xTimer parameter. + */ +const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); + * + * Updates a timer to be either an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being updated. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the timer's period (see the + * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If + * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + */ +void vTimerSetReloadMode( TimerHandle_t xTimer, + const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; + +/** + * UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); + * + * Queries a timer to determine if it is an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise + * pdFALSE is returned. + */ +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); + * + * Returns the period of a timer. + * + * @param xTimer The handle of the timer being queried. + * + * @return The period of the timer in ticks. + */ +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); + * + * Returns the time in ticks at which the timer will expire. If this is less + * than the current tick count then the expiry time has overflowed from the + * current time. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is running then the time in ticks at which the timer + * will next expire is returned. If the timer is not running then the return + * value is undefined. + */ +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/* + * Functions beyond this part are not part of the public API and are intended + * for use by the kernel only. + */ +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t * const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#if ( configUSE_TRACE_FACILITY == 1 ) + void vTimerSetTimerNumber( TimerHandle_t xTimer, + UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +#endif + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + /** + * task.h + *
void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, StackType_t ** ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) 
+ * + * This function is used to provide a statically allocated block of memory to FreeRTOS to hold the Timer Task TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * @param ppxTimerTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxTimerTaskStackBuffer A handle to a statically allocated Stack buffer for thie idle task + * @param pulTimerTaskStackSize A pointer to the number of elements that will fit in the allocated stack buffer + */ + void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + uint32_t * pulTimerTaskStackSize ); + +#endif + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ +#endif /* TIMERS_H */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/list.c b/source/Middlewares/Third_Party/FreeRTOS/Source/list.c index 0e0e72d82c..5d1dc9e790 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/list.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/list.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,180 +19,192 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + #include "FreeRTOS.h" #include "list.h" +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be + * defined for the header files above, but not in this file, in order to + * generate the correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + /*----------------------------------------------------------- - * PUBLIC LIST API documented in list.h - *----------------------------------------------------------*/ +* PUBLIC LIST API documented in list.h +*----------------------------------------------------------*/ void vListInitialise( List_t * const pxList ) { - /* The list structure contains a list item which is used to mark the - end of the list. To initialise the list the list end is inserted - as the only list entry. */ - pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - - /* The list end value is the highest possible value in the list to - ensure it remains at the end of the list. */ - pxList->xListEnd.xItemValue = portMAX_DELAY; - - /* The list end next and previous pointers point to itself so we know - when the list is empty. */ - pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ - - pxList->uxNumberOfItems = ( UBaseType_t ) 0U; - - /* Write known values into the list if - configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); - listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); + /* The list structure contains a list item which is used to mark the + * end of the list. To initialise the list the list end is inserted + * as the only list entry. */ + pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + /* The list end value is the highest possible value in the list to + * ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + * when the list is empty. */ + pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + pxList->uxNumberOfItems = ( UBaseType_t ) 0U; + + /* Write known values into the list if + * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); + listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); } /*-----------------------------------------------------------*/ void vListInitialiseItem( ListItem_t * const pxItem ) { - /* Make sure the list item is not recorded as being on a list. */ - pxItem->pxContainer = NULL; + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pxContainer = NULL; - /* Write known values into the list item if - configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ - listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); - listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); + /* Write known values into the list item if + * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); + listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); } /*-----------------------------------------------------------*/ -void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) +void vListInsertEnd( List_t * const pxList, + ListItem_t * const pxNewListItem ) { -ListItem_t * const pxIndex = pxList->pxIndex; + ListItem_t * const pxIndex = pxList->pxIndex; - /* Only effective when configASSERT() is also defined, these tests may catch - the list data structures being overwritten in memory. They will not catch - data errors caused by incorrect configuration or use of FreeRTOS. */ - listTEST_LIST_INTEGRITY( pxList ); - listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); + /* Only effective when configASSERT() is also defined, these tests may catch + * the list data structures being overwritten in memory. They will not catch + * data errors caused by incorrect configuration or use of FreeRTOS. */ + listTEST_LIST_INTEGRITY( pxList ); + listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); - /* Insert a new list item into pxList, but rather than sort the list, - makes the new list item the last item to be removed by a call to - listGET_OWNER_OF_NEXT_ENTRY(). */ - pxNewListItem->pxNext = pxIndex; - pxNewListItem->pxPrevious = pxIndex->pxPrevious; + /* Insert a new list item into pxList, but rather than sort the list, + * makes the new list item the last item to be removed by a call to + * listGET_OWNER_OF_NEXT_ENTRY(). */ + pxNewListItem->pxNext = pxIndex; + pxNewListItem->pxPrevious = pxIndex->pxPrevious; - /* Only used during decision coverage testing. */ - mtCOVERAGE_TEST_DELAY(); + /* Only used during decision coverage testing. */ + mtCOVERAGE_TEST_DELAY(); - pxIndex->pxPrevious->pxNext = pxNewListItem; - pxIndex->pxPrevious = pxNewListItem; + pxIndex->pxPrevious->pxNext = pxNewListItem; + pxIndex->pxPrevious = pxNewListItem; - /* Remember which list the item is in. */ - pxNewListItem->pxContainer = pxList; + /* Remember which list the item is in. */ + pxNewListItem->pxContainer = pxList; - ( pxList->uxNumberOfItems )++; + ( pxList->uxNumberOfItems )++; } /*-----------------------------------------------------------*/ -void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) +void vListInsert( List_t * const pxList, + ListItem_t * const pxNewListItem ) { -ListItem_t *pxIterator; -const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; - - /* Only effective when configASSERT() is also defined, these tests may catch - the list data structures being overwritten in memory. They will not catch - data errors caused by incorrect configuration or use of FreeRTOS. */ - listTEST_LIST_INTEGRITY( pxList ); - listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); - - /* Insert the new list item into the list, sorted in xItemValue order. - - If the list already contains a list item with the same item value then the - new list item should be placed after it. This ensures that TCBs which are - stored in ready lists (all of which have the same xItemValue value) get a - share of the CPU. However, if the xItemValue is the same as the back marker - the iteration loop below will not end. Therefore the value is checked - first, and the algorithm slightly modified if necessary. */ - if( xValueOfInsertion == portMAX_DELAY ) - { - pxIterator = pxList->xListEnd.pxPrevious; - } - else - { - /* *** NOTE *********************************************************** - If you find your application is crashing here then likely causes are - listed below. In addition see https://www.freertos.org/FAQHelp.html for - more tips, and ensure configASSERT() is defined! - https://www.freertos.org/a00110.html#configASSERT - - 1) Stack overflow - - see https://www.freertos.org/Stacks-and-stack-overflow-checking.html - 2) Incorrect interrupt priority assignment, especially on Cortex-M - parts where numerically high priority values denote low actual - interrupt priorities, which can seem counter intuitive. See - https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition - of configMAX_SYSCALL_INTERRUPT_PRIORITY on - https://www.freertos.org/a00110.html - 3) Calling an API function from within a critical section or when - the scheduler is suspended, or calling an API function that does - not end in "FromISR" from an interrupt. - 4) Using a queue or semaphore before it has been initialised or - before the scheduler has been started (are interrupts firing - before vTaskStartScheduler() has been called?). - **********************************************************************/ - - for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ - { - /* There is nothing to do here, just iterating to the wanted - insertion position. */ - } - } - - pxNewListItem->pxNext = pxIterator->pxNext; - pxNewListItem->pxNext->pxPrevious = pxNewListItem; - pxNewListItem->pxPrevious = pxIterator; - pxIterator->pxNext = pxNewListItem; - - /* Remember which list the item is in. This allows fast removal of the - item later. */ - pxNewListItem->pxContainer = pxList; - - ( pxList->uxNumberOfItems )++; + ListItem_t * pxIterator; + const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; + + /* Only effective when configASSERT() is also defined, these tests may catch + * the list data structures being overwritten in memory. They will not catch + * data errors caused by incorrect configuration or use of FreeRTOS. */ + listTEST_LIST_INTEGRITY( pxList ); + listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); + + /* Insert the new list item into the list, sorted in xItemValue order. + * + * If the list already contains a list item with the same item value then the + * new list item should be placed after it. This ensures that TCBs which are + * stored in ready lists (all of which have the same xItemValue value) get a + * share of the CPU. However, if the xItemValue is the same as the back marker + * the iteration loop below will not end. Therefore the value is checked + * first, and the algorithm slightly modified if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + /* *** NOTE *********************************************************** + * If you find your application is crashing here then likely causes are + * listed below. In addition see https://www.FreeRTOS.org/FAQHelp.html for + * more tips, and ensure configASSERT() is defined! + * https://www.FreeRTOS.org/a00110.html#configASSERT + * + * 1) Stack overflow - + * see https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html + * 2) Incorrect interrupt priority assignment, especially on Cortex-M + * parts where numerically high priority values denote low actual + * interrupt priorities, which can seem counter intuitive. See + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html and the definition + * of configMAX_SYSCALL_INTERRUPT_PRIORITY on + * https://www.FreeRTOS.org/a00110.html + * 3) Calling an API function from within a critical section or when + * the scheduler is suspended, or calling an API function that does + * not end in "FromISR" from an interrupt. + * 4) Using a queue or semaphore before it has been initialised or + * before the scheduler has been started (are interrupts firing + * before vTaskStartScheduler() has been called?). + **********************************************************************/ + + for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ + { + /* There is nothing to do here, just iterating to the wanted + * insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + * item later. */ + pxNewListItem->pxContainer = pxList; + + ( pxList->uxNumberOfItems )++; } /*-----------------------------------------------------------*/ UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) { /* The list item knows which list it is in. Obtain the list from the list -item. */ -List_t * const pxList = pxItemToRemove->pxContainer; + * item. */ + List_t * const pxList = pxItemToRemove->pxContainer; - pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; - pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; - /* Only used during decision coverage testing. */ - mtCOVERAGE_TEST_DELAY(); + /* Only used during decision coverage testing. */ + mtCOVERAGE_TEST_DELAY(); - /* Make sure the index is left pointing to a valid item. */ - if( pxList->pxIndex == pxItemToRemove ) - { - pxList->pxIndex = pxItemToRemove->pxPrevious; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - pxItemToRemove->pxContainer = NULL; - ( pxList->uxNumberOfItems )--; + pxItemToRemove->pxContainer = NULL; + ( pxList->uxNumberOfItems )--; - return pxList->uxNumberOfItems; + return pxList->uxNumberOfItems; } /*-----------------------------------------------------------*/ - diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/queue.c b/source/Middlewares/Third_Party/FreeRTOS/Source/queue.c index 7c216fac0c..5c9002dc91 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/queue.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/queue.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,116 +19,119 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ #include #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining -all the API functions to use the MPU wrappers. That should only be done when -task.h is included from an application file. */ + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE #include "FreeRTOS.h" -#include "queue.h" #include "task.h" +#include "queue.h" - -#if (configUSE_CO_ROUTINES == 1) -#include "croutine.h" +#if ( configUSE_CO_ROUTINES == 1 ) + #include "croutine.h" #endif /* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified -because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined -for the header files above, but not in this file, in order to generate the -correct privileged Vs unprivileged linkage and placement. */ + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + /* Constants used with the cRxLock and cTxLock structure members. */ -#define queueUNLOCKED ((int8_t)-1) -#define queueLOCKED_UNMODIFIED ((int8_t)0) +#define queueUNLOCKED ( ( int8_t ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( int8_t ) 0 ) +#define queueINT8_MAX ( ( int8_t ) 127 ) /* When the Queue_t structure is used to represent a base queue its pcHead and -pcTail members are used as pointers into the queue storage area. When the -Queue_t structure is used to represent a mutex pcHead and pcTail pointers are -not necessary, and the pcHead pointer is set to NULL to indicate that the -structure instead holds a pointer to the mutex holder (if any). Map alternative -names to the pcHead and structure member to ensure the readability of the code -is maintained. The QueuePointers_t and SemaphoreData_t types are used to form -a union as their usage is mutually exclusive dependent on what the queue is -being used for. */ -#define uxQueueType pcHead -#define queueQUEUE_IS_MUTEX NULL - -typedef struct QueuePointers { - int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ - int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ + * pcTail members are used as pointers into the queue storage area. When the + * Queue_t structure is used to represent a mutex pcHead and pcTail pointers are + * not necessary, and the pcHead pointer is set to NULL to indicate that the + * structure instead holds a pointer to the mutex holder (if any). Map alternative + * names to the pcHead and structure member to ensure the readability of the code + * is maintained. The QueuePointers_t and SemaphoreData_t types are used to form + * a union as their usage is mutually exclusive dependent on what the queue is + * being used for. */ +#define uxQueueType pcHead +#define queueQUEUE_IS_MUTEX NULL + +typedef struct QueuePointers +{ + int8_t * pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t * pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ } QueuePointers_t; -typedef struct SemaphoreData { - TaskHandle_t xMutexHolder; /*< The handle of the task that holds the mutex. */ - UBaseType_t uxRecursiveCallCount; /*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ +typedef struct SemaphoreData +{ + TaskHandle_t xMutexHolder; /*< The handle of the task that holds the mutex. */ + UBaseType_t uxRecursiveCallCount; /*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ } SemaphoreData_t; /* Semaphores do not actually store or copy data, so have an item size of -zero. */ -#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ((UBaseType_t)0) -#define queueMUTEX_GIVE_BLOCK_TIME ((TickType_t)0U) + * zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + +#if ( configUSE_PREEMPTION == 0 ) -#if (configUSE_PREEMPTION == 0) /* If the cooperative scheduler is being used then a yield should not be -performed just because a higher priority task has been woken. */ -#define queueYIELD_IF_USING_PREEMPTION() + * performed just because a higher priority task has been woken. */ + #define queueYIELD_IF_USING_PREEMPTION() #else -#define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() + #define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() #endif /* * Definition of the queue used by the scheduler. * Items are queued by copy, not reference. See the following link for the - * rationale: https://www.freertos.org/Embedded-RTOS-Queues.html + * rationale: https://www.FreeRTOS.org/Embedded-RTOS-Queues.html */ typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { - int8_t *pcHead; /*< Points to the beginning of the queue storage area. */ - int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */ - - union { - QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */ - SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */ - } u; + int8_t * pcHead; /*< Points to the beginning of the queue storage area. */ + int8_t * pcWriteTo; /*< Points to the free next place in the storage area. */ - List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ - List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + union + { + QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */ + SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */ + } u; - volatile UBaseType_t uxMessagesWaiting; /*< The number of items currently in the queue. */ - UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ - UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */ + List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ - volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ - volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + volatile UBaseType_t uxMessagesWaiting; /*< The number of items currently in the queue. */ + UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */ -#if ((configSUPPORT_STATIC_ALLOCATION == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) - uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ -#endif + volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ -#if (configUSE_QUEUE_SETS == 1) - struct QueueDefinition *pxQueueSetContainer; -#endif + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ + #endif -#if (configUSE_TRACE_FACILITY == 1) - UBaseType_t uxQueueNumber; - uint8_t ucQueueType; -#endif + #if ( configUSE_QUEUE_SETS == 1 ) + struct QueueDefinition * pxQueueSetContainer; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif } xQUEUE; /* The old xQUEUE name is maintained above then typedefed to the new Queue_t -name below to enable the use of older kernel aware debuggers. */ + * name below to enable the use of older kernel aware debuggers. */ typedef xQUEUE Queue_t; /*-----------------------------------------------------------*/ @@ -137,25 +140,26 @@ typedef xQUEUE Queue_t; * The queue registry is just a means for kernel aware debuggers to locate * queue structures. It has no other purpose so is an optional component. */ -#if (configQUEUE_REGISTRY_SIZE > 0) +#if ( configQUEUE_REGISTRY_SIZE > 0 ) /* The type stored within the queue registry array. This allows a name -to be assigned to each queue making kernel aware debugging a little -more user friendly. */ -typedef struct QUEUE_REGISTRY_ITEM { - const char *pcQueueName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - QueueHandle_t xHandle; -} xQueueRegistryItem; + * to be assigned to each queue making kernel aware debugging a little + * more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + const char * pcQueueName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + QueueHandle_t xHandle; + } xQueueRegistryItem; /* The old xQueueRegistryItem name is maintained above then typedefed to the -new xQueueRegistryItem name below to enable the use of older kernel aware -debuggers. */ -typedef xQueueRegistryItem QueueRegistryItem_t; + * new xQueueRegistryItem name below to enable the use of older kernel aware + * debuggers. */ + typedef xQueueRegistryItem QueueRegistryItem_t; /* The queue registry is simply an array of QueueRegistryItem_t structures. -The pcQueueName member of a structure being NULL is indicative of the -array position being vacant. */ -PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[configQUEUE_REGISTRY_SIZE]; + * The pcQueueName member of a structure being NULL is indicative of the + * array position being vacant. */ + PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; #endif /* configQUEUE_REGISTRY_SIZE */ @@ -167,57 +171,66 @@ PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[configQUEUE_REGISTRY_SIZE]; * to indicate that a task may require unblocking. When the queue in unlocked * these lock counts are inspected, and the appropriate action taken. */ -static void prvUnlockQueue(Queue_t *const pxQueue) PRIVILEGED_FUNCTION; +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any data in a queue. * * @return pdTRUE if the queue contains no items, otherwise pdFALSE. */ -static BaseType_t prvIsQueueEmpty(const Queue_t *pxQueue) PRIVILEGED_FUNCTION; +static BaseType_t prvIsQueueEmpty( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION; /* * Uses a critical section to determine if there is any space in a queue. * * @return pdTRUE if there is no space, otherwise pdFALSE; */ -static BaseType_t prvIsQueueFull(const Queue_t *pxQueue) PRIVILEGED_FUNCTION; +static BaseType_t prvIsQueueFull( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION; /* * Copies an item into the queue, either at the front of the queue or the * back of the queue. */ -static BaseType_t prvCopyDataToQueue(Queue_t *const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition) PRIVILEGED_FUNCTION; +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, + const void * pvItemToQueue, + const BaseType_t xPosition ) PRIVILEGED_FUNCTION; /* * Copies an item out of a queue. */ -static void prvCopyDataFromQueue(Queue_t *const pxQueue, void *const pvBuffer) PRIVILEGED_FUNCTION; +static void prvCopyDataFromQueue( Queue_t * const pxQueue, + void * const pvBuffer ) PRIVILEGED_FUNCTION; + +#if ( configUSE_QUEUE_SETS == 1 ) -#if (configUSE_QUEUE_SETS == 1) /* * Checks to see if a queue is a member of a queue set, and if so, notifies * the queue set that the queue contains data. */ -static BaseType_t prvNotifyQueueSetContainer(const Queue_t *const pxQueue) PRIVILEGED_FUNCTION; + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; #endif /* * Called after a Queue_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -static void prvInitialiseNewQueue(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue) PRIVILEGED_FUNCTION; +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + const uint8_t ucQueueType, + Queue_t * pxNewQueue ) PRIVILEGED_FUNCTION; /* * Mutexes are a special type of queue. When a mutex is created, first the * queue is created, then prvInitialiseMutex() is called to configure the queue * as a mutex. */ -#if (configUSE_MUTEXES == 1) -static void prvInitialiseMutex(Queue_t *pxNewQueue) PRIVILEGED_FUNCTION; +#if ( configUSE_MUTEXES == 1 ) + static void prvInitialiseMutex( Queue_t * pxNewQueue ) PRIVILEGED_FUNCTION; #endif -#if (configUSE_MUTEXES == 1) +#if ( configUSE_MUTEXES == 1 ) + /* * If a task waiting for a mutex causes the mutex holder to inherit a * priority, but the waiting task times out, then the holder should @@ -225,7 +238,7 @@ static void prvInitialiseMutex(Queue_t *pxNewQueue) PRIVILEGED_FUNCTION; * other tasks that are waiting for the same mutex. This function returns * that priority. */ -static UBaseType_t prvGetDisinheritPriorityAfterTimeout(const Queue_t *const pxQueue) PRIVILEGED_FUNCTION; + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; #endif /*-----------------------------------------------------------*/ @@ -233,2207 +246,2768 @@ static UBaseType_t prvGetDisinheritPriorityAfterTimeout(const Queue_t *const pxQ * Macro to mark a queue as locked. Locking a queue prevents an ISR from * accessing the queue event lists. */ -#define prvLockQueue(pxQueue) \ - taskENTER_CRITICAL(); \ - { \ - if ((pxQueue)->cRxLock == queueUNLOCKED) { \ - (pxQueue)->cRxLock = queueLOCKED_UNMODIFIED; \ - } \ - if ((pxQueue)->cTxLock == queueUNLOCKED) { \ - (pxQueue)->cTxLock = queueLOCKED_UNMODIFIED; \ - } \ - } \ - taskEXIT_CRITICAL() +#define prvLockQueue( pxQueue ) \ + taskENTER_CRITICAL(); \ + { \ + if( ( pxQueue )->cRxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( ( pxQueue )->cTxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL() /*-----------------------------------------------------------*/ -BaseType_t xQueueGenericReset(QueueHandle_t xQueue, BaseType_t xNewQueue) { - Queue_t *const pxQueue = xQueue; - - configASSERT(pxQueue); - - taskENTER_CRITICAL(); - { - pxQueue->u.xQueue.pcTail = pxQueue->pcHead + (pxQueue->uxLength * pxQueue->uxItemSize); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ - pxQueue->uxMessagesWaiting = (UBaseType_t)0U; - pxQueue->pcWriteTo = pxQueue->pcHead; - pxQueue->u.xQueue.pcReadFrom - = pxQueue->pcHead + ((pxQueue->uxLength - 1U) * pxQueue->uxItemSize); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ - pxQueue->cRxLock = queueUNLOCKED; - pxQueue->cTxLock = queueUNLOCKED; - - if (xNewQueue == pdFALSE) { - /* If there are tasks blocked waiting to read from the queue, then - the tasks will remain blocked as after this function exits the queue - will still be empty. If there are tasks blocked waiting to write to - the queue, then one should be unblocked as after this function exits - it will be possible to write to it. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* Ensure the event queues start in the correct state. */ - vListInitialise(&(pxQueue->xTasksWaitingToSend)); - vListInitialise(&(pxQueue->xTasksWaitingToReceive)); +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, + BaseType_t xNewQueue ) +{ + Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; + pxQueue->pcWriteTo = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + pxQueue->cRxLock = queueUNLOCKED; + pxQueue->cTxLock = queueUNLOCKED; + + if( xNewQueue == pdFALSE ) + { + /* If there are tasks blocked waiting to read from the queue, then + * the tasks will remain blocked as after this function exits the queue + * will still be empty. If there are tasks blocked waiting to write to + * the queue, then one should be unblocked as after this function exits + * it will be possible to write to it. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Ensure the event queues start in the correct state. */ + vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); + } } - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); - /* A value is returned for calling semantic consistency with previous - versions. */ - return pdPASS; + /* A value is returned for calling semantic consistency with previous + * versions. */ + return pdPASS; } /*-----------------------------------------------------------*/ -#if (configSUPPORT_STATIC_ALLOCATION == 1) +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) -QueueHandle_t xQueueGenericCreateStatic(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType) { - Queue_t *pxNewQueue; + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue, + const uint8_t ucQueueType ) + { + Queue_t * pxNewQueue; - configASSERT(uxQueueLength > (UBaseType_t)0); + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); - /* The StaticQueue_t structure and the queue storage area must be - supplied. */ - configASSERT(pxStaticQueue != NULL); + /* The StaticQueue_t structure and the queue storage area must be + * supplied. */ + configASSERT( pxStaticQueue != NULL ); - /* A queue storage area should be provided if the item size is not 0, and - should not be provided if the item size is 0. */ - configASSERT(!((pucQueueStorage != NULL) && (uxItemSize == 0))); - configASSERT(!((pucQueueStorage == NULL) && (uxItemSize != 0))); + /* A queue storage area should be provided if the item size is not 0, and + * should not be provided if the item size is 0. */ + configASSERT( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0 ) ) ); + configASSERT( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0 ) ) ); -#if (configASSERT_DEFINED == 1) - { - /* Sanity check that the size of the structure used to declare a - variable of type StaticQueue_t or StaticSemaphore_t equals the size of - the real queue and semaphore structures. */ - volatile size_t xSize = sizeof(StaticQueue_t); - configASSERT(xSize == sizeof(Queue_t)); - (void)xSize; /* Keeps lint quiet when configASSERT() is not defined. */ - } -#endif /* configASSERT_DEFINED */ + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticQueue_t or StaticSemaphore_t equals the size of + * the real queue and semaphore structures. */ + volatile size_t xSize = sizeof( StaticQueue_t ); + configASSERT( xSize == sizeof( Queue_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ + } + #endif /* configASSERT_DEFINED */ - /* The address of a statically allocated queue was passed in, use it. - The address of a statically allocated storage area was also passed in - but is already set. */ - pxNewQueue = (Queue_t *)pxStaticQueue; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + /* The address of a statically allocated queue was passed in, use it. + * The address of a statically allocated storage area was also passed in + * but is already set. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ - if (pxNewQueue != NULL) { -#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) - { - /* Queues can be allocated wither statically or dynamically, so - note this queue was allocated statically in case the queue is - later deleted. */ - pxNewQueue->ucStaticallyAllocated = pdTRUE; - } -#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + if( pxNewQueue != NULL ) + { + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Queues can be allocated wither statically or dynamically, so + * note this queue was allocated statically in case the queue is + * later deleted. */ + pxNewQueue->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - prvInitialiseNewQueue(uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue); - } else { - traceQUEUE_CREATE_FAILED(ucQueueType); - mtCOVERAGE_TEST_MARKER(); - } + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } - return pxNewQueue; -} + return pxNewQueue; + } #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if (configSUPPORT_DYNAMIC_ALLOCATION == 1) - -QueueHandle_t xQueueGenericCreate(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType) { - Queue_t *pxNewQueue; - size_t xQueueSizeInBytes; - uint8_t *pucQueueStorage; - - configASSERT(uxQueueLength > (UBaseType_t)0); - - /* Allocate enough space to hold the maximum number of items that - can be in the queue at any time. It is valid for uxItemSize to be - zero in the case the queue is used as a semaphore. */ - xQueueSizeInBytes = (size_t)(uxQueueLength * uxItemSize); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - /* Allocate the queue and storage area. Justification for MISRA - deviation as follows: pvPortMalloc() always ensures returned memory - blocks are aligned per the requirements of the MCU stack. In this case - pvPortMalloc() must return a pointer that is guaranteed to meet the - alignment requirements of the Queue_t structure - which in this case - is an int8_t *. Therefore, whenever the stack alignment requirements - are greater than or equal to the pointer to char requirements the cast - is safe. In other cases alignment requirements are not strict (one or - two bytes). */ - pxNewQueue = (Queue_t *)pvPortMalloc(sizeof(Queue_t) + xQueueSizeInBytes); /*lint !e9087 !e9079 see comment above. */ - - if (pxNewQueue != NULL) { - /* Jump past the queue structure to find the location of the queue - storage area. */ - pucQueueStorage = (uint8_t *)pxNewQueue; - pucQueueStorage += sizeof(Queue_t); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ - -#if (configSUPPORT_STATIC_ALLOCATION == 1) +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + const uint8_t ucQueueType ) { - /* Queues can be created either statically or dynamically, so - note this task was created dynamically in case it is later - deleted. */ - pxNewQueue->ucStaticallyAllocated = pdFALSE; - } -#endif /* configSUPPORT_STATIC_ALLOCATION */ + Queue_t * pxNewQueue; + size_t xQueueSizeInBytes; + uint8_t * pucQueueStorage; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + /* Allocate enough space to hold the maximum number of items that + * can be in the queue at any time. It is valid for uxItemSize to be + * zero in the case the queue is used as a semaphore. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Check for multiplication overflow. */ + configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) ); + + /* Allocate the queue and storage area. Justification for MISRA + * deviation as follows: pvPortMalloc() always ensures returned memory + * blocks are aligned per the requirements of the MCU stack. In this case + * pvPortMalloc() must return a pointer that is guaranteed to meet the + * alignment requirements of the Queue_t structure - which in this case + * is an int8_t *. Therefore, whenever the stack alignment requirements + * are greater than or equal to the pointer to char requirements the cast + * is safe. In other cases alignment requirements are not strict (one or + * two bytes). */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); /*lint !e9087 !e9079 see comment above. */ + + if( pxNewQueue != NULL ) + { + /* Jump past the queue structure to find the location of the queue + * storage area. */ + pucQueueStorage = ( uint8_t * ) pxNewQueue; + pucQueueStorage += sizeof( Queue_t ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Queues can be created either statically or dynamically, so + * note this task was created dynamically in case it is later + * deleted. */ + pxNewQueue->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ - prvInitialiseNewQueue(uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue); - } else { - traceQUEUE_CREATE_FAILED(ucQueueType); - mtCOVERAGE_TEST_MARKER(); - } + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } - return pxNewQueue; -} + return pxNewQueue; + } #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -static void prvInitialiseNewQueue(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue) { - /* Remove compiler warnings about unused parameters should - configUSE_TRACE_FACILITY not be set to 1. */ - (void)ucQueueType; - - if (uxItemSize == (UBaseType_t)0) { - /* No RAM was allocated for the queue storage area, but PC head cannot - be set to NULL because NULL is used as a key to say the queue is used as - a mutex. Therefore just set pcHead to point to the queue as a benign - value that is known to be within the memory map. */ - pxNewQueue->pcHead = (int8_t *)pxNewQueue; - } else { - /* Set the head to the start of the queue storage area. */ - pxNewQueue->pcHead = (int8_t *)pucQueueStorage; - } - - /* Initialise the queue members as described where the queue type is - defined. */ - pxNewQueue->uxLength = uxQueueLength; - pxNewQueue->uxItemSize = uxItemSize; - (void)xQueueGenericReset(pxNewQueue, pdTRUE); - -#if (configUSE_TRACE_FACILITY == 1) - { pxNewQueue->ucQueueType = ucQueueType; } -#endif /* configUSE_TRACE_FACILITY */ +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + const uint8_t ucQueueType, + Queue_t * pxNewQueue ) +{ + /* Remove compiler warnings about unused parameters should + * configUSE_TRACE_FACILITY not be set to 1. */ + ( void ) ucQueueType; -#if (configUSE_QUEUE_SETS == 1) - { pxNewQueue->pxQueueSetContainer = NULL; } -#endif /* configUSE_QUEUE_SETS */ + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* No RAM was allocated for the queue storage area, but PC head cannot + * be set to NULL because NULL is used as a key to say the queue is used as + * a mutex. Therefore just set pcHead to point to the queue as a benign + * value that is known to be within the memory map. */ + pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; + } + else + { + /* Set the head to the start of the queue storage area. */ + pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; + } + + /* Initialise the queue members as described where the queue type is + * defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); - traceQUEUE_CREATE(pxNewQueue); + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxNewQueue->ucQueueType = ucQueueType; + } + #endif /* configUSE_TRACE_FACILITY */ + + #if ( configUSE_QUEUE_SETS == 1 ) + { + pxNewQueue->pxQueueSetContainer = NULL; + } + #endif /* configUSE_QUEUE_SETS */ + + traceQUEUE_CREATE( pxNewQueue ); } /*-----------------------------------------------------------*/ -#if (configUSE_MUTEXES == 1) +#if ( configUSE_MUTEXES == 1 ) -static void prvInitialiseMutex(Queue_t *pxNewQueue) { - if (pxNewQueue != NULL) { - /* The queue create function will set all the queue structure members - correctly for a generic queue, but this function is creating a - mutex. Overwrite those members that need to be set differently - - in particular the information required for priority inheritance. */ - pxNewQueue->u.xSemaphore.xMutexHolder = NULL; - pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + static void prvInitialiseMutex( Queue_t * pxNewQueue ) + { + if( pxNewQueue != NULL ) + { + /* The queue create function will set all the queue structure members + * correctly for a generic queue, but this function is creating a + * mutex. Overwrite those members that need to be set differently - + * in particular the information required for priority inheritance. */ + pxNewQueue->u.xSemaphore.xMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; - /* In case this is a recursive mutex. */ - pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; + /* In case this is a recursive mutex. */ + pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; - traceCREATE_MUTEX(pxNewQueue); + traceCREATE_MUTEX( pxNewQueue ); - /* Start with the semaphore in the expected state. */ - (void)xQueueGenericSend(pxNewQueue, NULL, (TickType_t)0U, queueSEND_TO_BACK); - } else { - traceCREATE_MUTEX_FAILED(); - } -} + /* Start with the semaphore in the expected state. */ + ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ((configUSE_MUTEXES == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) +#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) -QueueHandle_t xQueueCreateMutex(const uint8_t ucQueueType) { - QueueHandle_t xNewQueue; - const UBaseType_t uxMutexLength = (UBaseType_t)1, uxMutexSize = (UBaseType_t)0; + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; - xNewQueue = xQueueGenericCreate(uxMutexLength, uxMutexSize, ucQueueType); - prvInitialiseMutex((Queue_t *)xNewQueue); + xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return xNewQueue; -} + return xNewQueue; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ((configUSE_MUTEXES == 1) && (configSUPPORT_STATIC_ALLOCATION == 1)) +#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) -QueueHandle_t xQueueCreateMutexStatic(const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue) { - QueueHandle_t xNewQueue; - const UBaseType_t uxMutexLength = (UBaseType_t)1, uxMutexSize = (UBaseType_t)0; + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, + StaticQueue_t * pxStaticQueue ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; - /* Prevent compiler warnings about unused parameters if - configUSE_TRACE_FACILITY does not equal 1. */ - (void)ucQueueType; + /* Prevent compiler warnings about unused parameters if + * configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; - xNewQueue = xQueueGenericCreateStatic(uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType); - prvInitialiseMutex((Queue_t *)xNewQueue); + xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); - return xNewQueue; -} + return xNewQueue; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ((configUSE_MUTEXES == 1) && (INCLUDE_xSemaphoreGetMutexHolder == 1)) - -TaskHandle_t xQueueGetMutexHolder(QueueHandle_t xSemaphore) { - TaskHandle_t pxReturn; - Queue_t *const pxSemaphore = (Queue_t *)xSemaphore; - - /* This function is called by xSemaphoreGetMutexHolder(), and should not - be called directly. Note: This is a good way of determining if the - calling task is the mutex holder, but not a good way of determining the - identity of the mutex holder, as the holder may change between the - following critical section exiting and the function returning. */ - taskENTER_CRITICAL(); - { - if (pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX) { - pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; - } else { - pxReturn = NULL; - } - } - taskEXIT_CRITICAL(); +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + Queue_t * const pxSemaphore = ( Queue_t * ) xSemaphore; + + /* This function is called by xSemaphoreGetMutexHolder(), and should not + * be called directly. Note: This is a good way of determining if the + * calling task is the mutex holder, but not a good way of determining the + * identity of the mutex holder, as the holder may change between the + * following critical section exiting and the function returning. */ + taskENTER_CRITICAL(); + { + if( pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + } + taskEXIT_CRITICAL(); - return pxReturn; -} /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ -#endif +#endif /* if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) */ /*-----------------------------------------------------------*/ -#if ((configUSE_MUTEXES == 1) && (INCLUDE_xSemaphoreGetMutexHolder == 1)) +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) -TaskHandle_t xQueueGetMutexHolderFromISR(QueueHandle_t xSemaphore) { - TaskHandle_t pxReturn; + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; - configASSERT(xSemaphore); + configASSERT( xSemaphore ); - /* Mutexes cannot be used in interrupt service routines, so the mutex - holder should not change in an ISR, and therefore a critical section is - not required here. */ - if (((Queue_t *)xSemaphore)->uxQueueType == queueQUEUE_IS_MUTEX) { - pxReturn = ((Queue_t *)xSemaphore)->u.xSemaphore.xMutexHolder; - } else { - pxReturn = NULL; - } + /* Mutexes cannot be used in interrupt service routines, so the mutex + * holder should not change in an ISR, and therefore a critical section is + * not required here. */ + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( ( Queue_t * ) xSemaphore )->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } - return pxReturn; -} /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ -#endif +#endif /* if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) */ /*-----------------------------------------------------------*/ -#if (configUSE_RECURSIVE_MUTEXES == 1) - -BaseType_t xQueueGiveMutexRecursive(QueueHandle_t xMutex) { - BaseType_t xReturn; - Queue_t *const pxMutex = (Queue_t *)xMutex; - - configASSERT(pxMutex); - - /* If this is the task that holds the mutex then xMutexHolder will not - change outside of this task. If this task does not hold the mutex then - pxMutexHolder can never coincidentally equal the tasks handle, and as - this is the only condition we are interested in it does not matter if - pxMutexHolder is accessed simultaneously by another task. Therefore no - mutual exclusion is required to test the pxMutexHolder variable. */ - if (pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle()) { - traceGIVE_MUTEX_RECURSIVE(pxMutex); - - /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to - the task handle, therefore no underflow check is required. Also, - uxRecursiveCallCount is only modified by the mutex holder, and as - there can only be one, no mutual exclusion is required to modify the - uxRecursiveCallCount member. */ - (pxMutex->u.xSemaphore.uxRecursiveCallCount)--; - - /* Has the recursive call count unwound to 0? */ - if (pxMutex->u.xSemaphore.uxRecursiveCallCount == (UBaseType_t)0) { - /* Return the mutex. This will automatically unblock any other - task that might be waiting to access the mutex. */ - (void)xQueueGenericSend(pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK); - } else { - mtCOVERAGE_TEST_MARKER(); - } +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) - xReturn = pdPASS; - } else { - /* The mutex cannot be given because the calling task is not the - holder. */ - xReturn = pdFAIL; + BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); + + /* If this is the task that holds the mutex then xMutexHolder will not + * change outside of this task. If this task does not hold the mutex then + * pxMutexHolder can never coincidentally equal the tasks handle, and as + * this is the only condition we are interested in it does not matter if + * pxMutexHolder is accessed simultaneously by another task. Therefore no + * mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); - traceGIVE_MUTEX_RECURSIVE_FAILED(pxMutex); - } + /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to + * the task handle, therefore no underflow check is required. Also, + * uxRecursiveCallCount is only modified by the mutex holder, and as + * there can only be one, no mutual exclusion is required to modify the + * uxRecursiveCallCount member. */ + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--; - return xReturn; -} + /* Has the recursive call count unwound to 0? */ + if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + { + /* Return the mutex. This will automatically unblock any other + * task that might be waiting to access the mutex. */ + ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + /* The mutex cannot be given because the calling task is not the + * holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + return xReturn; + } #endif /* configUSE_RECURSIVE_MUTEXES */ /*-----------------------------------------------------------*/ -#if (configUSE_RECURSIVE_MUTEXES == 1) +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) -BaseType_t xQueueTakeMutexRecursive(QueueHandle_t xMutex, TickType_t xTicksToWait) { - BaseType_t xReturn; - Queue_t *const pxMutex = (Queue_t *)xMutex; + BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); - configASSERT(pxMutex); + /* Comments regarding mutual exclusion as per those within + * xQueueGiveMutexRecursive(). */ - /* Comments regarding mutual exclusion as per those within - xQueueGiveMutexRecursive(). */ + traceTAKE_MUTEX_RECURSIVE( pxMutex ); - traceTAKE_MUTEX_RECURSIVE(pxMutex); + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); - if (pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle()) { - (pxMutex->u.xSemaphore.uxRecursiveCallCount)++; - xReturn = pdPASS; - } else { - xReturn = xQueueSemaphoreTake(pxMutex, xTicksToWait); + /* pdPASS will only be returned if the mutex was successfully + * obtained. The calling task may have entered the Blocked state + * before reaching here. */ + if( xReturn != pdFAIL ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + } + else + { + traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + } - /* pdPASS will only be returned if the mutex was successfully - obtained. The calling task may have entered the Blocked state - before reaching here. */ - if (xReturn != pdFAIL) { - (pxMutex->u.xSemaphore.uxRecursiveCallCount)++; - } else { - traceTAKE_MUTEX_RECURSIVE_FAILED(pxMutex); + return xReturn; } - } - - return xReturn; -} #endif /* configUSE_RECURSIVE_MUTEXES */ /*-----------------------------------------------------------*/ -#if ((configUSE_COUNTING_SEMAPHORES == 1) && (configSUPPORT_STATIC_ALLOCATION == 1)) +#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) -QueueHandle_t xQueueCreateCountingSemaphoreStatic(const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue) { - QueueHandle_t xHandle; + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount, + StaticQueue_t * pxStaticQueue ) + { + QueueHandle_t xHandle; - configASSERT(uxMaxCount != 0); - configASSERT(uxInitialCount <= uxMaxCount); + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); - xHandle = xQueueGenericCreateStatic(uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE); + xHandle = xQueueGenericCreateStatic( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); - if (xHandle != NULL) { - ((Queue_t *)xHandle)->uxMessagesWaiting = uxInitialCount; + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; - traceCREATE_COUNTING_SEMAPHORE(); - } else { - traceCREATE_COUNTING_SEMAPHORE_FAILED(); - } + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } - return xHandle; -} + return xHandle; + } #endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ -#if ((configUSE_COUNTING_SEMAPHORES == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) +#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) -QueueHandle_t xQueueCreateCountingSemaphore(const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount) { - QueueHandle_t xHandle; + QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount ) + { + QueueHandle_t xHandle; - configASSERT(uxMaxCount != 0); - configASSERT(uxInitialCount <= uxMaxCount); + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); - xHandle = xQueueGenericCreate(uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE); + xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); - if (xHandle != NULL) { - ((Queue_t *)xHandle)->uxMessagesWaiting = uxInitialCount; + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; - traceCREATE_COUNTING_SEMAPHORE(); - } else { - traceCREATE_COUNTING_SEMAPHORE_FAILED(); - } + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } - return xHandle; -} + return xHandle; + } #endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ -BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition) { - BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; - TimeOut_t xTimeOut; - Queue_t *const pxQueue = xQueue; - - configASSERT(pxQueue); - configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U))); - configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1))); -#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)) - { configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0))); } -#endif +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, + const void * const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition ) +{ + BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif - /*lint -save -e904 This function relaxes the coding standard somewhat to - allow return statements within the function itself. This is done in the - interest of execution time efficiency. */ - for (;;) { - taskENTER_CRITICAL(); + /*lint -save -e904 This function relaxes the coding standard somewhat to + * allow return statements within the function itself. This is done in the + * interest of execution time efficiency. */ + for( ; ; ) { - /* Is there room on the queue now? The running task must be the - highest priority task wanting to access the queue. If the head item - in the queue is to be overwritten then it does not matter if the - queue is full. */ - if ((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE)) { - traceQUEUE_SEND(pxQueue); - -#if (configUSE_QUEUE_SETS == 1) - { - const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; - - xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition); - - if (pxQueue->pxQueueSetContainer != NULL) { - if ((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0)) { - /* Do not notify the queue set as an existing item - was overwritten in the queue so the number of items - in the queue has not changed. */ - mtCOVERAGE_TEST_MARKER(); - } else if (prvNotifyQueueSetContainer(pxQueue) != pdFALSE) { - /* The queue is a member of a queue set, and posting - to the queue set caused a higher priority task to - unblock. A context switch is required. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* If there was a task waiting for data to arrive on the - queue then unblock it now. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The unblocked task has a priority higher than - our own so yield immediately. Yes it is ok to - do this from within the critical section - the - kernel takes care of that. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else if (xYieldRequired != pdFALSE) { - /* This path is a special case that will only get - executed if the task was holding multiple mutexes - and the mutexes were given back in an order that is - different to that in which they were taken. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } - } -#else /* configUSE_QUEUE_SETS */ - { - xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition); - - /* If there was a task waiting for data to arrive on the - queue then unblock it now. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The unblocked task has a priority higher than - our own so yield immediately. Yes it is ok to do - this from within the critical section - the kernel - takes care of that. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else if (xYieldRequired != pdFALSE) { - /* This path is a special case that will only get - executed if the task was holding multiple mutexes and - the mutexes were given back in an order that is - different to that in which they were taken. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? The running task must be the + * highest priority task wanting to access the queue. If the head item + * in the queue is to be overwritten then it does not matter if the + * queue is full. */ + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + traceQUEUE_SEND( pxQueue ); + + #if ( configUSE_QUEUE_SETS == 1 ) + { + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + * was overwritten in the queue so the number of items + * in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + * to the queue set caused a higher priority task to + * unblock. A context switch is required. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If there was a task waiting for data to arrive on the + * queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + * our own so yield immediately. Yes it is ok to + * do this from within the critical section - the + * kernel takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + * executed if the task was holding multiple mutexes + * and the mutexes were given back in an order that is + * different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + * queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + * our own so yield immediately. Yes it is ok to do + * this from within the critical section - the kernel + * takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + * executed if the task was holding multiple mutexes and + * the mutexes were given back in an order that is + * different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was full and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + * the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + * configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } } -#endif /* configUSE_QUEUE_SETS */ - taskEXIT_CRITICAL(); - return pdPASS; - } else { - if (xTicksToWait == (TickType_t)0) { - /* The queue was full and no block time is specified (or - the block time has expired) so leave now. */ - taskEXIT_CRITICAL(); - - /* Return to the original privilege level before exiting - the function. */ - traceQUEUE_SEND_FAILED(pxQueue); - return errQUEUE_FULL; - } else if (xEntryTimeSet == pdFALSE) { - /* The queue was full and a block time was specified so - configure the timeout structure. */ - vTaskInternalSetTimeOutState(&xTimeOut); - xEntryTimeSet = pdTRUE; - } else { - /* Entry time was already set. */ - mtCOVERAGE_TEST_MARKER(); - } - } - } - taskEXIT_CRITICAL(); - /* Interrupts and other tasks can send to and receive from the queue - now the critical section has been exited. */ - - vTaskSuspendAll(); - prvLockQueue(pxQueue); - - /* Update the timeout state to see if it has expired yet. */ - if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) { - if (prvIsQueueFull(pxQueue) != pdFALSE) { - traceBLOCKING_ON_QUEUE_SEND(pxQueue); - vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait); - - /* Unlocking the queue means queue events can effect the - event list. It is possible that interrupts occurring now - remove this task from the event list again - but as the - scheduler is suspended the task will go onto the pending - ready last instead of the actual ready list. */ - prvUnlockQueue(pxQueue); - - /* Resuming the scheduler will move tasks from the pending - ready list into the ready list - so it is feasible that this - task is already in a ready list before it yields - in which - case the yield will not cause a context switch unless there - is also a higher priority task in the pending ready list. */ - if (xTaskResumeAll() == pdFALSE) { - portYIELD_WITHIN_API(); - } - } else { - /* Try again. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - } - } else { - /* The timeout has expired. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - - traceQUEUE_SEND_FAILED(pxQueue); - return errQUEUE_FULL; - } - } /*lint -restore */ -} -/*-----------------------------------------------------------*/ + /* Interrupts and other tasks can send to and receive from the queue + * now the critical section has been exited. */ -BaseType_t xQueueGenericSendFromISR(QueueHandle_t xQueue, const void *const pvItemToQueue, BaseType_t *const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition) { - BaseType_t xReturn; - UBaseType_t uxSavedInterruptStatus; - Queue_t *const pxQueue = xQueue; - - configASSERT(pxQueue); - configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U))); - configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1))); - - /* RTOS ports that support interrupt nesting have the concept of a maximum - system call (or maximum API call) interrupt priority. Interrupts that are - above the maximum system call priority are kept permanently enabled, even - when the RTOS kernel is in a critical section, but cannot make any calls to - FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h - then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has been - assigned a priority above the configured maximum system call priority. - Only FreeRTOS functions that end in FromISR can be called from interrupts - that have been assigned a priority at or (logically) below the maximum - system call interrupt priority. FreeRTOS maintains a separate interrupt - safe API to ensure interrupt entry is as fast and as simple as possible. - More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - /* Similar to xQueueGenericSend, except without blocking if there is no room - in the queue. Also don't directly wake a task that was blocked on a queue - read, instead return a flag to say whether a context switch is required or - not (i.e. has a task with a higher priority than us been woken by this - post). */ - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - if ((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE)) { - const int8_t cTxLock = pxQueue->cTxLock; - const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; - - traceQUEUE_SEND_FROM_ISR(pxQueue); - - /* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a - semaphore or mutex. That means prvCopyDataToQueue() cannot result - in a task disinheriting a priority and prvCopyDataToQueue() can be - called here even though the disinherit function does not check if - the scheduler is suspended before accessing the ready lists. */ - (void)prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition); - - /* The event list is not altered if the queue is locked. This will - be done when the queue is unlocked later. */ - if (cTxLock == queueUNLOCKED) { -#if (configUSE_QUEUE_SETS == 1) - { - if (pxQueue->pxQueueSetContainer != NULL) { - if ((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0)) { - /* Do not notify the queue set as an existing item - was overwritten in the queue so the number of items - in the queue has not changed. */ - mtCOVERAGE_TEST_MARKER(); - } else if (prvNotifyQueueSetContainer(pxQueue) != pdFALSE) { - /* The queue is a member of a queue set, and posting - to the queue set caused a higher priority task to - unblock. A context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so - record that a context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + * event list. It is possible that interrupts occurring now + * remove this task from the event list again - but as the + * scheduler is suspended the task will go onto the pending + * ready last instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + * ready list into the ready list - so it is feasible that this + * task is already in a ready list before it yields - in which + * case the yield will not cause a context switch unless there + * is also a higher priority task in the pending ready list. */ + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); } - } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } } -#else /* configUSE_QUEUE_SETS */ + else { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so record that a - context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); - /* Not used in this path. */ - (void)uxPreviousMessagesWaiting; + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; } -#endif /* configUSE_QUEUE_SETS */ - } else { - /* Increment the lock count so the task that unlocks the queue - knows that data was posted while it was locked. */ - pxQueue->cTxLock = (int8_t)(cTxLock + 1); - } - - xReturn = pdPASS; - } else { - traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue); - xReturn = errQUEUE_FULL; - } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); - - return xReturn; + } /*lint -restore */ } /*-----------------------------------------------------------*/ -BaseType_t xQueueGiveFromISR(QueueHandle_t xQueue, BaseType_t *const pxHigherPriorityTaskWoken) { - BaseType_t xReturn; - UBaseType_t uxSavedInterruptStatus; - Queue_t *const pxQueue = xQueue; - - /* Similar to xQueueGenericSendFromISR() but used with semaphores where the - item size is 0. Don't directly wake a task that was blocked on a queue - read, instead return a flag to say whether a context switch is required or - not (i.e. has a task with a higher priority than us been woken by this - post). */ - - configASSERT(pxQueue); - - /* xQueueGenericSendFromISR() should be used instead of xQueueGiveFromISR() - if the item size is not 0. */ - configASSERT(pxQueue->uxItemSize == 0); - - /* Normally a mutex would not be given from an interrupt, especially if - there is a mutex holder, as priority inheritance makes no sense for an - interrupts, only tasks. */ - configASSERT(!((pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) && (pxQueue->u.xSemaphore.xMutexHolder != NULL))); - - /* RTOS ports that support interrupt nesting have the concept of a maximum - system call (or maximum API call) interrupt priority. Interrupts that are - above the maximum system call priority are kept permanently enabled, even - when the RTOS kernel is in a critical section, but cannot make any calls to - FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h - then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has been - assigned a priority above the configured maximum system call priority. - Only FreeRTOS functions that end in FromISR can be called from interrupts - that have been assigned a priority at or (logically) below the maximum - system call interrupt priority. FreeRTOS maintains a separate interrupt - safe API to ensure interrupt entry is as fast and as simple as possible. - More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - - /* When the queue is used to implement a semaphore no data is ever - moved through the queue but it is still valid to see if the queue 'has - space'. */ - if (uxMessagesWaiting < pxQueue->uxLength) { - const int8_t cTxLock = pxQueue->cTxLock; - - traceQUEUE_SEND_FROM_ISR(pxQueue); - - /* A task can only have an inherited priority if it is a mutex - holder - and if there is a mutex holder then the mutex cannot be - given from an ISR. As this is the ISR version of the function it - can be assumed there is no mutex holder and no need to determine if - priority disinheritance is needed. Simply increase the count of - messages (semaphores) available. */ - pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1; - - /* The event list is not altered if the queue is locked. This will - be done when the queue is unlocked later. */ - if (cTxLock == queueUNLOCKED) { -#if (configUSE_QUEUE_SETS == 1) - { - if (pxQueue->pxQueueSetContainer != NULL) { - if (prvNotifyQueueSetContainer(pxQueue) != pdFALSE) { - /* The semaphore is a member of a queue set, and - posting to the queue set caused a higher priority - task to unblock. A context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so - record that a context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, + const void * const pvItemToQueue, + BaseType_t * const pxHigherPriorityTaskWoken, + const BaseType_t xCopyPosition ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* Similar to xQueueGenericSend, except without blocking if there is no room + * in the queue. Also don't directly wake a task that was blocked on a queue + * read, instead return a flag to say whether a context switch is required or + * not (i.e. has a task with a higher priority than us been woken by this + * post). */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + const int8_t cTxLock = pxQueue->cTxLock; + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + /* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a + * semaphore or mutex. That means prvCopyDataToQueue() cannot result + * in a task disinheriting a priority and prvCopyDataToQueue() can be + * called here even though the disinherit function does not check if + * the scheduler is suspended before accessing the ready lists. */ + ( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* The event list is not altered if the queue is locked. This will + * be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + * was overwritten in the queue so the number of items + * in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + * to the queue set caused a higher priority task to + * unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + * record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Not used in this path. */ + ( void ) uxPreviousMessagesWaiting; + } + #endif /* configUSE_QUEUE_SETS */ } - } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was posted while it was locked. */ + configASSERT( cTxLock != queueINT8_MAX ); + + pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } + + xReturn = pdPASS; } -#else /* configUSE_QUEUE_SETS */ + else { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so record that a - context switch is required. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; } -#endif /* configUSE_QUEUE_SETS */ - } else { - /* Increment the lock count so the task that unlocks the queue - knows that data was posted while it was locked. */ - pxQueue->cTxLock = (int8_t)(cTxLock + 1); - } - - xReturn = pdPASS; - } else { - traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue); - xReturn = errQUEUE_FULL; } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xQueueReceive(QueueHandle_t xQueue, void *const pvBuffer, TickType_t xTicksToWait) { - BaseType_t xEntryTimeSet = pdFALSE; - TimeOut_t xTimeOut; - Queue_t *const pxQueue = xQueue; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + /* Similar to xQueueGenericSendFromISR() but used with semaphores where the + * item size is 0. Don't directly wake a task that was blocked on a queue + * read, instead return a flag to say whether a context switch is required or + * not (i.e. has a task with a higher priority than us been woken by this + * post). */ + + configASSERT( pxQueue ); + + /* xQueueGenericSendFromISR() should be used instead of xQueueGiveFromISR() + * if the item size is not 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Normally a mutex would not be given from an interrupt, especially if + * there is a mutex holder, as priority inheritance makes no sense for an + * interrupts, only tasks. */ + configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->u.xSemaphore.xMutexHolder != NULL ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - /* Check the pointer is not NULL. */ - configASSERT((pxQueue)); + /* When the queue is used to implement a semaphore no data is ever + * moved through the queue but it is still valid to see if the queue 'has + * space'. */ + if( uxMessagesWaiting < pxQueue->uxLength ) + { + const int8_t cTxLock = pxQueue->cTxLock; - /* The buffer into which data is received can only be NULL if the data size - is zero (so no data is copied into the buffer. */ - configASSERT(!(((pvBuffer) == NULL) && ((pxQueue)->uxItemSize != (UBaseType_t)0U))); + traceQUEUE_SEND_FROM_ISR( pxQueue ); -/* Cannot block if the scheduler is suspended. */ -#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)) - { configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0))); } -#endif + /* A task can only have an inherited priority if it is a mutex + * holder - and if there is a mutex holder then the mutex cannot be + * given from an ISR. As this is the ISR version of the function it + * can be assumed there is no mutex holder and no need to determine if + * priority disinheritance is needed. Simply increase the count of + * messages (semaphores) available. */ + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; - /*lint -save -e904 This function relaxes the coding standard somewhat to - allow return statements within the function itself. This is done in the - interest of execution time efficiency. */ - for (;;) { - taskENTER_CRITICAL(); - { - const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - - /* Is there data in the queue now? To be running the calling task - must be the highest priority task wanting to access the queue. */ - if (uxMessagesWaiting > (UBaseType_t)0) { - /* Data available, remove one item. */ - prvCopyDataFromQueue(pxQueue, pvBuffer); - traceQUEUE_RECEIVE(pxQueue); - pxQueue->uxMessagesWaiting = uxMessagesWaiting - (UBaseType_t)1; - - /* There is now space in the queue, were any tasks waiting to - post to the queue? If so, unblock the highest priority waiting - task. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } + /* The event list is not altered if the queue is locked. This will + * be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The semaphore is a member of a queue set, and + * posting to the queue set caused a higher priority + * task to unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + * record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was posted while it was locked. */ + configASSERT( cTxLock != queueINT8_MAX ); - taskEXIT_CRITICAL(); - return pdPASS; - } else { - if (xTicksToWait == (TickType_t)0) { - /* The queue was empty and no block time is specified (or - the block time has expired) so leave now. */ - taskEXIT_CRITICAL(); - traceQUEUE_RECEIVE_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else if (xEntryTimeSet == pdFALSE) { - /* The queue was empty and a block time was specified so - configure the timeout structure. */ - vTaskInternalSetTimeOutState(&xTimeOut); - xEntryTimeSet = pdTRUE; - } else { - /* Entry time was already set. */ - mtCOVERAGE_TEST_MARKER(); - } - } - } - taskEXIT_CRITICAL(); + pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } - /* Interrupts and other tasks can send to and receive from the queue - now the critical section has been exited. */ - - vTaskSuspendAll(); - prvLockQueue(pxQueue); - - /* Update the timeout state to see if it has expired yet. */ - if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) { - /* The timeout has not expired. If the queue is still empty place - the task on the list of tasks waiting to receive from the queue. */ - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { - traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue); - vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait); - prvUnlockQueue(pxQueue); - if (xTaskResumeAll() == pdFALSE) { - portYIELD_WITHIN_API(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* The queue contains data again. Loop back to try and read the - data. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - } - } else { - /* Timed out. If there is no data in the queue exit, otherwise loop - back and attempt to read the data. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { - traceQUEUE_RECEIVE_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else { - mtCOVERAGE_TEST_MARKER(); - } + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } } - } /*lint -restore */ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xQueueSemaphoreTake(QueueHandle_t xQueue, TickType_t xTicksToWait) { - BaseType_t xEntryTimeSet = pdFALSE; - TimeOut_t xTimeOut; - Queue_t *const pxQueue = xQueue; - -#if (configUSE_MUTEXES == 1) - BaseType_t xInheritanceOccurred = pdFALSE; -#endif +BaseType_t xQueueReceive( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; - /* Check the queue pointer is not NULL. */ - configASSERT((pxQueue)); + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); - /* Check this really is a semaphore, in which case the item size will be - 0. */ - configASSERT(pxQueue->uxItemSize == 0); + /* The buffer into which data is received can only be NULL if the data size + * is zero (so no data is copied into the buffer). */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); -/* Cannot block if the scheduler is suspended. */ -#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)) - { configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0))); } -#endif + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif - /*lint -save -e904 This function relaxes the coding standard somewhat to allow return - statements within the function itself. This is done in the interest - of execution time efficiency. */ - for (;;) { - taskENTER_CRITICAL(); + /*lint -save -e904 This function relaxes the coding standard somewhat to + * allow return statements within the function itself. This is done in the + * interest of execution time efficiency. */ + for( ; ; ) { - /* Semaphores are queues with an item size of 0, and where the - number of messages in the queue is the semaphore's count value. */ - const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; - - /* Is there data in the queue now? To be running the calling task - must be the highest priority task wanting to access the queue. */ - if (uxSemaphoreCount > (UBaseType_t)0) { - traceQUEUE_RECEIVE(pxQueue); - - /* Semaphores are queues with a data size of zero and where the - messages waiting is the semaphore's count. Reduce the count. */ - pxQueue->uxMessagesWaiting = uxSemaphoreCount - (UBaseType_t)1; - -#if (configUSE_MUTEXES == 1) - { - if (pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) { - /* Record the information required to implement - priority inheritance should it become necessary. */ - pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } -#endif /* configUSE_MUTEXES */ + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - /* Check to see if other tasks are blocked waiting to give the - semaphore, and if so, unblock the highest priority such task. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data available, remove one item. */ + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_RECEIVE( pxQueue ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; + + /* There is now space in the queue, were any tasks waiting to + * post to the queue? If so, unblock the highest priority waiting + * task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + * configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } taskEXIT_CRITICAL(); - return pdPASS; - } else { - if (xTicksToWait == (TickType_t)0) { -/* For inheritance to have occurred there must have been an -initial timeout, and an adjusted timeout cannot become 0, as -if it were 0 the function would have exited. */ -#if (configUSE_MUTEXES == 1) - { configASSERT(xInheritanceOccurred == pdFALSE); } -#endif /* configUSE_MUTEXES */ - - /* The semaphore count was 0 and no block time is specified - (or the block time has expired) so exit now. */ - taskEXIT_CRITICAL(); - traceQUEUE_RECEIVE_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else if (xEntryTimeSet == pdFALSE) { - /* The semaphore count was 0 and a block time was specified - so configure the timeout structure ready to block. */ - vTaskInternalSetTimeOutState(&xTimeOut); - xEntryTimeSet = pdTRUE; - } else { - /* Entry time was already set. */ - mtCOVERAGE_TEST_MARKER(); - } - } - } - taskEXIT_CRITICAL(); - /* Interrupts and other tasks can give to and take from the semaphore - now the critical section has been exited. */ + /* Interrupts and other tasks can send to and receive from the queue + * now the critical section has been exited. */ - vTaskSuspendAll(); - prvLockQueue(pxQueue); + vTaskSuspendAll(); + prvLockQueue( pxQueue ); - /* Update the timeout state to see if it has expired yet. */ - if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) { - /* A block time is specified and not expired. If the semaphore - count is 0 then enter the Blocked state to wait for a semaphore to - become available. As semaphores are implemented with queues the - queue being empty is equivalent to the semaphore count being 0. */ - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { - traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue); - -#if (configUSE_MUTEXES == 1) + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { - if (pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) { - taskENTER_CRITICAL(); - { xInheritanceOccurred = xTaskPriorityInherit(pxQueue->u.xSemaphore.xMutexHolder); } - taskEXIT_CRITICAL(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } -#endif - - vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait); - prvUnlockQueue(pxQueue); - if (xTaskResumeAll() == pdFALSE) { - portYIELD_WITHIN_API(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* There was no timeout and the semaphore count was not 0, so - attempt to take the semaphore again. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - } - } else { - /* Timed out. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - - /* If the semaphore count is 0 exit now as the timeout has - expired. Otherwise return to attempt to take the semaphore that is - known to be available. As semaphores are implemented by queues the - queue being empty is equivalent to the semaphore count being 0. */ - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { -#if (configUSE_MUTEXES == 1) - { - /* xInheritanceOccurred could only have be set if - pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to - test the mutex type again to check it is actually a mutex. */ - if (xInheritanceOccurred != pdFALSE) { - taskENTER_CRITICAL(); + /* The timeout has not expired. If the queue is still empty place + * the task on the list of tasks waiting to receive from the queue. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) { - UBaseType_t uxHighestWaitingPriority; + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); - /* This task blocking on the mutex caused another - task to inherit this task's priority. Now this task - has timed out the priority should be disinherited - again, but only as low as the next highest priority - task that is waiting for the same mutex. */ - uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue); - vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The queue contains data again. Loop back to try and read the + * data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); } - taskEXIT_CRITICAL(); - } } -#endif /* configUSE_MUTEXES */ + else + { + /* Timed out. If there is no data in the queue exit, otherwise loop + * back and attempt to read the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); - traceQUEUE_RECEIVE_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } - } /*lint -restore */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ } /*-----------------------------------------------------------*/ -BaseType_t xQueuePeek(QueueHandle_t xQueue, void *const pvBuffer, TickType_t xTicksToWait) { - BaseType_t xEntryTimeSet = pdFALSE; - TimeOut_t xTimeOut; - int8_t *pcOriginalReadPosition; - Queue_t *const pxQueue = xQueue; +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; - /* Check the pointer is not NULL. */ - configASSERT((pxQueue)); + #if ( configUSE_MUTEXES == 1 ) + BaseType_t xInheritanceOccurred = pdFALSE; + #endif - /* The buffer into which data is received can only be NULL if the data size - is zero (so no data is copied into the buffer. */ - configASSERT(!(((pvBuffer) == NULL) && ((pxQueue)->uxItemSize != (UBaseType_t)0U))); + /* Check the queue pointer is not NULL. */ + configASSERT( ( pxQueue ) ); -/* Cannot block if the scheduler is suspended. */ -#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)) - { configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0))); } -#endif + /* Check this really is a semaphore, in which case the item size will be + * 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); - /*lint -save -e904 This function relaxes the coding standard somewhat to - allow return statements within the function itself. This is done in the - interest of execution time efficiency. */ - for (;;) { - taskENTER_CRITICAL(); + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + /*lint -save -e904 This function relaxes the coding standard somewhat to allow return + * statements within the function itself. This is done in the interest + * of execution time efficiency. */ + for( ; ; ) { - const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - - /* Is there data in the queue now? To be running the calling task - must be the highest priority task wanting to access the queue. */ - if (uxMessagesWaiting > (UBaseType_t)0) { - /* Remember the read position so it can be reset after the data - is read from the queue as this function is only peeking the - data, not removing it. */ - pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; - - prvCopyDataFromQueue(pxQueue, pvBuffer); - traceQUEUE_PEEK(pxQueue); - - /* The data is not being removed, so reset the read pointer. */ - pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; - - /* The data is being left in the queue, so see if there are - any other tasks waiting for the data. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority than this task. */ - queueYIELD_IF_USING_PREEMPTION(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); + taskENTER_CRITICAL(); + { + /* Semaphores are queues with an item size of 0, and where the + * number of messages in the queue is the semaphore's count value. */ + const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxSemaphoreCount > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Semaphores are queues with a data size of zero and where the + * messages waiting is the semaphore's count. Reduce the count. */ + pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1; + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + * priority inheritance should it become necessary. */ + pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + + /* Check to see if other tasks are blocked waiting to give the + * semaphore, and if so, unblock the highest priority such task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* For inheritance to have occurred there must have been an + * initial timeout, and an adjusted timeout cannot become 0, as + * if it were 0 the function would have exited. */ + #if ( configUSE_MUTEXES == 1 ) + { + configASSERT( xInheritanceOccurred == pdFALSE ); + } + #endif /* configUSE_MUTEXES */ + + /* The semaphore count was 0 and no block time is specified + * (or the block time has expired) so exit now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The semaphore count was 0 and a block time was specified + * so configure the timeout structure ready to block. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can give to and take from the semaphore + * now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* A block time is specified and not expired. If the semaphore + * count is 0 then enter the Blocked state to wait for a semaphore to + * become available. As semaphores are implemented with queues the + * queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + taskENTER_CRITICAL(); + { + xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder ); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* if ( configUSE_MUTEXES == 1 ) */ + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There was no timeout and the semaphore count was not 0, so + * attempt to take the semaphore again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* If the semaphore count is 0 exit now as the timeout has + * expired. Otherwise return to attempt to take the semaphore that is + * known to be available. As semaphores are implemented by queues the + * queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + #if ( configUSE_MUTEXES == 1 ) + { + /* xInheritanceOccurred could only have be set if + * pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to + * test the mutex type again to check it is actually a mutex. */ + if( xInheritanceOccurred != pdFALSE ) + { + taskENTER_CRITICAL(); + { + UBaseType_t uxHighestWaitingPriority; + + /* This task blocking on the mutex caused another + * task to inherit this task's priority. Now this task + * has timed out the priority should be disinherited + * again, but only as low as the next highest priority + * task that is waiting for the same mutex. */ + uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue ); + vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority ); + } + taskEXIT_CRITICAL(); + } + } + #endif /* configUSE_MUTEXES */ + + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeek( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + int8_t * pcOriginalReadPosition; + Queue_t * const pxQueue = xQueue; + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + * is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); } + #endif + + /*lint -save -e904 This function relaxes the coding standard somewhat to + * allow return statements within the function itself. This is done in the + * interest of execution time efficiency. */ + for( ; ; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position so it can be reset after the data + * is read from the queue as this function is only peeking the + * data, not removing it. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read pointer. */ + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + * any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + * configure the timeout structure ready to enter the blocked + * state. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } taskEXIT_CRITICAL(); - return pdPASS; - } else { - if (xTicksToWait == (TickType_t)0) { - /* The queue was empty and no block time is specified (or - the block time has expired) so leave now. */ - taskEXIT_CRITICAL(); - traceQUEUE_PEEK_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else if (xEntryTimeSet == pdFALSE) { - /* The queue was empty and a block time was specified so - configure the timeout structure ready to enter the blocked - state. */ - vTaskInternalSetTimeOutState(&xTimeOut); - xEntryTimeSet = pdTRUE; - } else { - /* Entry time was already set. */ - mtCOVERAGE_TEST_MARKER(); - } - } - } - taskEXIT_CRITICAL(); - /* Interrupts and other tasks can send to and receive from the queue - now the critical section has been exited. */ - - vTaskSuspendAll(); - prvLockQueue(pxQueue); - - /* Update the timeout state to see if it has expired yet. */ - if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE) { - /* Timeout has not expired yet, check to see if there is data in the - queue now, and if not enter the Blocked state to wait for data. */ - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { - traceBLOCKING_ON_QUEUE_PEEK(pxQueue); - vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait); - prvUnlockQueue(pxQueue); - if (xTaskResumeAll() == pdFALSE) { - portYIELD_WITHIN_API(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* There is data in the queue now, so don't enter the blocked - state, instead return to try and obtain the data. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - } - } else { - /* The timeout has expired. If there is still no data in the queue - exit, otherwise go back and try to read the data again. */ - prvUnlockQueue(pxQueue); - (void)xTaskResumeAll(); - - if (prvIsQueueEmpty(pxQueue) != pdFALSE) { - traceQUEUE_PEEK_FAILED(pxQueue); - return errQUEUE_EMPTY; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } - } /*lint -restore */ + /* Interrupts and other tasks can send to and receive from the queue + * now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* Timeout has not expired yet, check to see if there is data in the + * queue now, and if not enter the Blocked state to wait for data. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_PEEK( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There is data in the queue now, so don't enter the blocked + * state, instead return to try and obtain the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. If there is still no data in the queue + * exit, otherwise go back and try to read the data again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ } /*-----------------------------------------------------------*/ -BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *const pvBuffer, BaseType_t *const pxHigherPriorityTaskWoken) { - BaseType_t xReturn; - UBaseType_t uxSavedInterruptStatus; - Queue_t *const pxQueue = xQueue; - - configASSERT(pxQueue); - configASSERT(!((pvBuffer == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U))); - - /* RTOS ports that support interrupt nesting have the concept of a maximum - system call (or maximum API call) interrupt priority. Interrupts that are - above the maximum system call priority are kept permanently enabled, even - when the RTOS kernel is in a critical section, but cannot make any calls to - FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h - then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has been - assigned a priority above the configured maximum system call priority. - Only FreeRTOS functions that end in FromISR can be called from interrupts - that have been assigned a priority at or (logically) below the maximum - system call interrupt priority. FreeRTOS maintains a separate interrupt - safe API to ensure interrupt entry is as fast and as simple as possible. - More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; - - /* Cannot block in an ISR, so check there is data available. */ - if (uxMessagesWaiting > (UBaseType_t)0) { - const int8_t cRxLock = pxQueue->cRxLock; - - traceQUEUE_RECEIVE_FROM_ISR(pxQueue); - - prvCopyDataFromQueue(pxQueue, pvBuffer); - pxQueue->uxMessagesWaiting = uxMessagesWaiting - (UBaseType_t)1; - - /* If the queue is locked the event list will not be modified. - Instead update the lock count so the task that unlocks the queue - will know that an ISR has removed data while the queue was - locked. */ - if (cRxLock == queueUNLOCKED) { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - /* The task waiting has a higher priority than us so - force a context switch. */ - if (pxHigherPriorityTaskWoken != NULL) { - *pxHigherPriorityTaskWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* Increment the lock count so the task that unlocks the queue - knows that data was removed while it was locked. */ - pxQueue->cRxLock = (int8_t)(cRxLock + 1); - } - - xReturn = pdPASS; - } else { - xReturn = pdFAIL; - traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue); +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, + void * const pvBuffer, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Cannot block in an ISR, so check there is data available. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + const int8_t cRxLock = pxQueue->cRxLock; + + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; + + /* If the queue is locked the event list will not be modified. + * Instead update the lock count so the task that unlocks the queue + * will know that an ISR has removed data while the queue was + * locked. */ + if( cRxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + * force a context switch. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was removed while it was locked. */ + configASSERT( cRxLock != queueINT8_MAX ); + + pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, void *const pvBuffer) { - BaseType_t xReturn; - UBaseType_t uxSavedInterruptStatus; - int8_t *pcOriginalReadPosition; - Queue_t *const pxQueue = xQueue; - - configASSERT(pxQueue); - configASSERT(!((pvBuffer == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U))); - configASSERT(pxQueue->uxItemSize != 0); /* Can't peek a semaphore. */ - - /* RTOS ports that support interrupt nesting have the concept of a maximum - system call (or maximum API call) interrupt priority. Interrupts that are - above the maximum system call priority are kept permanently enabled, even - when the RTOS kernel is in a critical section, but cannot make any calls to - FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h - then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has been - assigned a priority above the configured maximum system call priority. - Only FreeRTOS functions that end in FromISR can be called from interrupts - that have been assigned a priority at or (logically) below the maximum - system call interrupt priority. FreeRTOS maintains a separate interrupt - safe API to ensure interrupt entry is as fast and as simple as possible. - More information (albeit Cortex-M specific) is provided on the following - link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - /* Cannot block in an ISR, so check there is data available. */ - if (pxQueue->uxMessagesWaiting > (UBaseType_t)0) { - traceQUEUE_PEEK_FROM_ISR(pxQueue); - - /* Remember the read position so it can be reset as nothing is - actually being removed from the queue. */ - pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; - prvCopyDataFromQueue(pxQueue, pvBuffer); - pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; - - xReturn = pdPASS; - } else { - xReturn = pdFAIL; - traceQUEUE_PEEK_FROM_ISR_FAILED(pxQueue); +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, + void * const pvBuffer ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + int8_t * pcOriginalReadPosition; + Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( pxQueue->uxItemSize != 0 ); /* Can't peek a semaphore. */ + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Cannot block in an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + traceQUEUE_PEEK_FROM_ISR( pxQueue ); + + /* Remember the read position so it can be reset as nothing is + * actually being removed from the queue. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ); + } } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -UBaseType_t uxQueueMessagesWaiting(const QueueHandle_t xQueue) { - UBaseType_t uxReturn; +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; - configASSERT(xQueue); + configASSERT( xQueue ); - taskENTER_CRITICAL(); - { uxReturn = ((Queue_t *)xQueue)->uxMessagesWaiting; } - taskEXIT_CRITICAL(); + taskENTER_CRITICAL(); + { + uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); - return uxReturn; + return uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ /*-----------------------------------------------------------*/ -UBaseType_t uxQueueSpacesAvailable(const QueueHandle_t xQueue) { - UBaseType_t uxReturn; - Queue_t *const pxQueue = xQueue; +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; + Queue_t * const pxQueue = xQueue; - configASSERT(pxQueue); + configASSERT( pxQueue ); - taskENTER_CRITICAL(); - { uxReturn = pxQueue->uxLength - pxQueue->uxMessagesWaiting; } - taskEXIT_CRITICAL(); + taskENTER_CRITICAL(); + { + uxReturn = pxQueue->uxLength - pxQueue->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); - return uxReturn; + return uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ /*-----------------------------------------------------------*/ -UBaseType_t uxQueueMessagesWaitingFromISR(const QueueHandle_t xQueue) { - UBaseType_t uxReturn; - Queue_t *const pxQueue = xQueue; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; + Queue_t * const pxQueue = xQueue; - configASSERT(pxQueue); - uxReturn = pxQueue->uxMessagesWaiting; + configASSERT( pxQueue ); + uxReturn = pxQueue->uxMessagesWaiting; - return uxReturn; + return uxReturn; } /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ /*-----------------------------------------------------------*/ -void vQueueDelete(QueueHandle_t xQueue) { - Queue_t *const pxQueue = xQueue; +void vQueueDelete( QueueHandle_t xQueue ) +{ + Queue_t * const pxQueue = xQueue; - configASSERT(pxQueue); - traceQUEUE_DELETE(pxQueue); + configASSERT( pxQueue ); + traceQUEUE_DELETE( pxQueue ); -#if (configQUEUE_REGISTRY_SIZE > 0) - { vQueueUnregisterQueue(pxQueue); } -#endif + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueUnregisterQueue( pxQueue ); + } + #endif -#if ((configSUPPORT_DYNAMIC_ALLOCATION == 1) && (configSUPPORT_STATIC_ALLOCATION == 0)) - { - /* The queue can only have been allocated dynamically - free it - again. */ - vPortFree(pxQueue); - } -#elif ((configSUPPORT_DYNAMIC_ALLOCATION == 1) && (configSUPPORT_STATIC_ALLOCATION == 1)) - { - /* The queue could have been allocated statically or dynamically, so - check before attempting to free the memory. */ - if (pxQueue->ucStaticallyAllocated == (uint8_t)pdFALSE) { - vPortFree(pxQueue); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } -#else - { - /* The queue must have been statically allocated, so is not going to be - deleted. Avoid compiler warnings about the unused parameter. */ - (void)pxQueue; - } -#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The queue can only have been allocated dynamically - free it + * again. */ + vPortFree( pxQueue ); + } + #elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The queue could have been allocated statically or dynamically, so + * check before attempting to free the memory. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxQueue ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) */ + { + /* The queue must have been statically allocated, so is not going to be + * deleted. Avoid compiler warnings about the unused parameter. */ + ( void ) pxQueue; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } /*-----------------------------------------------------------*/ -#if (configUSE_TRACE_FACILITY == 1) +#if ( configUSE_TRACE_FACILITY == 1 ) -UBaseType_t uxQueueGetQueueNumber(QueueHandle_t xQueue) { return ((Queue_t *)xQueue)->uxQueueNumber; } + UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->uxQueueNumber; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if (configUSE_TRACE_FACILITY == 1) +#if ( configUSE_TRACE_FACILITY == 1 ) -void vQueueSetQueueNumber(QueueHandle_t xQueue, UBaseType_t uxQueueNumber) { ((Queue_t *)xQueue)->uxQueueNumber = uxQueueNumber; } + void vQueueSetQueueNumber( QueueHandle_t xQueue, + UBaseType_t uxQueueNumber ) + { + ( ( Queue_t * ) xQueue )->uxQueueNumber = uxQueueNumber; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if (configUSE_TRACE_FACILITY == 1) +#if ( configUSE_TRACE_FACILITY == 1 ) -uint8_t ucQueueGetQueueType(QueueHandle_t xQueue) { return ((Queue_t *)xQueue)->ucQueueType; } + uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->ucQueueType; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if (configUSE_MUTEXES == 1) - -static UBaseType_t prvGetDisinheritPriorityAfterTimeout(const Queue_t *const pxQueue) { - UBaseType_t uxHighestPriorityOfWaitingTasks; +#if ( configUSE_MUTEXES == 1 ) - /* If a task waiting for a mutex causes the mutex holder to inherit a - priority, but the waiting task times out, then the holder should - disinherit the priority - but only down to the highest priority of any - other tasks that are waiting for the same mutex. For this purpose, - return the priority of the highest priority task that is waiting for the - mutex. */ - if (listCURRENT_LIST_LENGTH(&(pxQueue->xTasksWaitingToReceive)) > 0U) { - uxHighestPriorityOfWaitingTasks = (UBaseType_t)configMAX_PRIORITIES - (UBaseType_t)listGET_ITEM_VALUE_OF_HEAD_ENTRY(&(pxQueue->xTasksWaitingToReceive)); - } else { - uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; - } + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) + { + UBaseType_t uxHighestPriorityOfWaitingTasks; + + /* If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. For this purpose, + * return the priority of the highest priority task that is waiting for the + * mutex. */ + if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U ) + { + uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) ); + } + else + { + uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; + } - return uxHighestPriorityOfWaitingTasks; -} + return uxHighestPriorityOfWaitingTasks; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ -static BaseType_t prvCopyDataToQueue(Queue_t *const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition) { - BaseType_t xReturn = pdFALSE; - UBaseType_t uxMessagesWaiting; +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, + const void * pvItemToQueue, + const BaseType_t xPosition ) +{ + BaseType_t xReturn = pdFALSE; + UBaseType_t uxMessagesWaiting; - /* This function is called from a critical section. */ + /* This function is called from a critical section. */ - uxMessagesWaiting = pxQueue->uxMessagesWaiting; + uxMessagesWaiting = pxQueue->uxMessagesWaiting; - if (pxQueue->uxItemSize == (UBaseType_t)0) { -#if (configUSE_MUTEXES == 1) + if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ) { - if (pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) { - /* The mutex is no longer being held. */ - xReturn = xTaskPriorityDisinherit(pxQueue->u.xSemaphore.xMutexHolder); - pxQueue->u.xSemaphore.xMutexHolder = NULL; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } -#endif /* configUSE_MUTEXES */ - } else if (xPosition == queueSEND_TO_BACK) { - if (pvItemToQueue) { - (void)memcpy( - (void *)pxQueue->pcWriteTo, pvItemToQueue, - (size_t)pxQueue->uxItemSize); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to - memcpy() if the copy size is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); + pxQueue->u.xSemaphore.xMutexHolder = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ } - pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ - if (pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + else if( xPosition == queueSEND_TO_BACK ) { - pxQueue->pcWriteTo = pxQueue->pcHead; - } else { - mtCOVERAGE_TEST_MARKER(); + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + + if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } - } else { - (void)memcpy((void *)pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, - (size_t)pxQueue->uxItemSize); /*lint !e961 !e9087 !e418 MISRA exception as the casts are only redundant for some ports. Cast to void required by function signature and safe as no - alignment requirement and copy length specified in bytes. Assert checks null pointer only used when length is 0. */ - pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; - if (pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + else { - pxQueue->u.xQueue.pcReadFrom = (pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize); - } else { - mtCOVERAGE_TEST_MARKER(); - } + ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e9087 !e418 MISRA exception as the casts are only redundant for some ports. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. Assert checks null pointer only used when length is 0. */ + pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - if (xPosition == queueOVERWRITE) { - if (uxMessagesWaiting > (UBaseType_t)0) { - /* An item is not being added but overwritten, so subtract - one from the recorded number of items in the queue so when - one is added again below the number of recorded items remains - correct. */ - --uxMessagesWaiting; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); + if( xPosition == queueOVERWRITE ) + { + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* An item is not being added but overwritten, so subtract + * one from the recorded number of items in the queue so when + * one is added again below the number of recorded items remains + * correct. */ + --uxMessagesWaiting; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } - } - pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1; + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -static void prvCopyDataFromQueue(Queue_t *const pxQueue, void *const pvBuffer) { - if (pxQueue->uxItemSize != (UBaseType_t)0) { - pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ - if (pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ +static void prvCopyDataFromQueue( Queue_t * const pxQueue, + void * const pvBuffer ) +{ + if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) { - pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; - } else { - mtCOVERAGE_TEST_MARKER(); + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ } - (void)memcpy( - (void *)pvBuffer, (void *)pxQueue->u.xQueue.pcReadFrom, - (size_t)pxQueue->uxItemSize); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to - memcpy() when the count is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ - } } /*-----------------------------------------------------------*/ -static void prvUnlockQueue(Queue_t *const pxQueue) { - /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ - - /* The lock counts contains the number of extra data items placed or - removed from the queue while the queue was locked. When a queue is - locked items can be added or removed, but the event lists cannot be - updated. */ - taskENTER_CRITICAL(); - { - int8_t cTxLock = pxQueue->cTxLock; - - /* See if data was added to the queue while it was locked. */ - while (cTxLock > queueLOCKED_UNMODIFIED) { -/* Data was posted while the queue was locked. Are any tasks -blocked waiting for data to become available? */ -#if (configUSE_QUEUE_SETS == 1) - { - if (pxQueue->pxQueueSetContainer != NULL) { - if (prvNotifyQueueSetContainer(pxQueue) != pdFALSE) { - /* The queue is a member of a queue set, and posting to - the queue set caused a higher priority task to unblock. - A context switch is required. */ - vTaskMissedYield(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - /* Tasks that are removed from the event list will get - added to the pending ready list as the scheduler is still - suspended. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so record that a - context switch is required. */ - vTaskMissedYield(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - break; - } - } - } -#else /* configUSE_QUEUE_SETS */ - { - /* Tasks that are removed from the event list will get added to - the pending ready list as the scheduler is still suspended. */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority so record that - a context switch is required. */ - vTaskMissedYield(); - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - break; +static void prvUnlockQueue( Queue_t * const pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + * removed from the queue while the queue was locked. When a queue is + * locked items can be added or removed, but the event lists cannot be + * updated. */ + taskENTER_CRITICAL(); + { + int8_t cTxLock = pxQueue->cTxLock; + + /* See if data was added to the queue while it was locked. */ + while( cTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + * blocked waiting for data to become available? */ + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting to + * the queue set caused a higher priority task to unblock. + * A context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Tasks that are removed from the event list will get + * added to the pending ready list as the scheduler is still + * suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + /* Tasks that are removed from the event list will get added to + * the pending ready list as the scheduler is still suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that + * a context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + #endif /* configUSE_QUEUE_SETS */ + + --cTxLock; } - } -#endif /* configUSE_QUEUE_SETS */ - --cTxLock; + pxQueue->cTxLock = queueUNLOCKED; } + taskEXIT_CRITICAL(); - pxQueue->cTxLock = queueUNLOCKED; - } - taskEXIT_CRITICAL(); + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + int8_t cRxLock = pxQueue->cRxLock; - /* Do the same for the Rx lock. */ - taskENTER_CRITICAL(); - { - int8_t cRxLock = pxQueue->cRxLock; + while( cRxLock > queueLOCKED_UNMODIFIED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } - while (cRxLock > queueLOCKED_UNMODIFIED) { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - vTaskMissedYield(); - } else { - mtCOVERAGE_TEST_MARKER(); + --cRxLock; + } + else + { + break; + } } - --cRxLock; - } else { - break; - } + pxQueue->cRxLock = queueUNLOCKED; } - - pxQueue->cRxLock = queueUNLOCKED; - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); } /*-----------------------------------------------------------*/ -static BaseType_t prvIsQueueEmpty(const Queue_t *pxQueue) { - BaseType_t xReturn; +static BaseType_t prvIsQueueEmpty( const Queue_t * pxQueue ) +{ + BaseType_t xReturn; - taskENTER_CRITICAL(); - { - if (pxQueue->uxMessagesWaiting == (UBaseType_t)0) { - xReturn = pdTRUE; - } else { - xReturn = pdFALSE; + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } } - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xQueueIsQueueEmptyFromISR(const QueueHandle_t xQueue) { - BaseType_t xReturn; - Queue_t *const pxQueue = xQueue; +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) +{ + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; - configASSERT(pxQueue); - if (pxQueue->uxMessagesWaiting == (UBaseType_t)0) { - xReturn = pdTRUE; - } else { - xReturn = pdFALSE; - } + configASSERT( pxQueue ); - return xReturn; + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; } /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ /*-----------------------------------------------------------*/ -static BaseType_t prvIsQueueFull(const Queue_t *pxQueue) { - BaseType_t xReturn; +static BaseType_t prvIsQueueFull( const Queue_t * pxQueue ) +{ + BaseType_t xReturn; - taskENTER_CRITICAL(); - { - if (pxQueue->uxMessagesWaiting == pxQueue->uxLength) { - xReturn = pdTRUE; - } else { - xReturn = pdFALSE; + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } } - } - taskEXIT_CRITICAL(); + taskEXIT_CRITICAL(); - return xReturn; + return xReturn; } /*-----------------------------------------------------------*/ -BaseType_t xQueueIsQueueFullFromISR(const QueueHandle_t xQueue) { - BaseType_t xReturn; - Queue_t *const pxQueue = xQueue; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) +{ + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; - configASSERT(pxQueue); - if (pxQueue->uxMessagesWaiting == pxQueue->uxLength) { - xReturn = pdTRUE; - } else { - xReturn = pdFALSE; - } + configASSERT( pxQueue ); - return xReturn; + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; } /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ /*-----------------------------------------------------------*/ -#if (configUSE_CO_ROUTINES == 1) - -BaseType_t xQueueCRSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait) { - BaseType_t xReturn; - Queue_t *const pxQueue = xQueue; - - /* If the queue is already full we may have to block. A critical section - is required to prevent an interrupt removing something from the queue - between the check to see if the queue is full and blocking on the queue. */ - portDISABLE_INTERRUPTS(); - { - if (prvIsQueueFull(pxQueue) != pdFALSE) { - /* The queue is full - do we want to block or just leave without - posting? */ - if (xTicksToWait > (TickType_t)0) { - /* As this is called from a coroutine we cannot block directly, but - return indicating that we need to block. */ - vCoRoutineAddToDelayedList(xTicksToWait, &(pxQueue->xTasksWaitingToSend)); +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSend( QueueHandle_t xQueue, + const void * pvItemToQueue, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + /* If the queue is already full we may have to block. A critical section + * is required to prevent an interrupt removing something from the queue + * between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + /* The queue is full - do we want to block or just leave without + * posting? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + * return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } portENABLE_INTERRUPTS(); - return errQUEUE_BLOCKED; - } else { + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + * into the ready list as we are within a critical section. + * Instead the same pending ready list mechanism is used as if + * the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + * that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = errQUEUE_FULL; + } + } portENABLE_INTERRUPTS(); - return errQUEUE_FULL; - } - } - } - portENABLE_INTERRUPTS(); - - portDISABLE_INTERRUPTS(); - { - if (pxQueue->uxMessagesWaiting < pxQueue->uxLength) { - /* There is room in the queue, copy the data into the queue. */ - prvCopyDataToQueue(pxQueue, pvItemToQueue, queueSEND_TO_BACK); - xReturn = pdPASS; - - /* Were any co-routines waiting for data to become available? */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - /* In this instance the co-routine could be placed directly - into the ready list as we are within a critical section. - Instead the same pending ready list mechanism is used as if - the event were caused from within an interrupt. */ - if (xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - /* The co-routine waiting has a higher priority so record - that a yield might be appropriate. */ - xReturn = errQUEUE_YIELD; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - xReturn = errQUEUE_FULL; - } - } - portENABLE_INTERRUPTS(); - return xReturn; -} + return xReturn; + } #endif /* configUSE_CO_ROUTINES */ /*-----------------------------------------------------------*/ -#if (configUSE_CO_ROUTINES == 1) - -BaseType_t xQueueCRReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait) { - BaseType_t xReturn; - Queue_t *const pxQueue = xQueue; - - /* If the queue is already empty we may have to block. A critical section - is required to prevent an interrupt adding something to the queue - between the check to see if the queue is empty and blocking on the queue. */ - portDISABLE_INTERRUPTS(); - { - if (pxQueue->uxMessagesWaiting == (UBaseType_t)0) { - /* There are no messages in the queue, do we want to block or just - leave with nothing? */ - if (xTicksToWait > (TickType_t)0) { - /* As this is a co-routine we cannot block directly, but return - indicating that we need to block. */ - vCoRoutineAddToDelayedList(xTicksToWait, &(pxQueue->xTasksWaitingToReceive)); +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceive( QueueHandle_t xQueue, + void * pvBuffer, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + /* If the queue is already empty we may have to block. A critical section + * is required to prevent an interrupt adding something to the queue + * between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + * leave with nothing? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + * indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } portENABLE_INTERRUPTS(); - return errQUEUE_BLOCKED; - } else { + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + * into the ready list as we are within a critical section. + * Instead the same pending ready list mechanism is used as if + * the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = pdFAIL; + } + } portENABLE_INTERRUPTS(); - return errQUEUE_FULL; - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } - portENABLE_INTERRUPTS(); - - portDISABLE_INTERRUPTS(); - { - if (pxQueue->uxMessagesWaiting > (UBaseType_t)0) { - /* Data is available from the queue. */ - pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; - if (pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail) { - pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; - } else { - mtCOVERAGE_TEST_MARKER(); - } - --(pxQueue->uxMessagesWaiting); - (void)memcpy((void *)pvBuffer, (void *)pxQueue->u.xQueue.pcReadFrom, (unsigned)pxQueue->uxItemSize); - - xReturn = pdPASS; - - /* Were any co-routines waiting for space to become available? */ - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - /* In this instance the co-routine could be placed directly - into the ready list as we are within a critical section. - Instead the same pending ready list mechanism is used as if - the event were caused from within an interrupt. */ - if (xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - xReturn = errQUEUE_YIELD; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - xReturn = pdFAIL; - } - } - portENABLE_INTERRUPTS(); - return xReturn; -} + return xReturn; + } #endif /* configUSE_CO_ROUTINES */ /*-----------------------------------------------------------*/ -#if (configUSE_CO_ROUTINES == 1) - -BaseType_t xQueueCRSendFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken) { - Queue_t *const pxQueue = xQueue; - - /* Cannot block within an ISR so if there is no space on the queue then - exit without doing anything. */ - if (pxQueue->uxMessagesWaiting < pxQueue->uxLength) { - prvCopyDataToQueue(pxQueue, pvItemToQueue, queueSEND_TO_BACK); - - /* We only want to wake one co-routine per ISR, so check that a - co-routine has not already been woken. */ - if (xCoRoutinePreviouslyWoken == pdFALSE) { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE) { - if (xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE) { - return pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } +#if ( configUSE_CO_ROUTINES == 1 ) - return xCoRoutinePreviouslyWoken; -} + BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, + const void * pvItemToQueue, + BaseType_t xCoRoutinePreviouslyWoken ) + { + Queue_t * const pxQueue = xQueue; + + /* Cannot block within an ISR so if there is no space on the queue then + * exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + * co-routine has not already been woken. */ + if( xCoRoutinePreviouslyWoken == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCoRoutinePreviouslyWoken; + } #endif /* configUSE_CO_ROUTINES */ /*-----------------------------------------------------------*/ -#if (configUSE_CO_ROUTINES == 1) - -BaseType_t xQueueCRReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxCoRoutineWoken) { - BaseType_t xReturn; - Queue_t *const pxQueue = xQueue; - - /* We cannot block from an ISR, so check there is data available. If - not then just leave without doing anything. */ - if (pxQueue->uxMessagesWaiting > (UBaseType_t)0) { - /* Copy the data from the queue. */ - pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; - if (pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail) { - pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; - } else { - mtCOVERAGE_TEST_MARKER(); - } - --(pxQueue->uxMessagesWaiting); - (void)memcpy((void *)pvBuffer, (void *)pxQueue->u.xQueue.pcReadFrom, (unsigned)pxQueue->uxItemSize); - - if ((*pxCoRoutineWoken) == pdFALSE) { - if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToSend)) == pdFALSE) { - if (xCoRoutineRemoveFromEventList(&(pxQueue->xTasksWaitingToSend)) != pdFALSE) { - *pxCoRoutineWoken = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } +#if ( configUSE_CO_ROUTINES == 1 ) - xReturn = pdPASS; - } else { - xReturn = pdFAIL; - } + BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, + void * pvBuffer, + BaseType_t * pxCoRoutineWoken ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; - return xReturn; -} + /* We cannot block from an ISR, so check there is data available. If + * not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( ( *pxCoRoutineWoken ) == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } #endif /* configUSE_CO_ROUTINES */ /*-----------------------------------------------------------*/ -#if (configQUEUE_REGISTRY_SIZE > 0) +#if ( configQUEUE_REGISTRY_SIZE > 0 ) -void vQueueAddToRegistry(QueueHandle_t xQueue, const char *pcQueueName) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ - UBaseType_t ux; - - /* See if there is an empty space in the registry. A NULL name denotes - a free slot. */ - for (ux = (UBaseType_t)0U; ux < (UBaseType_t)configQUEUE_REGISTRY_SIZE; ux++) { - if (xQueueRegistry[ux].pcQueueName == NULL) { - /* Store the information on this queue. */ - xQueueRegistry[ux].pcQueueName = pcQueueName; - xQueueRegistry[ux].xHandle = xQueue; - - traceQUEUE_REGISTRY_ADD(xQueue, pcQueueName); - break; - } else { - mtCOVERAGE_TEST_MARKER(); + void vQueueAddToRegistry( QueueHandle_t xQueue, + const char * pcQueueName ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t ux; + + /* See if there is an empty space in the registry. A NULL name denotes + * a free slot. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].pcQueueName == NULL ) + { + /* Store the information on this queue. */ + xQueueRegistry[ ux ].pcQueueName = pcQueueName; + xQueueRegistry[ ux ].xHandle = xQueue; + + traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ); + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } } - } -} #endif /* configQUEUE_REGISTRY_SIZE */ /*-----------------------------------------------------------*/ -#if (configQUEUE_REGISTRY_SIZE > 0) +#if ( configQUEUE_REGISTRY_SIZE > 0 ) -const char *pcQueueGetName(QueueHandle_t xQueue) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ - UBaseType_t ux; - const char *pcReturn = NULL; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - - /* Note there is nothing here to protect against another task adding or - removing entries from the registry while it is being searched. */ - for (ux = (UBaseType_t)0U; ux < (UBaseType_t)configQUEUE_REGISTRY_SIZE; ux++) { - if (xQueueRegistry[ux].xHandle == xQueue) { - pcReturn = xQueueRegistry[ux].pcQueueName; - break; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } + const char * pcQueueGetName( QueueHandle_t xQueue ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t ux; + const char * pcReturn = NULL; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + /* Note there is nothing here to protect against another task adding or + * removing entries from the registry while it is being searched. */ - return pcReturn; -} /*lint !e818 xQueue cannot be a pointer to const because it is a typedef. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + pcReturn = xQueueRegistry[ ux ].pcQueueName; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return pcReturn; + } /*lint !e818 xQueue cannot be a pointer to const because it is a typedef. */ #endif /* configQUEUE_REGISTRY_SIZE */ /*-----------------------------------------------------------*/ -#if (configQUEUE_REGISTRY_SIZE > 0) - -void vQueueUnregisterQueue(QueueHandle_t xQueue) { - UBaseType_t ux; - - /* See if the handle of the queue being unregistered in actually in the - registry. */ - for (ux = (UBaseType_t)0U; ux < (UBaseType_t)configQUEUE_REGISTRY_SIZE; ux++) { - if (xQueueRegistry[ux].xHandle == xQueue) { - /* Set the name to NULL to show that this slot if free again. */ - xQueueRegistry[ux].pcQueueName = NULL; - - /* Set the handle to NULL to ensure the same queue handle cannot - appear in the registry twice if it is added, removed, then - added again. */ - xQueueRegistry[ux].xHandle = (QueueHandle_t)0; - break; - } else { - mtCOVERAGE_TEST_MARKER(); - } - } +#if ( configQUEUE_REGISTRY_SIZE > 0 ) -} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ + void vQueueUnregisterQueue( QueueHandle_t xQueue ) + { + UBaseType_t ux; + + /* See if the handle of the queue being unregistered in actually in the + * registry. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + + /* Set the handle to NULL to ensure the same queue handle cannot + * appear in the registry twice if it is added, removed, then + * added again. */ + xQueueRegistry[ ux ].xHandle = ( QueueHandle_t ) 0; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ #endif /* configQUEUE_REGISTRY_SIZE */ /*-----------------------------------------------------------*/ -#if (configUSE_TIMERS == 1) - -void vQueueWaitForMessageRestricted(QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely) { - Queue_t *const pxQueue = xQueue; - - /* This function should not be called by application code hence the - 'Restricted' in its name. It is not part of the public API. It is - designed for use by kernel code, and has special calling requirements. - It can result in vListInsert() being called on a list that can only - possibly ever have one item in it, so the list will be fast, but even - so it should be called with the scheduler locked and not from a critical - section. */ - - /* Only do anything if there are no messages in the queue. This function - will not actually cause the task to block, just place it on a blocked - list. It will not block until the scheduler is unlocked - at which - time a yield will be performed. If an item is added to the queue while - the queue is locked, and the calling task blocks on the queue, then the - calling task will be immediately unblocked when the queue is unlocked. */ - prvLockQueue(pxQueue); - if (pxQueue->uxMessagesWaiting == (UBaseType_t)0U) { - /* There is nothing in the queue, block for the specified period. */ - vTaskPlaceOnEventListRestricted(&(pxQueue->xTasksWaitingToReceive), xTicksToWait, xWaitIndefinitely); - } else { - mtCOVERAGE_TEST_MARKER(); - } - prvUnlockQueue(pxQueue); -} +#if ( configUSE_TIMERS == 1 ) + + void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + { + Queue_t * const pxQueue = xQueue; + + /* This function should not be called by application code hence the + * 'Restricted' in its name. It is not part of the public API. It is + * designed for use by kernel code, and has special calling requirements. + * It can result in vListInsert() being called on a list that can only + * possibly ever have one item in it, so the list will be fast, but even + * so it should be called with the scheduler locked and not from a critical + * section. */ + + /* Only do anything if there are no messages in the queue. This function + * will not actually cause the task to block, just place it on a blocked + * list. It will not block until the scheduler is unlocked - at which + * time a yield will be performed. If an item is added to the queue while + * the queue is locked, and the calling task blocks on the queue, then the + * calling task will be immediately unblocked when the queue is unlocked. */ + prvLockQueue( pxQueue ); + + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) + { + /* There is nothing in the queue, block for the specified period. */ + vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvUnlockQueue( pxQueue ); + } #endif /* configUSE_TIMERS */ /*-----------------------------------------------------------*/ -#if ((configUSE_QUEUE_SETS == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) +#if ( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) -QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength) { - QueueSetHandle_t pxQueue; + QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) + { + QueueSetHandle_t pxQueue; - pxQueue = xQueueGenericCreate(uxEventQueueLength, (UBaseType_t)sizeof(Queue_t *), queueQUEUE_TYPE_SET); + pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); - return pxQueue; -} + return pxQueue; + } #endif /* configUSE_QUEUE_SETS */ /*-----------------------------------------------------------*/ -#if (configUSE_QUEUE_SETS == 1) - -BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { - BaseType_t xReturn; - - taskENTER_CRITICAL(); - { - if (((Queue_t *)xQueueOrSemaphore)->pxQueueSetContainer != NULL) { - /* Cannot add a queue/semaphore to more than one queue set. */ - xReturn = pdFAIL; - } else if (((Queue_t *)xQueueOrSemaphore)->uxMessagesWaiting != (UBaseType_t)0) { - /* Cannot add a queue/semaphore to a queue set if there are already - items in the queue/semaphore. */ - xReturn = pdFAIL; - } else { - ((Queue_t *)xQueueOrSemaphore)->pxQueueSetContainer = xQueueSet; - xReturn = pdPASS; - } - } - taskEXIT_CRITICAL(); +#if ( configUSE_QUEUE_SETS == 1 ) - return xReturn; -} + BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL ) + { + /* Cannot add a queue/semaphore to more than one queue set. */ + xReturn = pdFAIL; + } + else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* Cannot add a queue/semaphore to a queue set if there are already + * items in the queue/semaphore. */ + xReturn = pdFAIL; + } + else + { + ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet; + xReturn = pdPASS; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } #endif /* configUSE_QUEUE_SETS */ /*-----------------------------------------------------------*/ -#if (configUSE_QUEUE_SETS == 1) - -BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { - BaseType_t xReturn; - Queue_t *const pxQueueOrSemaphore = (Queue_t *)xQueueOrSemaphore; - - if (pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet) { - /* The queue was not a member of the set. */ - xReturn = pdFAIL; - } else if (pxQueueOrSemaphore->uxMessagesWaiting != (UBaseType_t)0) { - /* It is dangerous to remove a queue from a set when the queue is - not empty because the queue set will still hold pending events for - the queue. */ - xReturn = pdFAIL; - } else { - taskENTER_CRITICAL(); +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) { - /* The queue is no longer contained in the set. */ - pxQueueOrSemaphore->pxQueueSetContainer = NULL; - } - taskEXIT_CRITICAL(); - xReturn = pdPASS; - } + BaseType_t xReturn; + Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore; - return xReturn; -} /*lint !e818 xQueueSet could not be declared as pointing to const as it is a typedef. */ + if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet ) + { + /* The queue was not a member of the set. */ + xReturn = pdFAIL; + } + else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* It is dangerous to remove a queue from a set when the queue is + * not empty because the queue set will still hold pending events for + * the queue. */ + xReturn = pdFAIL; + } + else + { + taskENTER_CRITICAL(); + { + /* The queue is no longer contained in the set. */ + pxQueueOrSemaphore->pxQueueSetContainer = NULL; + } + taskEXIT_CRITICAL(); + xReturn = pdPASS; + } + + return xReturn; + } /*lint !e818 xQueueSet could not be declared as pointing to const as it is a typedef. */ #endif /* configUSE_QUEUE_SETS */ /*-----------------------------------------------------------*/ -#if (configUSE_QUEUE_SETS == 1) +#if ( configUSE_QUEUE_SETS == 1 ) -QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait) { - QueueSetMemberHandle_t xReturn = NULL; + QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, + TickType_t const xTicksToWait ) + { + QueueSetMemberHandle_t xReturn = NULL; - (void)xQueueReceive((QueueHandle_t)xQueueSet, &xReturn, xTicksToWait); /*lint !e961 Casting from one typedef to another is not redundant. */ - return xReturn; -} + ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } #endif /* configUSE_QUEUE_SETS */ /*-----------------------------------------------------------*/ -#if (configUSE_QUEUE_SETS == 1) +#if ( configUSE_QUEUE_SETS == 1 ) -QueueSetMemberHandle_t xQueueSelectFromSetFromISR(QueueSetHandle_t xQueueSet) { - QueueSetMemberHandle_t xReturn = NULL; + QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) + { + QueueSetMemberHandle_t xReturn = NULL; - (void)xQueueReceiveFromISR((QueueHandle_t)xQueueSet, &xReturn, NULL); /*lint !e961 Casting from one typedef to another is not redundant. */ - return xReturn; -} + ( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } #endif /* configUSE_QUEUE_SETS */ /*-----------------------------------------------------------*/ -#if (configUSE_QUEUE_SETS == 1) +#if ( configUSE_QUEUE_SETS == 1 ) -static BaseType_t prvNotifyQueueSetContainer(const Queue_t *const pxQueue) { - Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer; - BaseType_t xReturn = pdFALSE; + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) + { + Queue_t * pxQueueSetContainer = pxQueue->pxQueueSetContainer; + BaseType_t xReturn = pdFALSE; - /* This function must be called form a critical section. */ + /* This function must be called form a critical section. */ - configASSERT(pxQueueSetContainer); - configASSERT(pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength); + configASSERT( pxQueueSetContainer ); + configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ); - if (pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength) { - const int8_t cTxLock = pxQueueSetContainer->cTxLock; + if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ) + { + const int8_t cTxLock = pxQueueSetContainer->cTxLock; - traceQUEUE_SEND(pxQueueSetContainer); + traceQUEUE_SET_SEND( pxQueueSetContainer ); - /* The data copied is the handle of the queue that contains data. */ - xReturn = prvCopyDataToQueue(pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK); + /* The data copied is the handle of the queue that contains data. */ + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK ); - if (cTxLock == queueUNLOCKED) { - if (listLIST_IS_EMPTY(&(pxQueueSetContainer->xTasksWaitingToReceive)) == pdFALSE) { - if (xTaskRemoveFromEventList(&(pxQueueSetContainer->xTasksWaitingToReceive)) != pdFALSE) { - /* The task waiting has a higher priority. */ - xReturn = pdTRUE; - } else { - mtCOVERAGE_TEST_MARKER(); + if( cTxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + configASSERT( cTxLock != queueINT8_MAX ); + + pxQueueSetContainer->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); } - } else { - mtCOVERAGE_TEST_MARKER(); - } - } else { - pxQueueSetContainer->cTxLock = (int8_t)(cTxLock + 1); - } - } else { - mtCOVERAGE_TEST_MARKER(); - } - return xReturn; -} + return xReturn; + } #endif /* configUSE_QUEUE_SETS */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c b/source/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c new file mode 100644 index 0000000000..8482e68fe5 --- /dev/null +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/stream_buffer.c @@ -0,0 +1,1326 @@ +/* + * FreeRTOS Kernel V10.4.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* Standard includes. */ +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "stream_buffer.h" + +#if ( configUSE_TASK_NOTIFICATIONS != 1 ) + #error configUSE_TASK_NOTIFICATIONS must be set to 1 to build stream_buffer.c +#endif + +/* Lint e961, e9021 and e750 are suppressed as a MISRA exception justified + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + +/* If the user has not provided application specific Rx notification macros, + * or #defined the notification macros away, them provide default implementations + * that uses task notifications. */ +/*lint -save -e9026 Function like macros allowed and needed here so they can be overridden. */ +#ifndef sbRECEIVE_COMPLETED + #define sbRECEIVE_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbRECEIVE_COMPLETED */ + +#ifndef sbRECEIVE_COMPLETED_FROM_ISR + #define sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, \ + pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbRECEIVE_COMPLETED_FROM_ISR */ + +/* If the user has not provided an application specific Tx notification macro, + * or #defined the notification macro away, them provide a default implementation + * that uses task notifications. */ +#ifndef sbSEND_COMPLETED + #define sbSEND_COMPLETED( pxStreamBuffer ) \ + vTaskSuspendAll(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotify( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + ( void ) xTaskResumeAll(); +#endif /* sbSEND_COMPLETED */ + +#ifndef sbSEND_COMPLETE_FROM_ISR + #define sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + \ + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \ + { \ + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, \ + ( uint32_t ) 0, \ + eNoAction, \ + pxHigherPriorityTaskWoken ); \ + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \ + } \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } +#endif /* sbSEND_COMPLETE_FROM_ISR */ +/*lint -restore (9026) */ + +/* The number of bytes used to hold the length of a message in the buffer. */ +#define sbBYTES_TO_STORE_MESSAGE_LENGTH ( sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) ) + +/* Bits stored in the ucFlags field of the stream buffer. */ +#define sbFLAGS_IS_MESSAGE_BUFFER ( ( uint8_t ) 1 ) /* Set if the stream buffer was created as a message buffer, in which case it holds discrete messages rather than a stream. */ +#define sbFLAGS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 2 ) /* Set if the stream buffer was created using statically allocated memory. */ + +/*-----------------------------------------------------------*/ + +/* Structure that hold state information on the buffer. */ +typedef struct StreamBufferDef_t /*lint !e9058 Style convention uses tag. */ +{ + volatile size_t xTail; /* Index to the next item to read within the buffer. */ + volatile size_t xHead; /* Index to the next item to write within the buffer. */ + size_t xLength; /* The length of the buffer pointed to by pucBuffer. */ + size_t xTriggerLevelBytes; /* The number of bytes that must be in the stream buffer before a task that is waiting for data is unblocked. */ + volatile TaskHandle_t xTaskWaitingToReceive; /* Holds the handle of a task waiting for data, or NULL if no tasks are waiting. */ + volatile TaskHandle_t xTaskWaitingToSend; /* Holds the handle of a task waiting to send data to a message buffer that is full. */ + uint8_t * pucBuffer; /* Points to the buffer itself - that is - the RAM that stores the data passed through the buffer. */ + uint8_t ucFlags; + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; /* Used for tracing purposes. */ + #endif +} StreamBuffer_t; + +/* + * The number of bytes available to be read from the buffer. + */ +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION; + +/* + * Add xCount bytes from pucData into the pxStreamBuffer message buffer. + * Returns the number of bytes written, which will either equal xCount in the + * success case, or 0 if there was not enough space in the buffer (in which case + * no data is written into the buffer). + */ +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, + const uint8_t * pucData, + size_t xCount ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then reads an entire + * message out of the buffer. If the stream buffer is being used as a stream + * buffer then read as many bytes as possible from the buffer. + * prvReadBytesFromBuffer() is called to actually extract the bytes from the + * buffer's data storage area. + */ +static size_t prvReadMessageFromBuffer( StreamBuffer_t * pxStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) PRIVILEGED_FUNCTION; + +/* + * If the stream buffer is being used as a message buffer, then writes an entire + * message to the buffer. If the stream buffer is being used as a stream + * buffer then write as many bytes as possible to the buffer. + * prvWriteBytestoBuffer() is called to actually send the bytes to the buffer's + * data storage area. + */ +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) PRIVILEGED_FUNCTION; + +/* + * Read xMaxCount bytes from the pxStreamBuffer message buffer and write them + * to pucData. + */ +static size_t prvReadBytesFromBuffer( StreamBuffer_t * pxStreamBuffer, + uint8_t * pucData, + size_t xMaxCount, + size_t xBytesAvailable ) PRIVILEGED_FUNCTION; + +/* + * Called by both pxStreamBufferCreate() and pxStreamBufferCreateStatic() to + * initialise the members of the newly created stream buffer structure. + */ +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer ) + { + uint8_t * pucAllocatedMemory; + uint8_t ucFlags; + + /* In case the stream buffer is going to be used as a message buffer + * (that is, it will hold discrete messages with a little meta data that + * says how big the next message is) check the buffer will be large enough + * to hold at least one message. */ + if( xIsMessageBuffer == pdTRUE ) + { + /* Is a message buffer but not statically allocated. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER; + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* Not a message buffer and not statically allocated. */ + ucFlags = 0; + configASSERT( xBufferSizeBytes > 0 ); + } + + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + * the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + /* A stream buffer requires a StreamBuffer_t structure and a buffer. + * Both are allocated in a single call to pvPortMalloc(). The + * StreamBuffer_t structure is placed at the start of the allocated memory + * and the buffer follows immediately after. The requested size is + * incremented so the free space is returned as the user would expect - + * this is a quirk of the implementation that means otherwise the free + * space would be reported as one byte smaller than would be logically + * expected. */ + xBufferSizeBytes++; + pucAllocatedMemory = ( uint8_t * ) pvPortMalloc( xBufferSizeBytes + sizeof( StreamBuffer_t ) ); /*lint !e9079 malloc() only returns void*. */ + + if( pucAllocatedMemory != NULL ) + { + prvInitialiseNewStreamBuffer( ( StreamBuffer_t * ) pucAllocatedMemory, /* Structure at the start of the allocated memory. */ /*lint !e9087 Safe cast as allocated memory is aligned. */ /*lint !e826 Area is not too small and alignment is guaranteed provided malloc() behaves as expected and returns aligned buffer. */ + pucAllocatedMemory + sizeof( StreamBuffer_t ), /* Storage area follows. */ /*lint !e9016 Indexing past structure valid for uint8_t pointer, also storage area has no alignment requirement. */ + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pucAllocatedMemory ), xIsMessageBuffer ); + } + else + { + traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ); + } + + return ( StreamBufferHandle_t ) pucAllocatedMemory; /*lint !e9087 !e826 Safe cast as allocated memory is aligned. */ + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + StreamBufferHandle_t xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, + uint8_t * const pucStreamBufferStorageArea, + StaticStreamBuffer_t * const pxStaticStreamBuffer ) + { + StreamBuffer_t * const pxStreamBuffer = ( StreamBuffer_t * ) pxStaticStreamBuffer; /*lint !e740 !e9087 Safe cast as StaticStreamBuffer_t is opaque Streambuffer_t. */ + StreamBufferHandle_t xReturn; + uint8_t ucFlags; + + configASSERT( pucStreamBufferStorageArea ); + configASSERT( pxStaticStreamBuffer ); + configASSERT( xTriggerLevelBytes <= xBufferSizeBytes ); + + /* A trigger level of 0 would cause a waiting task to unblock even when + * the buffer was empty. */ + if( xTriggerLevelBytes == ( size_t ) 0 ) + { + xTriggerLevelBytes = ( size_t ) 1; + } + + if( xIsMessageBuffer != pdFALSE ) + { + /* Statically allocated message buffer. */ + ucFlags = sbFLAGS_IS_MESSAGE_BUFFER | sbFLAGS_IS_STATICALLY_ALLOCATED; + } + else + { + /* Statically allocated stream buffer. */ + ucFlags = sbFLAGS_IS_STATICALLY_ALLOCATED; + } + + /* In case the stream buffer is going to be used as a message buffer + * (that is, it will hold discrete messages with a little meta data that + * says how big the next message is) check the buffer will be large enough + * to hold at least one message. */ + configASSERT( xBufferSizeBytes > sbBYTES_TO_STORE_MESSAGE_LENGTH ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticStreamBuffer_t equals the size of the real + * message buffer structure. */ + volatile size_t xSize = sizeof( StaticStreamBuffer_t ); + configASSERT( xSize == sizeof( StreamBuffer_t ) ); + } /*lint !e529 xSize is referenced is configASSERT() is defined. */ + #endif /* configASSERT_DEFINED */ + + if( ( pucStreamBufferStorageArea != NULL ) && ( pxStaticStreamBuffer != NULL ) ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pucStreamBufferStorageArea, + xBufferSizeBytes, + xTriggerLevelBytes, + ucFlags ); + + /* Remember this was statically allocated in case it is ever deleted + * again. */ + pxStreamBuffer->ucFlags |= sbFLAGS_IS_STATICALLY_ALLOCATED; + + traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ); + + xReturn = ( StreamBufferHandle_t ) pxStaticStreamBuffer; /*lint !e9087 Data hiding requires cast to opaque type. */ + } + else + { + xReturn = NULL; + traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ); + } + + return xReturn; + } + +#endif /* ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) +{ + StreamBuffer_t * pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + traceSTREAM_BUFFER_DELETE( xStreamBuffer ); + + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) pdFALSE ) + { + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both the structure and the buffer were allocated using a single call + * to pvPortMalloc(), hence only one call to vPortFree() is required. */ + vPortFree( ( void * ) pxStreamBuffer ); /*lint !e9087 Standard free() semantics require void *, plus pxStreamBuffer was allocated by pvPortMalloc(). */ + } + #else + { + /* Should not be possible to get here, ucFlags must be corrupt. + * Force an assert. */ + configASSERT( xStreamBuffer == ( StreamBufferHandle_t ) ~0 ); + } + #endif + } + else + { + /* The structure and buffer were not allocated dynamically and cannot be + * freed - just scrub the structure so future use will assert. */ + ( void ) memset( pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + BaseType_t xReturn = pdFAIL; + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxStreamBufferNumber; + #endif + + configASSERT( pxStreamBuffer ); + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Store the stream buffer number so it can be restored after the + * reset. */ + uxStreamBufferNumber = pxStreamBuffer->uxStreamBufferNumber; + } + #endif + + /* Can only reset a message buffer if there are no tasks blocked on it. */ + taskENTER_CRITICAL(); + { + if( pxStreamBuffer->xTaskWaitingToReceive == NULL ) + { + if( pxStreamBuffer->xTaskWaitingToSend == NULL ) + { + prvInitialiseNewStreamBuffer( pxStreamBuffer, + pxStreamBuffer->pucBuffer, + pxStreamBuffer->xLength, + pxStreamBuffer->xTriggerLevelBytes, + pxStreamBuffer->ucFlags ); + xReturn = pdPASS; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + #endif + + traceSTREAM_BUFFER_RESET( xStreamBuffer ); + } + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, + size_t xTriggerLevel ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + BaseType_t xReturn; + + configASSERT( pxStreamBuffer ); + + /* It is not valid for the trigger level to be 0. */ + if( xTriggerLevel == ( size_t ) 0 ) + { + xTriggerLevel = ( size_t ) 1; + } + + /* The trigger level is the number of bytes that must be in the stream + * buffer before a task that is waiting for data is unblocked. */ + if( xTriggerLevel <= pxStreamBuffer->xLength ) + { + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevel; + xReturn = pdPASS; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ + const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xSpace; + + configASSERT( pxStreamBuffer ); + + xSpace = pxStreamBuffer->xLength + pxStreamBuffer->xTail; + xSpace -= pxStreamBuffer->xHead; + xSpace -= ( size_t ) 1; + + if( xSpace >= pxStreamBuffer->xLength ) + { + xSpace -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xSpace; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) +{ + const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReturn; + + configASSERT( pxStreamBuffer ); + + xReturn = prvBytesInBuffer( pxStreamBuffer ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReturn, xSpace = 0; + size_t xRequiredSpace = xDataLengthBytes; + TimeOut_t xTimeOut; + + /* Having a 'isFeasible' variable allows to respect the convention that there is only a return statement at the end. Othewise, return + * could be done as soon as we realise the send cannot happen. We will let the call to 'prvWriteMessageToBuffer' dealing with this scenario. */ + BaseType_t xIsFeasible; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + * buffers. If this is a message buffer then the space needed must be + * increased by the amount of bytes needed to store the length of the + * message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + + /* Overflow? */ + configASSERT( xRequiredSpace > xDataLengthBytes ); + + /* In the case of the message buffer, one has to be able to write the complete message as opposed to + * a stream buffer for semantic reasons. Check if it is physically possible to write the message given + * the length of the buffer. */ + if( xRequiredSpace > pxStreamBuffer->xLength ) + { + /* The message could never be written because it is greater than the buffer length. + * By setting xIsFeasable to FALSE, we skip over the following do..while loop, thus avoiding + * a deadlock. The call to 'prvWriteMessageToBuffer' toward the end of this function with + * xRequiredSpace greater than xSpace will suffice in not writing anything to the internal buffer. + * Now, the function will return 0 because the message could not be written. Should an error code be + * returned instead ??? In my opinion, probably.. But the return type doesn't allow for negative + * values to be returned. A confusion could exist to the caller. Returning 0 because a timeout occurred + * and a subsequent send attempts could eventually succeed, and returning 0 because a write could never + * happen because of the size are two scenarios to me :/ */ + xIsFeasible = pdFALSE; + } + else + { + /* It is possible to write the message completely in the buffer. This is the intended route. + * Let's continue with the regular timeout logic. */ + xIsFeasible = pdTRUE; + } + } + else + { + /* In the case of the stream buffer, not being able to completely write the message in the buffer + * is an acceptable scenario, but it has to be dealt with properly */ + if( xRequiredSpace > pxStreamBuffer->xLength ) + { + /* Not enough buffer space. We will attempt to write as much as we can in this run + * so that the caller can send the remaining in subsequent calls. We avoid a deadlock by + * offering the possibility to take the 'else' branch in the 'if( xSpace < xRequiredSpace )' + * condition inside the following do..while loop */ + xRequiredSpace = pxStreamBuffer->xLength; + + /* TODO FIXME: Is there a check we should do with the xTriggerLevelBytes value ? */ + + /* With the adjustment to 'xRequiredSpace', the deadlock is avoided, thus it's now feasible. */ + xIsFeasible = pdTRUE; + } + else + { + /* It is possible to write the message completely in the buffer. */ + xIsFeasible = pdTRUE; + } + } + + /* Added check against xIsFeasible. If it's not feasible, don't even wait for notification, let the call to 'prvWriteMessageToBuffer' do nothing and return 0 */ + if( ( xTicksToWait != ( TickType_t ) 0 ) && ( xIsFeasible == pdTRUE ) ) + { + vTaskSetTimeOutState( &xTimeOut ); + + do + { + /* Wait until the required number of bytes are free in the message + * buffer. */ + taskENTER_CRITICAL(); + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + + if( xSpace < xRequiredSpace ) + { + /* Clear notification state as going to wait for space. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one writer. */ + configASSERT( pxStreamBuffer->xTaskWaitingToSend == NULL ); + pxStreamBuffer->xTaskWaitingToSend = xTaskGetCurrentTaskHandle(); + } + else + { + taskEXIT_CRITICAL(); + break; + } + } + taskEXIT_CRITICAL(); + + traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToSend = NULL; + } while( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xSpace == ( size_t ) 0 ) + { + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + traceSTREAM_BUFFER_SEND( xStreamBuffer, xReturn ); + + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReturn, xSpace; + size_t xRequiredSpace = xDataLengthBytes; + + configASSERT( pvTxData ); + configASSERT( pxStreamBuffer ); + + /* This send function is used to write to both message buffers and stream + * buffers. If this is a message buffer then the space needed must be + * increased by the amount of bytes needed to store the length of the + * message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xRequiredSpace += sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer ); + xReturn = prvWriteMessageToBuffer( pxStreamBuffer, pvTxData, xDataLengthBytes, xSpace, xRequiredSpace ); + + if( xReturn > ( size_t ) 0 ) + { + /* Was a task waiting for the data? */ + if( prvBytesInBuffer( pxStreamBuffer ) >= pxStreamBuffer->xTriggerLevelBytes ) + { + sbSEND_COMPLETE_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteMessageToBuffer( StreamBuffer_t * const pxStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + size_t xSpace, + size_t xRequiredSpace ) +{ + BaseType_t xShouldWrite; + size_t xReturn; + + if( xSpace == ( size_t ) 0 ) + { + /* Doesn't matter if this is a stream buffer or a message buffer, there + * is no space to write. */ + xShouldWrite = pdFALSE; + } + else if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) == ( uint8_t ) 0 ) + { + /* This is a stream buffer, as opposed to a message buffer, so writing a + * stream of bytes rather than discrete messages. Write as many bytes as + * possible. */ + xShouldWrite = pdTRUE; + xDataLengthBytes = configMIN( xDataLengthBytes, xSpace ); + } + else if( xSpace >= xRequiredSpace ) + { + /* This is a message buffer, as opposed to a stream buffer, and there + * is enough space to write both the message length and the message itself + * into the buffer. Start by writing the length of the data, the data + * itself will be written later in this function. */ + xShouldWrite = pdTRUE; + ( void ) prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) &( xDataLengthBytes ), sbBYTES_TO_STORE_MESSAGE_LENGTH ); + } + else + { + /* There is space available, but not enough space. */ + xShouldWrite = pdFALSE; + } + + if( xShouldWrite != pdFALSE ) + { + /* Writes the data itself. */ + xReturn = prvWriteBytesToBuffer( pxStreamBuffer, ( const uint8_t * ) pvTxData, xDataLengthBytes ); /*lint !e9079 Storage buffer is implemented as uint8_t for ease of sizing, alignment and access. */ + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + * discrete messages, and stream buffers, which store a continuous stream of + * bytes. Discrete messages include an additional + * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + * message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + if( xTicksToWait != ( TickType_t ) 0 ) + { + /* Checking if there is data and clearing the notification state must be + * performed atomically. */ + taskENTER_CRITICAL(); + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* If this function was invoked by a message buffer read then + * xBytesToStoreMessageLength holds the number of bytes used to hold + * the length of the next discrete message. If this function was + * invoked by a stream buffer read then xBytesToStoreMessageLength will + * be 0. */ + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Clear notification state as going to wait for data. */ + ( void ) xTaskNotifyStateClear( NULL ); + + /* Should only be one reader. */ + configASSERT( pxStreamBuffer->xTaskWaitingToReceive == NULL ); + pxStreamBuffer->xTaskWaitingToReceive = xTaskGetCurrentTaskHandle(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + if( xBytesAvailable <= xBytesToStoreMessageLength ) + { + /* Wait for data to be available. */ + traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ); + ( void ) xTaskNotifyWait( ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait ); + pxStreamBuffer->xTaskWaitingToReceive = NULL; + + /* Recheck the data available after blocking. */ + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + } + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + * holds the number of bytes used to store the message length) or a stream of + * bytes (where xBytesToStoreMessageLength is zero), the number of bytes + * available must be greater than xBytesToStoreMessageLength to be able to + * read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ); + sbRECEIVE_COMPLETED( pxStreamBuffer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ); + mtCOVERAGE_TEST_MARKER(); + } + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReturn, xBytesAvailable, xOriginalTail; + configMESSAGE_BUFFER_LENGTH_TYPE xTempReturn; + + configASSERT( pxStreamBuffer ); + + /* Ensure the stream buffer is being used as a message buffer. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + if( xBytesAvailable > sbBYTES_TO_STORE_MESSAGE_LENGTH ) + { + /* The number of bytes available is greater than the number of bytes + * required to hold the length of the next message, so another message + * is available. Return its length without removing the length bytes + * from the buffer. A copy of the tail is stored so the buffer can be + * returned to its prior state as the message is not actually being + * removed from the buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempReturn, sbBYTES_TO_STORE_MESSAGE_LENGTH, xBytesAvailable ); + xReturn = ( size_t ) xTempReturn; + pxStreamBuffer->xTail = xOriginalTail; + } + else + { + /* The minimum amount of bytes in a message buffer is + * ( sbBYTES_TO_STORE_MESSAGE_LENGTH + 1 ), so if xBytesAvailable is + * less than sbBYTES_TO_STORE_MESSAGE_LENGTH the only other valid + * value is 0. */ + configASSERT( xBytesAvailable == 0 ); + xReturn = 0; + } + } + else + { + xReturn = 0; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + size_t xReceivedLength = 0, xBytesAvailable, xBytesToStoreMessageLength; + + configASSERT( pvRxData ); + configASSERT( pxStreamBuffer ); + + /* This receive function is used by both message buffers, which store + * discrete messages, and stream buffers, which store a continuous stream of + * bytes. Discrete messages include an additional + * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the + * message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + xBytesAvailable = prvBytesInBuffer( pxStreamBuffer ); + + /* Whether receiving a discrete message (where xBytesToStoreMessageLength + * holds the number of bytes used to store the message length) or a stream of + * bytes (where xBytesToStoreMessageLength is zero), the number of bytes + * available must be greater than xBytesToStoreMessageLength to be able to + * read bytes from the buffer. */ + if( xBytesAvailable > xBytesToStoreMessageLength ) + { + xReceivedLength = prvReadMessageFromBuffer( pxStreamBuffer, pvRxData, xBufferLengthBytes, xBytesAvailable, xBytesToStoreMessageLength ); + + /* Was a task waiting for space in the buffer? */ + if( xReceivedLength != ( size_t ) 0 ) + { + sbRECEIVE_COMPLETED_FROM_ISR( pxStreamBuffer, pxHigherPriorityTaskWoken ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ); + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadMessageFromBuffer( StreamBuffer_t * pxStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + size_t xBytesAvailable, + size_t xBytesToStoreMessageLength ) +{ + size_t xOriginalTail, xReceivedLength, xNextMessageLength; + configMESSAGE_BUFFER_LENGTH_TYPE xTempNextMessageLength; + + if( xBytesToStoreMessageLength != ( size_t ) 0 ) + { + /* A discrete message is being received. First receive the length + * of the message. A copy of the tail is stored so the buffer can be + * returned to its prior state if the length of the message is too + * large for the provided buffer. */ + xOriginalTail = pxStreamBuffer->xTail; + ( void ) prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) &xTempNextMessageLength, xBytesToStoreMessageLength, xBytesAvailable ); + xNextMessageLength = ( size_t ) xTempNextMessageLength; + + /* Reduce the number of bytes available by the number of bytes just + * read out. */ + xBytesAvailable -= xBytesToStoreMessageLength; + + /* Check there is enough space in the buffer provided by the + * user. */ + if( xNextMessageLength > xBufferLengthBytes ) + { + /* The user has provided insufficient space to read the message + * so return the buffer to its previous state (so the length of + * the message is in the buffer again). */ + pxStreamBuffer->xTail = xOriginalTail; + xNextMessageLength = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* A stream of bytes is being received (as opposed to a discrete + * message), so read as many bytes as possible. */ + xNextMessageLength = xBufferLengthBytes; + } + + /* Read the actual data. */ + xReceivedLength = prvReadBytesFromBuffer( pxStreamBuffer, ( uint8_t * ) pvRxData, xNextMessageLength, xBytesAvailable ); /*lint !e9079 Data storage area is implemented as uint8_t array for ease of sizing, indexing and alignment. */ + + return xReceivedLength; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) +{ + const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + BaseType_t xReturn; + size_t xTail; + + configASSERT( pxStreamBuffer ); + + /* True if no bytes are available. */ + xTail = pxStreamBuffer->xTail; + + if( pxStreamBuffer->xHead == xTail ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) +{ + BaseType_t xReturn; + size_t xBytesToStoreMessageLength; + const StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + + configASSERT( pxStreamBuffer ); + + /* This generic version of the receive function is used by both message + * buffers, which store discrete messages, and stream buffers, which store a + * continuous stream of bytes. Discrete messages include an additional + * sbBYTES_TO_STORE_MESSAGE_LENGTH bytes that hold the length of the message. */ + if( ( pxStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ) != ( uint8_t ) 0 ) + { + xBytesToStoreMessageLength = sbBYTES_TO_STORE_MESSAGE_LENGTH; + } + else + { + xBytesToStoreMessageLength = 0; + } + + /* True if the available space equals zero. */ + if( xStreamBufferSpacesAvailable( xStreamBuffer ) <= xBytesToStoreMessageLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToReceive, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToReceive = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) +{ + StreamBuffer_t * const pxStreamBuffer = xStreamBuffer; + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( pxStreamBuffer ); + + uxSavedInterruptStatus = ( UBaseType_t ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) + { + ( void ) xTaskNotifyFromISR( ( pxStreamBuffer )->xTaskWaitingToSend, + ( uint32_t ) 0, + eNoAction, + pxHigherPriorityTaskWoken ); + ( pxStreamBuffer )->xTaskWaitingToSend = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static size_t prvWriteBytesToBuffer( StreamBuffer_t * const pxStreamBuffer, + const uint8_t * pucData, + size_t xCount ) +{ + size_t xNextHead, xFirstLength; + + configASSERT( xCount > ( size_t ) 0 ); + + xNextHead = pxStreamBuffer->xHead; + + /* Calculate the number of bytes that can be added in the first write - + * which may be less than the total number of bytes that need to be added if + * the buffer will wrap back to the beginning. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextHead, xCount ); + + /* Write as many bytes as can be written in the first write. */ + configASSERT( ( xNextHead + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) ( &( pxStreamBuffer->pucBuffer[ xNextHead ] ) ), ( const void * ) pucData, xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the number of bytes written was less than the number that could be + * written in the first write... */ + if( xCount > xFirstLength ) + { + /* ...then write the remaining bytes to the start of the buffer. */ + configASSERT( ( xCount - xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pxStreamBuffer->pucBuffer, ( const void * ) &( pucData[ xFirstLength ] ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xNextHead += xCount; + + if( xNextHead >= pxStreamBuffer->xLength ) + { + xNextHead -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxStreamBuffer->xHead = xNextHead; + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvReadBytesFromBuffer( StreamBuffer_t * pxStreamBuffer, + uint8_t * pucData, + size_t xMaxCount, + size_t xBytesAvailable ) +{ + size_t xCount, xFirstLength, xNextTail; + + /* Use the minimum of the wanted bytes and the available bytes. */ + xCount = configMIN( xBytesAvailable, xMaxCount ); + + if( xCount > ( size_t ) 0 ) + { + xNextTail = pxStreamBuffer->xTail; + + /* Calculate the number of bytes that can be read - which may be + * less than the number wanted if the data wraps around to the start of + * the buffer. */ + xFirstLength = configMIN( pxStreamBuffer->xLength - xNextTail, xCount ); + + /* Obtain the number of bytes it is possible to obtain in the first + * read. Asserts check bounds of read and write. */ + configASSERT( xFirstLength <= xMaxCount ); + configASSERT( ( xNextTail + xFirstLength ) <= pxStreamBuffer->xLength ); + ( void ) memcpy( ( void * ) pucData, ( const void * ) &( pxStreamBuffer->pucBuffer[ xNextTail ] ), xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + + /* If the total number of wanted bytes is greater than the number + * that could be read in the first read... */ + if( xCount > xFirstLength ) + { + /*...then read the remaining bytes from the start of the buffer. */ + configASSERT( xCount <= xMaxCount ); + ( void ) memcpy( ( void * ) &( pucData[ xFirstLength ] ), ( void * ) ( pxStreamBuffer->pucBuffer ), xCount - xFirstLength ); /*lint !e9087 memcpy() requires void *. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Move the tail pointer to effectively remove the data read from + * the buffer. */ + xNextTail += xCount; + + if( xNextTail >= pxStreamBuffer->xLength ) + { + xNextTail -= pxStreamBuffer->xLength; + } + + pxStreamBuffer->xTail = xNextTail; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static size_t prvBytesInBuffer( const StreamBuffer_t * const pxStreamBuffer ) +{ +/* Returns the distance between xTail and xHead. */ + size_t xCount; + + xCount = pxStreamBuffer->xLength + pxStreamBuffer->xHead; + xCount -= pxStreamBuffer->xTail; + + if( xCount >= pxStreamBuffer->xLength ) + { + xCount -= pxStreamBuffer->xLength; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCount; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer, + uint8_t * const pucBuffer, + size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + uint8_t ucFlags ) +{ + /* Assert here is deliberately writing to the entire buffer to ensure it can + * be written to without generating exceptions, and is setting the buffer to a + * known value to assist in development/debugging. */ + #if ( configASSERT_DEFINED == 1 ) + { + /* The value written just has to be identifiable when looking at the + * memory. Don't use 0xA5 as that is the stack fill value and could + * result in confusion as to what is actually being observed. */ + const BaseType_t xWriteValue = 0x55; + configASSERT( memset( pucBuffer, ( int ) xWriteValue, xBufferSizeBytes ) == pucBuffer ); + } /*lint !e529 !e438 xWriteValue is only used if configASSERT() is defined. */ + #endif + + ( void ) memset( ( void * ) pxStreamBuffer, 0x00, sizeof( StreamBuffer_t ) ); /*lint !e9087 memset() requires void *. */ + pxStreamBuffer->pucBuffer = pucBuffer; + pxStreamBuffer->xLength = xBufferSizeBytes; + pxStreamBuffer->xTriggerLevelBytes = xTriggerLevelBytes; + pxStreamBuffer->ucFlags = ucFlags; +} + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) + { + return xStreamBuffer->uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, + UBaseType_t uxStreamBufferNumber ) + { + xStreamBuffer->uxStreamBufferNumber = uxStreamBufferNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) + { + return( xStreamBuffer->ucFlags & sbFLAGS_IS_MESSAGE_BUFFER ); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/tasks.c b/source/Middlewares/Third_Party/FreeRTOS/Source/tasks.c index 6d2484b139..90811f6be2 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/tasks.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/tasks.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,10 +19,9 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /* Standard includes. */ @@ -30,8 +29,8 @@ #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining -all the API functions to use the MPU wrappers. That should only be done when -task.h is included from an application file. */ + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE /* FreeRTOS includes. */ @@ -41,173 +40,175 @@ task.h is included from an application file. */ #include "stack_macros.h" /* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified -because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined -for the header files above, but not in this file, in order to generate the -correct privileged Vs unprivileged linkage and placement. */ + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ /* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting -functions but without including stdio.h here. */ + * functions but without including stdio.h here. */ #if ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) - /* At the bottom of this file are two optional functions that can be used - to generate human readable text from the raw data generated by the - uxTaskGetSystemState() function. Note the formatting functions are provided - for convenience only, and are NOT considered part of the kernel. */ - #include + +/* At the bottom of this file are two optional functions that can be used + * to generate human readable text from the raw data generated by the + * uxTaskGetSystemState() function. Note the formatting functions are provided + * for convenience only, and are NOT considered part of the kernel. */ + #include #endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */ -#if( configUSE_PREEMPTION == 0 ) - /* If the cooperative scheduler is being used then a yield should not be - performed just because a higher priority task has been woken. */ - #define taskYIELD_IF_USING_PREEMPTION() +#if ( configUSE_PREEMPTION == 0 ) + +/* If the cooperative scheduler is being used then a yield should not be + * performed just because a higher priority task has been woken. */ + #define taskYIELD_IF_USING_PREEMPTION() #else - #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() + #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() #endif /* Values that can be assigned to the ucNotifyState member of the TCB. */ -#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) -#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) -#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) +#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* Must be zero as it is the initialised value. */ +#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) +#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) /* * The value used to fill the stack of a task when the task is created. This * is used purely for checking the high water mark for tasks. */ -#define tskSTACK_FILL_BYTE ( 0xa5U ) +#define tskSTACK_FILL_BYTE ( 0xa5U ) /* Bits used to recored how a task's stack and TCB were allocated. */ -#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) -#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) -#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) /* If any of the following are set then task stacks are filled with a known -value so the high water mark can be determined. If none of the following are -set then don't fill the stack so there is no unnecessary dependency on memset. */ -#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 + * value so the high water mark can be determined. If none of the following are + * set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 #else - #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 #endif /* * Macros used by vListTask to indicate which state a task is in. */ -#define tskRUNNING_CHAR ( 'X' ) -#define tskBLOCKED_CHAR ( 'B' ) -#define tskREADY_CHAR ( 'R' ) -#define tskDELETED_CHAR ( 'D' ) -#define tskSUSPENDED_CHAR ( 'S' ) +#define tskRUNNING_CHAR ( 'X' ) +#define tskBLOCKED_CHAR ( 'B' ) +#define tskREADY_CHAR ( 'R' ) +#define tskDELETED_CHAR ( 'D' ) +#define tskSUSPENDED_CHAR ( 'S' ) /* * Some kernel aware debuggers require the data the debugger needs access to be * global, rather than file scope. */ #ifdef portREMOVE_STATIC_QUALIFIER - #define static + #define static #endif /* The name allocated to the Idle task. This can be overridden by defining -configIDLE_TASK_NAME in FreeRTOSConfig.h. */ + * configIDLE_TASK_NAME in FreeRTOSConfig.h. */ #ifndef configIDLE_TASK_NAME - #define configIDLE_TASK_NAME "IDLE" + #define configIDLE_TASK_NAME "IDLE" #endif #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) - /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is - performed in a generic way that is not optimised to any particular - microcontroller architecture. */ - - /* uxTopReadyPriority holds the priority of the highest priority ready - state task. */ - #define taskRECORD_READY_PRIORITY( uxPriority ) \ - { \ - if( ( uxPriority ) > uxTopReadyPriority ) \ - { \ - uxTopReadyPriority = ( uxPriority ); \ - } \ - } /* taskRECORD_READY_PRIORITY */ - - /*-----------------------------------------------------------*/ - - #define taskSELECT_HIGHEST_PRIORITY_TASK() \ - { \ - UBaseType_t uxTopPriority = uxTopReadyPriority; \ - \ - /* Find the highest priority queue that contains ready tasks. */ \ - while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \ - { \ - configASSERT( uxTopPriority ); \ - --uxTopPriority; \ - } \ - \ - /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ - the same priority get an equal share of the processor time. */ \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ - uxTopReadyPriority = uxTopPriority; \ - } /* taskSELECT_HIGHEST_PRIORITY_TASK */ - - /*-----------------------------------------------------------*/ - - /* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as - they are only required when a port optimised method of task selection is - being used. */ - #define taskRESET_READY_PRIORITY( uxPriority ) - #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is + * performed in a generic way that is not optimised to any particular + * microcontroller architecture. */ + +/* uxTopReadyPriority holds the priority of the highest priority ready + * state task. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) \ + { \ + if( ( uxPriority ) > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( uxPriority ); \ + } \ + } /* taskRECORD_READY_PRIORITY */ + +/*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + UBaseType_t uxTopPriority = uxTopReadyPriority; \ + \ + /* Find the highest priority queue that contains ready tasks. */ \ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \ + { \ + configASSERT( uxTopPriority ); \ + --uxTopPriority; \ + } \ + \ + /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ + * the same priority get an equal share of the processor time. */ \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + uxTopReadyPriority = uxTopPriority; \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK */ + +/*-----------------------------------------------------------*/ + +/* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as + * they are only required when a port optimised method of task selection is + * being used. */ + #define taskRESET_READY_PRIORITY( uxPriority ) + #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) #else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ - /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is - performed in a way that is tailored to the particular microcontroller - architecture being used. */ - - /* A port optimised version is provided. Call the port defined macros. */ - #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority ) - - /*-----------------------------------------------------------*/ - - #define taskSELECT_HIGHEST_PRIORITY_TASK() \ - { \ - UBaseType_t uxTopPriority; \ - \ - /* Find the highest priority list that contains ready tasks. */ \ - portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ - configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ - } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ - - /*-----------------------------------------------------------*/ - - /* A port optimised version is provided, call it only if the TCB being reset - is being referenced from a ready list. If it is referenced from a delayed - or suspended list then it won't be in a ready list. */ - #define taskRESET_READY_PRIORITY( uxPriority ) \ - { \ - if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ - { \ - portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ - } \ - } +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is + * performed in a way that is tailored to the particular microcontroller + * architecture being used. */ + +/* A port optimised version is provided. Call the port defined macros. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + +/*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + UBaseType_t uxTopPriority; \ + \ + /* Find the highest priority list that contains ready tasks. */ \ + portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ + configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ + +/*-----------------------------------------------------------*/ + +/* A port optimised version is provided, call it only if the TCB being reset + * is being referenced from a ready list. If it is referenced from a delayed + * or suspended list then it won't be in a ready list. */ + #define taskRESET_READY_PRIORITY( uxPriority ) \ + { \ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ + { \ + portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ + } \ + } #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ /*-----------------------------------------------------------*/ /* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick -count overflows. */ -#define taskSWITCH_DELAYED_LISTS() \ -{ \ - List_t *pxTemp; \ - \ - /* The delayed tasks list should be empty when the lists are switched. */ \ - configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ - \ - pxTemp = pxDelayedTaskList; \ - pxDelayedTaskList = pxOverflowDelayedTaskList; \ - pxOverflowDelayedTaskList = pxTemp; \ - xNumOfOverflows++; \ - prvResetNextTaskUnblockTime(); \ -} + * count overflows. */ +#define taskSWITCH_DELAYED_LISTS() \ + { \ + List_t * pxTemp; \ + \ + /* The delayed tasks list should be empty when the lists are switched. */ \ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ + \ + pxTemp = pxDelayedTaskList; \ + pxDelayedTaskList = pxOverflowDelayedTaskList; \ + pxOverflowDelayedTaskList = pxTemp; \ + xNumOfOverflows++; \ + prvResetNextTaskUnblockTime(); \ + } /*-----------------------------------------------------------*/ @@ -215,11 +216,11 @@ count overflows. */ * Place the task represented by pxTCB into the appropriate ready list for * the task. It is inserted at the end of the list. */ -#define prvAddTaskToReadyList( pxTCB ) \ - traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ - taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ - vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ - tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) +#define prvAddTaskToReadyList( pxTCB ) \ + traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ + taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ + vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ + tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) /*-----------------------------------------------------------*/ /* @@ -228,20 +229,20 @@ count overflows. */ * task should be used in place of the parameter. This macro simply checks to * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority -of the task to which it belongs (coded to allow it to be held in reverse -priority order). However, it is occasionally borrowed for other purposes. It -is important its value is not updated due to a task priority change while it is -being used for another purpose. The following bit definition is used to inform -the scheduler that the value should not be changed - in which case it is the -responsibility of whichever module is using the value to ensure it gets set back -to its original value when it is released. */ -#if( configUSE_16_BIT_TICKS == 1 ) - #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U + * of the task to which it belongs (coded to allow it to be held in reverse + * priority order). However, it is occasionally borrowed for other purposes. It + * is important its value is not updated due to a task priority change while it is + * being used for another purpose. The following bit definition is used to inform + * the scheduler that the value should not be changed - in which case it is the + * responsibility of whichever module is using the value to ensure it gets set back + * to its original value when it is released. */ +#if ( configUSE_16_BIT_TICKS == 1 ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U #else - #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL #endif /* @@ -249,158 +250,151 @@ to its original value when it is released. */ * and stores task state information, including a pointer to the task's context * (the task's run time environment, including register values) */ -typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { - volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ - - #if ( portUSING_MPU_WRAPPERS == 1 ) - xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ - #endif - - ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ - ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ - UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ - StackType_t *pxStack; /*< Points to the start of the stack. */ - char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - - #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) - StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */ - #endif - - #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ - #endif - - #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ - UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ - #endif - - #if ( configUSE_MUTEXES == 1 ) - UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ - UBaseType_t uxMutexesHeld; - #endif - - #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - TaskHookFunction_t pxTaskTag; - #endif - - #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) - void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; - #endif - - #if( configGENERATE_RUN_TIME_STATS == 1 ) - uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ - #endif - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - /* Allocate a Newlib reent structure that is specific to this task. - Note Newlib support has been included by popular demand, but is not - used by the FreeRTOS maintainers themselves. FreeRTOS is not - responsible for resulting newlib operation. User must be familiar with - newlib and must provide system-wide implementations of the necessary - stubs. Be warned that (at the time of writing) the current newlib design - implements a system-wide malloc() that must be provided with locks. - - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ - struct _reent xNewLib_reent; - #endif - - #if( configUSE_TASK_NOTIFICATIONS == 1 ) - volatile uint32_t ulNotifiedValue; - volatile uint8_t ucNotifyState; - #endif - - /* See the comments in FreeRTOS.h with the definition of - tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ - uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ - #endif - - #if( INCLUDE_xTaskAbortDelay == 1 ) - uint8_t ucDelayAborted; - #endif - - #if( configUSE_POSIX_ERRNO == 1 ) - int iTaskErrno; - #endif - + volatile StackType_t * pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ + StackType_t * pxStack; /*< Points to the start of the stack. */ + char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + + /* Allocate a Newlib reent structure that is specific to this task. + * Note Newlib support has been included by popular demand, but is not + * used by the FreeRTOS maintainers themselves. FreeRTOS is not + * responsible for resulting newlib operation. User must be familiar with + * newlib and must provide system-wide implementations of the necessary + * stubs. Be warned that (at the time of writing) the current newlib design + * implements a system-wide malloc() that must be provided with locks. + * + * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * for additional information. */ + struct _reent xNewLib_reent; + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + #endif + + /* See the comments in FreeRTOS.h with the definition of + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDelayAborted; + #endif + + #if ( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name -below to enable the use of older kernel aware debuggers. */ + * below to enable the use of older kernel aware debuggers. */ typedef tskTCB TCB_t; /*lint -save -e956 A manual analysis and inspection has been used to determine -which static variables must be declared volatile. */ + * which static variables must be declared volatile. */ PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; /* Lists for ready and blocked tasks. -------------------- -xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but -doing so breaks some kernel aware debuggers and debuggers that rely on removing -the static qualifier. */ -PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ -PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ -PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ -PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ + * xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but + * doing so breaks some kernel aware debuggers and debuggers that rely on removing + * the static qualifier. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ -#if( INCLUDE_vTaskDelete == 1 ) +#if ( INCLUDE_vTaskDelete == 1 ) - PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ - PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; #endif #if ( INCLUDE_vTaskSuspend == 1 ) - PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ + PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ #endif /* Global POSIX errno. Its value is changed upon context switching to match -the errno of the currently running task. */ + * the errno of the currently running task. */ #if ( configUSE_POSIX_ERRNO == 1 ) - int FreeRTOS_errno = 0; + int FreeRTOS_errno = 0; #endif /* Other file private variables. --------------------------------*/ -PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; -PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; -PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; -PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; -PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; -PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; -PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; -PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; -PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ -PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ - -/* Improve support for OpenOCD. The kernel tracks Ready tasks via priority lists. - * For tracking the state of remote threads, OpenOCD uses uxTopUsedPriority - * to determine the number of priority lists to read back from the remote target. */ -const volatile UBaseType_t uxTopUsedPriority = configMAX_PRIORITIES - 1U; - - +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ /* Context switches are held pending while the scheduler is suspended. Also, -interrupts must not manipulate the xStateListItem of a TCB, or any of the -lists the xStateListItem can be referenced from, if the scheduler is suspended. -If an interrupt needs to unblock a task while the scheduler is suspended then it -moves the task's event list item into the xPendingReadyList, ready for the -kernel to move the task from the pending ready list into the real ready list -when the scheduler is unsuspended. The pending ready list itself can only be -accessed from a critical section. */ -PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; + * interrupts must not manipulate the xStateListItem of a TCB, or any of the + * lists the xStateListItem can be referenced from, if the scheduler is suspended. + * If an interrupt needs to unblock a task while the scheduler is suspended then it + * moves the task's event list item into the xPendingReadyList, ready for the + * kernel to move the task from the pending ready list into the real ready list + * when the scheduler is unsuspended. The pending ready list itself can only be + * accessed from a critical section. */ +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; #if ( configGENERATE_RUN_TIME_STATS == 1 ) - /* Do not move these variables to function scope as doing so prevents the - code working with debuggers that need to remove the static qualifier. */ - PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ - PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ +/* Do not move these variables to function scope as doing so prevents the + * code working with debuggers that need to remove the static qualifier. */ + PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static volatile uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ #endif @@ -408,25 +402,6 @@ PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t /*-----------------------------------------------------------*/ -/* Callback function prototypes. --------------------------*/ -#if( configCHECK_FOR_STACK_OVERFLOW > 0 ) - - extern void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ); - -#endif - -#if( configUSE_TICK_HOOK > 0 ) - - extern void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an application callback. */ - -#endif - -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - - extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is an application callback. */ - -#endif - /* File private functions. --------------------------------*/ /** @@ -436,7 +411,7 @@ PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t */ #if ( INCLUDE_vTaskSuspend == 1 ) - static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; #endif /* INCLUDE_vTaskSuspend */ @@ -457,7 +432,7 @@ static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; * void prvIdleTask( void *pvParameters ); * */ -static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ) PRIVILEGED_FUNCTION; /* * Utility to free all memory allocated by the scheduler to hold a TCB, @@ -468,7 +443,7 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); */ #if ( INCLUDE_vTaskDelete == 1 ) - static void prvDeleteTCB( TCB_t *pxTCB ) PRIVILEGED_FUNCTION; + static void prvDeleteTCB( TCB_t * pxTCB ) PRIVILEGED_FUNCTION; #endif @@ -483,7 +458,8 @@ static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; * The currently executing task is entering the Blocked state. Add the task to * either the current or the overflow delayed task list. */ -static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; /* * Fills an TaskStatus_t structure with information on each task that is @@ -495,7 +471,9 @@ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseT */ #if ( configUSE_TRACE_FACILITY == 1 ) - static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) PRIVILEGED_FUNCTION; + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) PRIVILEGED_FUNCTION; #endif @@ -505,7 +483,8 @@ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseT */ #if ( INCLUDE_xTaskGetHandle == 1 ) - static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; #endif @@ -516,7 +495,7 @@ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseT */ #if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; #endif @@ -531,7 +510,7 @@ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseT */ #if ( configUSE_TICKLESS_IDLE != 0 ) - static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; #endif @@ -539,15 +518,16 @@ static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseT * Set xNextTaskUnblockTime to the time at which the next Blocked state task * will exit the Blocked state. */ -static void prvResetNextTaskUnblockTime( void ); +static void prvResetNextTaskUnblockTime( void ) PRIVILEGED_FUNCTION; #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) - /* - * Helper function used to pad task names with spaces when printing out - * human readable tables of task information. - */ - static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) PRIVILEGED_FUNCTION; +/* + * Helper function used to pad task names with spaces when printing out + * human readable tables of task information. + */ + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) PRIVILEGED_FUNCTION; #endif @@ -555,20 +535,20 @@ static void prvResetNextTaskUnblockTime( void ); * Called after a Task_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask, - TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /* * Called after a new task has been created and initialised to place the task * under the control of the scheduler. */ -static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION; +static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION; /* * freertos_tasks_c_additions_init() should only be called if the user definable @@ -577,1331 +557,1344 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION; */ #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT - static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; #endif /*-----------------------------------------------------------*/ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - - TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - StackType_t * const puxStackBuffer, - StaticTask_t * const pxTaskBuffer ) - { - TCB_t *pxNewTCB; - TaskHandle_t xReturn; - - configASSERT( puxStackBuffer != NULL ); - configASSERT( pxTaskBuffer != NULL ); - - #if( configASSERT_DEFINED == 1 ) - { - /* Sanity check that the size of the structure used to declare a - variable of type StaticTask_t equals the size of the real task - structure. */ - volatile size_t xSize = sizeof( StaticTask_t ); - configASSERT( xSize == sizeof( TCB_t ) ); - ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ - } - #endif /* configASSERT_DEFINED */ - - - if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) - { - /* The memory used for the task's TCB and stack are passed into this - function - use them. */ - pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ - pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; - - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ - { - /* Tasks can be created statically or dynamically, so note this - task was created statically in case the task is later deleted. */ - pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; - } - #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ - - prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); - prvAddNewTaskToReadyList( pxNewTCB ); - } - else - { - xReturn = NULL; - } - - return xReturn; - } +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) + { + TCB_t * pxNewTCB; + TaskHandle_t xReturn; + + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticTask_t equals the size of the real task + * structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) + { + /* The memory used for the task's TCB and stack are passed into this + * function - use them. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + } + else + { + xReturn = NULL; + } + + return xReturn; + } #endif /* SUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) - - BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) - { - TCB_t *pxNewTCB; - BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - - configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); - configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); - - if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) - { - /* Allocate space for the TCB. Where the memory comes from depends - on the implementation of the port malloc function and whether or - not static allocation is being used. */ - pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; - - /* Store the stack location in the TCB. */ - pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; - - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) - { - /* Tasks can be created statically or dynamically, so note this - task was created statically in case the task is later deleted. */ - pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; - } - #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ - - prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, - pxTaskDefinition->pcName, - ( uint32_t ) pxTaskDefinition->usStackDepth, - pxTaskDefinition->pvParameters, - pxTaskDefinition->uxPriority, - pxCreatedTask, pxNewTCB, - pxTaskDefinition->xRegions ); - - prvAddNewTaskToReadyList( pxNewTCB ); - xReturn = pdPASS; - } - - return xReturn; - } +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + * on the implementation of the port malloc function and whether or + * not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + + return xReturn; + } #endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ /*-----------------------------------------------------------*/ -#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - - BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) - { - TCB_t *pxNewTCB; - BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - - configASSERT( pxTaskDefinition->puxStackBuffer ); - - if( pxTaskDefinition->puxStackBuffer != NULL ) - { - /* Allocate space for the TCB. Where the memory comes from depends - on the implementation of the port malloc function and whether or - not static allocation is being used. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Store the stack location in the TCB. */ - pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; - - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) - { - /* Tasks can be created statically or dynamically, so note - this task had a statically allocated stack in case it is - later deleted. The TCB was allocated dynamically. */ - pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; - } - #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ - - prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, - pxTaskDefinition->pcName, - ( uint32_t ) pxTaskDefinition->usStackDepth, - pxTaskDefinition->pvParameters, - pxTaskDefinition->uxPriority, - pxCreatedTask, pxNewTCB, - pxTaskDefinition->xRegions ); - - prvAddNewTaskToReadyList( pxNewTCB ); - xReturn = pdPASS; - } - } - - return xReturn; - } +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + * on the implementation of the port malloc function and whether or + * not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + * this task had a statically allocated stack in case it is + * later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + } + + return xReturn; + } #endif /* portUSING_MPU_WRAPPERS */ /*-----------------------------------------------------------*/ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - - BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const configSTACK_DEPTH_TYPE usStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask ) - { - TCB_t *pxNewTCB; - BaseType_t xReturn; - - /* If the stack grows down then allocate the stack then the TCB so the stack - does not grow into the TCB. Likewise if the stack grows up then allocate - the TCB then the stack. */ - #if( portSTACK_GROWTH > 0 ) - { - /* Allocate space for the TCB. Where the memory comes from depends on - the implementation of the port malloc function and whether or not static - allocation is being used. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); - - if( pxNewTCB != NULL ) - { - /* Allocate space for the stack used by the task being created. - The base of the stack memory stored in the TCB so the task can - be deleted later if required. */ - pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - if( pxNewTCB->pxStack == NULL ) - { - /* Could not allocate the stack. Delete the allocated TCB. */ - vPortFree( pxNewTCB ); - pxNewTCB = NULL; - } - } - } - #else /* portSTACK_GROWTH */ - { - StackType_t *pxStack; - - /* Allocate space for the stack used by the task being created. */ - pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ - - if( pxStack != NULL ) - { - /* Allocate space for the TCB. */ - pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ - - if( pxNewTCB != NULL ) - { - /* Store the stack location in the TCB. */ - pxNewTCB->pxStack = pxStack; - } - else - { - /* The stack cannot be used as the TCB was not created. Free - it again. */ - vPortFree( pxStack ); - } - } - else - { - pxNewTCB = NULL; - } - } - #endif /* portSTACK_GROWTH */ - - if( pxNewTCB != NULL ) - { - #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ - { - /* Tasks can be created statically or dynamically, so note this - task was created dynamically in case it is later deleted. */ - pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; - } - #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ - - prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); - prvAddNewTaskToReadyList( pxNewTCB ); - xReturn = pdPASS; - } - else - { - xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; - } - - return xReturn; - } +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + /* If the stack grows down then allocate the stack then the TCB so the stack + * does not grow into the TCB. Likewise if the stack grows up then allocate + * the TCB then the stack. */ + #if ( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + * the implementation of the port malloc function and whether or not static + * allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + * The base of the stack memory stored in the TCB so the task can + * be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } + } + #else /* portSTACK_GROWTH */ + { + StackType_t * pxStack; + + /* Allocate space for the stack used by the task being created. */ + pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ + + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + * it again. */ + vPortFree( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } + } + #endif /* portSTACK_GROWTH */ + + if( pxNewTCB != NULL ) + { + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + * task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; + } #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ -static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, - const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const uint32_t ulStackDepth, - void * const pvParameters, - UBaseType_t uxPriority, - TaskHandle_t * const pxCreatedTask, - TCB_t *pxNewTCB, - const MemoryRegion_t * const xRegions ) +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) { -StackType_t *pxTopOfStack; -UBaseType_t x; - - #if( portUSING_MPU_WRAPPERS == 1 ) - /* Should the task be created in privileged mode? */ - BaseType_t xRunPrivileged; - if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) - { - xRunPrivileged = pdTRUE; - } - else - { - xRunPrivileged = pdFALSE; - } - uxPriority &= ~portPRIVILEGE_BIT; - #endif /* portUSING_MPU_WRAPPERS == 1 */ - - /* Avoid dependency on memset() if it is not required. */ - #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) - { - /* Fill the stack with a known value to assist debugging. */ - ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); - } - #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ - - /* Calculate the top of stack address. This depends on whether the stack - grows from high memory to low (as per the 80x86) or vice versa. - portSTACK_GROWTH is used to make the result positive or negative as required - by the port. */ - #if( portSTACK_GROWTH < 0 ) - { - pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); - pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ - - /* Check the alignment of the calculated top of stack is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); - - #if( configRECORD_STACK_HIGH_ADDRESS == 1 ) - { - /* Also record the stack's high address, which may assist - debugging. */ - pxNewTCB->pxEndOfStack = pxTopOfStack; - } - #endif /* configRECORD_STACK_HIGH_ADDRESS */ - } - #else /* portSTACK_GROWTH */ - { - pxTopOfStack = pxNewTCB->pxStack; - - /* Check the alignment of the stack buffer is correct. */ - configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); - - /* The other extreme of the stack space is required if stack checking is - performed. */ - pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); - } - #endif /* portSTACK_GROWTH */ - - /* Store the task name in the TCB. */ - if( pcName != NULL ) - { - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) - { - pxNewTCB->pcTaskName[ x ] = pcName[ x ]; - - /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than - configMAX_TASK_NAME_LEN characters just in case the memory after the - string is not accessible (extremely unlikely). */ - if( pcName[ x ] == ( char ) 0x00 ) - { - break; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - /* Ensure the name string is terminated in the case that the string length - was greater or equal to configMAX_TASK_NAME_LEN. */ - pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; - } - else - { - /* The task has not been given a name, so just ensure there is a NULL - terminator when it is read out. */ - pxNewTCB->pcTaskName[ 0 ] = 0x00; - } - - /* This is used as an array index so must ensure it's not too large. First - remove the privilege bit if one is present. */ - if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) - { - uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - pxNewTCB->uxPriority = uxPriority; - #if ( configUSE_MUTEXES == 1 ) - { - pxNewTCB->uxBasePriority = uxPriority; - pxNewTCB->uxMutexesHeld = 0; - } - #endif /* configUSE_MUTEXES */ - - vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); - vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); - - /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get - back to the containing TCB from a generic item in a list. */ - listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); - - /* Event lists are always in priority order. */ - listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); - - #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - { - pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; - } - #endif /* portCRITICAL_NESTING_IN_TCB */ - - #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - { - pxNewTCB->pxTaskTag = NULL; - } - #endif /* configUSE_APPLICATION_TASK_TAG */ - - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - { - pxNewTCB->ulRunTimeCounter = 0UL; - } - #endif /* configGENERATE_RUN_TIME_STATS */ - - #if ( portUSING_MPU_WRAPPERS == 1 ) - { - vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); - } - #else - { - /* Avoid compiler warning about unreferenced parameter. */ - ( void ) xRegions; - } - #endif - - #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) - { - for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) - { - pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; - } - } - #endif - - #if ( configUSE_TASK_NOTIFICATIONS == 1 ) - { - pxNewTCB->ulNotifiedValue = 0; - pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; - } - #endif - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - /* Initialise this task's Newlib reent structure. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ - _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); - } - #endif - - #if( INCLUDE_xTaskAbortDelay == 1 ) - { - pxNewTCB->ucDelayAborted = pdFALSE; - } - #endif - - /* Initialize the TCB stack to look as if the task was already running, - but had been interrupted by the scheduler. The return address is set - to the start of the task function. Once the stack has been initialised - the top of stack variable is updated. */ - #if( portUSING_MPU_WRAPPERS == 1 ) - { - /* If the port has capability to detect stack overflow, - pass the stack end address to the stack initialization - function as well. */ - #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) - { - #if( portSTACK_GROWTH < 0 ) - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); - } - #else /* portSTACK_GROWTH */ - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); - } - #endif /* portSTACK_GROWTH */ - } - #else /* portHAS_STACK_OVERFLOW_CHECKING */ - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); - } - #endif /* portHAS_STACK_OVERFLOW_CHECKING */ - } - #else /* portUSING_MPU_WRAPPERS */ - { - /* If the port has capability to detect stack overflow, - pass the stack end address to the stack initialization - function as well. */ - #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) - { - #if( portSTACK_GROWTH < 0 ) - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); - } - #else /* portSTACK_GROWTH */ - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); - } - #endif /* portSTACK_GROWTH */ - } - #else /* portHAS_STACK_OVERFLOW_CHECKING */ - { - pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); - } - #endif /* portHAS_STACK_OVERFLOW_CHECKING */ - } - #endif /* portUSING_MPU_WRAPPERS */ - - if( pxCreatedTask != NULL ) - { - /* Pass the handle out in an anonymous way. The handle can be used to - change the created task's priority, delete the created task, etc.*/ - *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + StackType_t * pxTopOfStack; + UBaseType_t x; + + #if ( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); + } + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ + + /* Calculate the top of stack address. This depends on whether the stack + * grows from high memory to low (as per the 80x86) or vice versa. + * portSTACK_GROWTH is used to make the result positive or negative as required + * by the port. */ + #if ( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + #if ( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + * debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* The other extreme of the stack space is required if stack checking is + * performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + if( pcName != NULL ) + { + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + * configMAX_TASK_NAME_LEN characters just in case the memory after the + * string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure the name string is terminated in the case that the string length + * was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + } + else + { + /* The task has not been given a name, so just ensure there is a NULL + * terminator when it is read out. */ + pxNewTCB->pcTaskName[ 0 ] = 0x00; + } + + /* This is used as an array index so must ensure it's not too large. First + * remove the privilege bit if one is present. */ + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + pxNewTCB->uxMutexesHeld = 0; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + * back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; + } + #endif /* portCRITICAL_NESTING_IN_TCB */ + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxNewTCB->pxTaskTag = NULL; + } + #endif /* configUSE_APPLICATION_TASK_TAG */ + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxNewTCB->ulRunTimeCounter = 0UL; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + { + memset( ( void * ) &( pxNewTCB->pvThreadLocalStoragePointers[ 0 ] ), 0x00, sizeof( pxNewTCB->pvThreadLocalStoragePointers ) ); + } + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + memset( ( void * ) &( pxNewTCB->ulNotifiedValue[ 0 ] ), 0x00, sizeof( pxNewTCB->ulNotifiedValue ) ); + memset( ( void * ) &( pxNewTCB->ucNotifyState[ 0 ] ), 0x00, sizeof( pxNewTCB->ucNotifyState ) ); + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Initialise this task's Newlib reent structure. + * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * for additional information. */ + _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); + } + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + { + pxNewTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + * but had been interrupted by the scheduler. The return address is set + * to the start of the task function. Once the stack has been initialised + * the top of stack variable is updated. */ + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #else /* portUSING_MPU_WRAPPERS */ + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + * change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } /*-----------------------------------------------------------*/ -static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) +static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) { - /* Ensure interrupts don't access the task lists while the lists are being - updated. */ - taskENTER_CRITICAL(); - { - uxCurrentNumberOfTasks++; - if( pxCurrentTCB == NULL ) - { - /* There are no other tasks, or all the other tasks are in - the suspended state - make this the current task. */ - pxCurrentTCB = pxNewTCB; - - if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) - { - /* This is the first task to be created so do the preliminary - initialisation required. We will not recover if this call - fails, but we will report the failure. */ - prvInitialiseTaskLists(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - /* If the scheduler is not already running, make this task the - current task if it is the highest priority task to be created - so far. */ - if( xSchedulerRunning == pdFALSE ) - { - if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) - { - pxCurrentTCB = pxNewTCB; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - uxTaskNumber++; - - #if ( configUSE_TRACE_FACILITY == 1 ) - { - /* Add a counter into the TCB for tracing only. */ - pxNewTCB->uxTCBNumber = uxTaskNumber; - } - #endif /* configUSE_TRACE_FACILITY */ - traceTASK_CREATE( pxNewTCB ); - - prvAddTaskToReadyList( pxNewTCB ); - - portSETUP_TCB( pxNewTCB ); - } - taskEXIT_CRITICAL(); - - if( xSchedulerRunning != pdFALSE ) - { - /* If the created task is of a higher priority than the current task - then it should run now. */ - if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) - { - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + /* Ensure interrupts don't access the task lists while the lists are being + * updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + * the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + * initialisation required. We will not recover if this call + * fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + * current task if it is the highest priority task to be created + * so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + * then it should run now. */ + if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } } /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskDelete == 1 ) - void vTaskDelete( TaskHandle_t xTaskToDelete ) - { - TCB_t *pxTCB; - - taskENTER_CRITICAL(); - { - /* If null is passed in here then it is the calling task that is - being deleted. */ - pxTCB = prvGetTCBFromHandle( xTaskToDelete ); - - /* Remove task from the ready/delayed list. */ - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Is the task waiting on an event also? */ - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Increment the uxTaskNumber also so kernel aware debuggers can - detect that the task lists need re-generating. This is done before - portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will - not return. */ - uxTaskNumber++; - - if( pxTCB == pxCurrentTCB ) - { - /* A task is deleting itself. This cannot complete within the - task itself, as a context switch to another task is required. - Place the task in the termination list. The idle task will - check the termination list and free up any memory allocated by - the scheduler for the TCB and stack of the deleted task. */ - vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); - - /* Increment the ucTasksDeleted variable so the idle task knows - there is a task that has been deleted and that it should therefore - check the xTasksWaitingTermination list. */ - ++uxDeletedTasksWaitingCleanUp; - - /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as - portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ - traceTASK_DELETE( pxTCB ); - - /* The pre-delete hook is primarily for the Windows simulator, - in which Windows specific clean up operations are performed, - after which it is not possible to yield away from this task - - hence xYieldPending is used to latch that a context switch is - required. */ - portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); - } - else - { - --uxCurrentNumberOfTasks; - traceTASK_DELETE( pxTCB ); - prvDeleteTCB( pxTCB ); - - /* Reset the next expected unblock time in case it referred to - the task that has just been deleted. */ - prvResetNextTaskUnblockTime(); - } - } - taskEXIT_CRITICAL(); - - /* Force a reschedule if it is the currently running task that has just - been deleted. */ - if( xSchedulerRunning != pdFALSE ) - { - if( pxTCB == pxCurrentTCB ) - { - configASSERT( uxSchedulerSuspended == 0 ); - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } + void vTaskDelete( TaskHandle_t xTaskToDelete ) + { + TCB_t * pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the calling task that is + * being deleted. */ + pxTCB = prvGetTCBFromHandle( xTaskToDelete ); + + /* Remove task from the ready/delayed list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Increment the uxTaskNumber also so kernel aware debuggers can + * detect that the task lists need re-generating. This is done before + * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will + * not return. */ + uxTaskNumber++; + + if( pxTCB == pxCurrentTCB ) + { + /* A task is deleting itself. This cannot complete within the + * task itself, as a context switch to another task is required. + * Place the task in the termination list. The idle task will + * check the termination list and free up any memory allocated by + * the scheduler for the TCB and stack of the deleted task. */ + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + * there is a task that has been deleted and that it should therefore + * check the xTasksWaitingTermination list. */ + ++uxDeletedTasksWaitingCleanUp; + + /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as + * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ + traceTASK_DELETE( pxTCB ); + + /* The pre-delete hook is primarily for the Windows simulator, + * in which Windows specific clean up operations are performed, + * after which it is not possible to yield away from this task - + * hence xYieldPending is used to latch that a context switch is + * required. */ + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + } + else + { + --uxCurrentNumberOfTasks; + traceTASK_DELETE( pxTCB ); + prvDeleteTCB( pxTCB ); + + /* Reset the next expected unblock time in case it referred to + * the task that has just been deleted. */ + prvResetNextTaskUnblockTime(); + } + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if it is the currently running task that has just + * been deleted. */ + if( xSchedulerRunning != pdFALSE ) + { + if( pxTCB == pxCurrentTCB ) + { + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } #endif /* INCLUDE_vTaskDelete */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskDelayUntil == 1 ) - void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) - { - TickType_t xTimeToWake; - BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; - - configASSERT( pxPreviousWakeTime ); - configASSERT( ( xTimeIncrement > 0U ) ); - configASSERT( uxSchedulerSuspended == 0 ); - - vTaskSuspendAll(); - { - /* Minor optimisation. The tick count cannot change in this - block. */ - const TickType_t xConstTickCount = xTickCount; - - /* Generate the tick time at which the task wants to wake. */ - xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; - - if( xConstTickCount < *pxPreviousWakeTime ) - { - /* The tick count has overflowed since this function was - lasted called. In this case the only time we should ever - actually delay is if the wake time has also overflowed, - and the wake time is greater than the tick time. When this - is the case it is as if neither time had overflowed. */ - if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) - { - xShouldDelay = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - /* The tick time has not overflowed. In this case we will - delay if either the wake time has overflowed, and/or the - tick time is less than the wake time. */ - if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) - { - xShouldDelay = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - /* Update the wake time ready for the next call. */ - *pxPreviousWakeTime = xTimeToWake; - - if( xShouldDelay != pdFALSE ) - { - traceTASK_DELAY_UNTIL( xTimeToWake ); - - /* prvAddCurrentTaskToDelayedList() needs the block time, not - the time to wake, so subtract the current tick count. */ - prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - xAlreadyYielded = xTaskResumeAll(); - - /* Force a reschedule if xTaskResumeAll has not already done so, we may - have put ourselves to sleep. */ - if( xAlreadyYielded == pdFALSE ) - { - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + configASSERT( uxSchedulerSuspended == 0 ); + + vTaskSuspendAll(); + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount; + + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xConstTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + * lasted called. In this case the only time we should ever + * actually delay is if the wake time has also overflowed, + * and the wake time is greater than the tick time. When this + * is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The tick time has not overflowed. In this case we will + * delay if either the wake time has overflowed, and/or the + * tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL( xTimeToWake ); + + /* prvAddCurrentTaskToDelayedList() needs the block time, not + * the time to wake, so subtract the current tick count. */ + prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* INCLUDE_vTaskDelayUntil */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskDelay == 1 ) - void vTaskDelay( const TickType_t xTicksToDelay ) - { - BaseType_t xAlreadyYielded = pdFALSE; - - /* A delay time of zero just forces a reschedule. */ - if( xTicksToDelay > ( TickType_t ) 0U ) - { - configASSERT( uxSchedulerSuspended == 0 ); - vTaskSuspendAll(); - { - traceTASK_DELAY(); - - /* A task that is removed from the event list while the - scheduler is suspended will not get placed in the ready - list or removed from the blocked list until the scheduler - is resumed. - - This task cannot be in an event list as it is the currently - executing task. */ - prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); - } - xAlreadyYielded = xTaskResumeAll(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Force a reschedule if xTaskResumeAll has not already done so, we may - have put ourselves to sleep. */ - if( xAlreadyYielded == pdFALSE ) - { - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskDelay( const TickType_t xTicksToDelay ) + { + BaseType_t xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( TickType_t ) 0U ) + { + configASSERT( uxSchedulerSuspended == 0 ); + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + * scheduler is suspended will not get placed in the ready + * list or removed from the blocked list until the scheduler + * is resumed. + * + * This task cannot be in an event list as it is the currently + * executing task. */ + prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); + } + xAlreadyYielded = xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* INCLUDE_vTaskDelay */ /*-----------------------------------------------------------*/ -#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) - - eTaskState eTaskGetState( TaskHandle_t xTask ) - { - eTaskState eReturn; - List_t const * pxStateList, *pxDelayedList, *pxOverflowedDelayedList; - const TCB_t * const pxTCB = xTask; - - configASSERT( pxTCB ); - - if( pxTCB == pxCurrentTCB ) - { - /* The task calling this function is querying its own state. */ - eReturn = eRunning; - } - else - { - taskENTER_CRITICAL(); - { - pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); - pxDelayedList = pxDelayedTaskList; - pxOverflowedDelayedList = pxOverflowDelayedTaskList; - } - taskEXIT_CRITICAL(); - - if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) - { - /* The task being queried is referenced from one of the Blocked - lists. */ - eReturn = eBlocked; - } - - #if ( INCLUDE_vTaskSuspend == 1 ) - else if( pxStateList == &xSuspendedTaskList ) - { - /* The task being queried is referenced from the suspended - list. Is it genuinely suspended or is it blocked - indefinitely? */ - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) - { - #if( configUSE_TASK_NOTIFICATIONS == 1 ) - { - /* The task does not appear on the event list item of - and of the RTOS objects, but could still be in the - blocked state if it is waiting on its notification - rather than waiting on an object. */ - if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) - { - eReturn = eBlocked; - } - else - { - eReturn = eSuspended; - } - } - #else - { - eReturn = eSuspended; - } - #endif - } - else - { - eReturn = eBlocked; - } - } - #endif - - #if ( INCLUDE_vTaskDelete == 1 ) - else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) - { - /* The task being queried is referenced from the deleted - tasks list, or it is not referenced from any lists at - all. */ - eReturn = eDeleted; - } - #endif - - else /*lint !e525 Negative indentation is intended to make use of pre-processor clearer. */ - { - /* If the task is not in any other state, it must be in the - Ready (including pending ready) state. */ - eReturn = eReady; - } - } - - return eReturn; - } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ +#if ( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) + + eTaskState eTaskGetState( TaskHandle_t xTask ) + { + eTaskState eReturn; + List_t const * pxStateList, * pxDelayedList, * pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; + + configASSERT( pxTCB ); + + if( pxTCB == pxCurrentTCB ) + { + /* The task calling this function is querying its own state. */ + eReturn = eRunning; + } + else + { + taskENTER_CRITICAL(); + { + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; + } + taskEXIT_CRITICAL(); + + if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) + { + /* The task being queried is referenced from one of the Blocked + * lists. */ + eReturn = eBlocked; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( pxStateList == &xSuspendedTaskList ) + { + /* The task being queried is referenced from the suspended + * list. Is it genuinely suspended or is it blocked + * indefinitely? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) + { + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + /* The task does not appear on the event list item of + * and of the RTOS objects, but could still be in the + * blocked state if it is waiting on its notification + * rather than waiting on an object. If not, is + * suspended. */ + eReturn = eSuspended; + + for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + break; + } + } + } + #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + { + eReturn = eSuspended; + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + else + { + eReturn = eBlocked; + } + } + #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */ + + #if ( INCLUDE_vTaskDelete == 1 ) + else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) + { + /* The task being queried is referenced from the deleted + * tasks list, or it is not referenced from any lists at + * all. */ + eReturn = eDeleted; + } + #endif + + else /*lint !e525 Negative indentation is intended to make use of pre-processor clearer. */ + { + /* If the task is not in any other state, it must be in the + * Ready (including pending ready) state. */ + eReturn = eReady; + } + } + + return eReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ #endif /* INCLUDE_eTaskGetState */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) - { - TCB_t const *pxTCB; - UBaseType_t uxReturn; + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; - taskENTER_CRITICAL(); - { - /* If null is passed in here then it is the priority of the task - that called uxTaskPriorityGet() that is being queried. */ - pxTCB = prvGetTCBFromHandle( xTask ); - uxReturn = pxTCB->uxPriority; - } - taskEXIT_CRITICAL(); + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the task + * that called uxTaskPriorityGet() that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); - return uxReturn; - } + return uxReturn; + } #endif /* INCLUDE_uxTaskPriorityGet */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskPriorityGet == 1 ) - UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) - { - TCB_t const *pxTCB; - UBaseType_t uxReturn, uxSavedInterruptState; - - /* RTOS ports that support interrupt nesting have the concept of a - maximum system call (or maximum API call) interrupt priority. - Interrupts that are above the maximum system call priority are keep - permanently enabled, even when the RTOS kernel is in a critical section, - but cannot make any calls to FreeRTOS API functions. If configASSERT() - is defined in FreeRTOSConfig.h then - portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has - been assigned a priority above the configured maximum system call - priority. Only FreeRTOS functions that end in FromISR can be called - from interrupts that have been assigned a priority at or (logically) - below the maximum system call interrupt priority. FreeRTOS maintains a - separate interrupt safe API to ensure interrupt entry is as fast and as - simple as possible. More information (albeit Cortex-M specific) is - provided on the following link: - https://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); - { - /* If null is passed in here then it is the priority of the calling - task that is being queried. */ - pxTCB = prvGetTCBFromHandle( xTask ); - uxReturn = pxTCB->uxPriority; - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptState ); - - return uxReturn; - } + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn, uxSavedInterruptState; + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptState ); + + return uxReturn; + } #endif /* INCLUDE_uxTaskPriorityGet */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskPrioritySet == 1 ) - void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) - { - TCB_t *pxTCB; - UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; - BaseType_t xYieldRequired = pdFALSE; - - configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); - - /* Ensure the new priority is valid. */ - if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) - { - uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - taskENTER_CRITICAL(); - { - /* If null is passed in here then it is the priority of the calling - task that is being changed. */ - pxTCB = prvGetTCBFromHandle( xTask ); - - traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); - - #if ( configUSE_MUTEXES == 1 ) - { - uxCurrentBasePriority = pxTCB->uxBasePriority; - } - #else - { - uxCurrentBasePriority = pxTCB->uxPriority; - } - #endif - - if( uxCurrentBasePriority != uxNewPriority ) - { - /* The priority change may have readied a task of higher - priority than the calling task. */ - if( uxNewPriority > uxCurrentBasePriority ) - { - if( pxTCB != pxCurrentTCB ) - { - /* The priority of a task other than the currently - running task is being raised. Is the priority being - raised above that of the running task? */ - if( uxNewPriority >= pxCurrentTCB->uxPriority ) - { - xYieldRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - /* The priority of the running task is being raised, - but the running task must already be the highest - priority task able to run so no yield is required. */ - } - } - else if( pxTCB == pxCurrentTCB ) - { - /* Setting the priority of the running task down means - there may now be another task of higher priority that - is ready to execute. */ - xYieldRequired = pdTRUE; - } - else - { - /* Setting the priority of any other task down does not - require a yield as the running task must be above the - new priority of the task being modified. */ - } - - /* Remember the ready list the task might be referenced from - before its uxPriority member is changed so the - taskRESET_READY_PRIORITY() macro can function correctly. */ - uxPriorityUsedOnEntry = pxTCB->uxPriority; - - #if ( configUSE_MUTEXES == 1 ) - { - /* Only change the priority being used if the task is not - currently using an inherited priority. */ - if( pxTCB->uxBasePriority == pxTCB->uxPriority ) - { - pxTCB->uxPriority = uxNewPriority; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* The base priority gets set whatever. */ - pxTCB->uxBasePriority = uxNewPriority; - } - #else - { - pxTCB->uxPriority = uxNewPriority; - } - #endif - - /* Only reset the event list item value if the value is not - being used for anything else. */ - if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) - { - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* If the task is in the blocked or suspended list we need do - nothing more than change its priority variable. However, if - the task is in a ready list it needs to be removed and placed - in the list appropriate to its new priority. */ - if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) - { - /* The task is currently in its ready list - remove before - adding it to it's new ready list. As we are in a critical - section we can do this even if the scheduler is suspended. */ - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - /* It is known that the task is in its ready list so - there is no need to check again and the port level - reset macro can be called directly. */ - portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - prvAddTaskToReadyList( pxTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - if( xYieldRequired != pdFALSE ) - { - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Remove compiler warning about unused variables when the port - optimised task selection is not being used. */ - ( void ) uxPriorityUsedOnEntry; - } - } - taskEXIT_CRITICAL(); - } + void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) + { + TCB_t * pxTCB; + UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being changed. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentBasePriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentBasePriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentBasePriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + * priority than the calling task. */ + if( uxNewPriority > uxCurrentBasePriority ) + { + if( pxTCB != pxCurrentTCB ) + { + /* The priority of a task other than the currently + * running task is being raised. Is the priority being + * raised above that of the running task? */ + if( uxNewPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The priority of the running task is being raised, + * but the running task must already be the highest + * priority task able to run so no yield is required. */ + } + } + else if( pxTCB == pxCurrentTCB ) + { + /* Setting the priority of the running task down means + * there may now be another task of higher priority that + * is ready to execute. */ + xYieldRequired = pdTRUE; + } + else + { + /* Setting the priority of any other task down does not + * require a yield as the running task must be above the + * new priority of the task being modified. */ + } + + /* Remember the ready list the task might be referenced from + * before its uxPriority member is changed so the + * taskRESET_READY_PRIORITY() macro can function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + * currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else /* if ( configUSE_MUTEXES == 1 ) */ + { + pxTCB->uxPriority = uxNewPriority; + } + #endif /* if ( configUSE_MUTEXES == 1 ) */ + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task is in the blocked or suspended list we need do + * nothing more than change its priority variable. However, if + * the task is in a ready list it needs to be removed and placed + * in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + * adding it to it's new ready list. As we are in a critical + * section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xYieldRequired != pdFALSE ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Remove compiler warning about unused variables when the port + * optimised task selection is not being used. */ + ( void ) uxPriorityUsedOnEntry; + } + } + taskEXIT_CRITICAL(); + } #endif /* INCLUDE_vTaskPrioritySet */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskSuspend == 1 ) - void vTaskSuspend( TaskHandle_t xTaskToSuspend ) - { - TCB_t *pxTCB; - - taskENTER_CRITICAL(); - { - /* If null is passed in here then it is the running task that is - being suspended. */ - pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); - - traceTASK_SUSPEND( pxTCB ); - - /* Remove task from the ready/delayed list and place in the - suspended list. */ - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Is the task waiting on an event also? */ - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); - - #if( configUSE_TASK_NOTIFICATIONS == 1 ) - { - if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) - { - /* The task was blocked to wait for a notification, but is - now suspended, so no notification was received. */ - pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; - } - } - #endif - } - taskEXIT_CRITICAL(); - - if( xSchedulerRunning != pdFALSE ) - { - /* Reset the next expected unblock time in case it referred to the - task that is now in the Suspended state. */ - taskENTER_CRITICAL(); - { - prvResetNextTaskUnblockTime(); - } - taskEXIT_CRITICAL(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - if( pxTCB == pxCurrentTCB ) - { - if( xSchedulerRunning != pdFALSE ) - { - /* The current task has just been suspended. */ - configASSERT( uxSchedulerSuspended == 0 ); - portYIELD_WITHIN_API(); - } - else - { - /* The scheduler is not running, but the task that was pointed - to by pxCurrentTCB has just been suspended and pxCurrentTCB - must be adjusted to point to a different task. */ - if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ - { - /* No other tasks are ready, so set pxCurrentTCB back to - NULL so when the next task is created pxCurrentTCB will - be set to point to it no matter what its relative priority - is. */ - pxCurrentTCB = NULL; - } - else - { - vTaskSwitchContext(); - } - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskSuspend( TaskHandle_t xTaskToSuspend ) + { + TCB_t * pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the running task that is + * being suspended. */ + pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the + * suspended list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + for( x = 0; x < configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + * now suspended, so no notification was received. */ + pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; + } + } + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* Reset the next expected unblock time in case it referred to the + * task that is now in the Suspended state. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( pxTCB == pxCurrentTCB ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + * to by pxCurrentTCB has just been suspended and pxCurrentTCB + * must be adjusted to point to a different task. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ + { + /* No other tasks are ready, so set pxCurrentTCB back to + * NULL so when the next task is created pxCurrentTCB will + * be set to point to it no matter what its relative priority + * is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* INCLUDE_vTaskSuspend */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskSuspend == 1 ) - static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) - { - BaseType_t xReturn = pdFALSE; - const TCB_t * const pxTCB = xTask; - - /* Accesses xPendingReadyList so must be called from a critical - section. */ - - /* It does not make sense to check if the calling task is suspended. */ - configASSERT( xTask ); - - /* Is the task being resumed actually in the suspended list? */ - if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) - { - /* Has the task already been resumed from within an ISR? */ - if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) - { - /* Is it in the suspended list because it is in the Suspended - state, or because is is blocked with no timeout? */ - if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ - { - xReturn = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return xReturn; - } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) + { + BaseType_t xReturn = pdFALSE; + const TCB_t * const pxTCB = xTask; + + /* Accesses xPendingReadyList so must be called from a critical + * section. */ + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task being resumed actually in the suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + { + /* Is it in the suspended list because it is in the Suspended + * state, or because is is blocked with no timeout? */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ + { + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ #endif /* INCLUDE_vTaskSuspend */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskSuspend == 1 ) - void vTaskResume( TaskHandle_t xTaskToResume ) - { - TCB_t * const pxTCB = xTaskToResume; - - /* It does not make sense to resume the calling task. */ - configASSERT( xTaskToResume ); - - /* The parameter cannot be NULL as it is impossible to resume the - currently executing task. */ - if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) - { - taskENTER_CRITICAL(); - { - if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) - { - traceTASK_RESUME( pxTCB ); - - /* The ready list can be accessed even if the scheduler is - suspended because this is inside a critical section. */ - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - - /* A higher priority task may have just been resumed. */ - if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) - { - /* This yield may not cause the task just resumed to run, - but will leave the lists in the correct state for the - next yield. */ - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskResume( TaskHandle_t xTaskToResume ) + { + TCB_t * const pxTCB = xTaskToResume; + + /* It does not make sense to resume the calling task. */ + configASSERT( xTaskToResume ); + + /* The parameter cannot be NULL as it is impossible to resume the + * currently executing task. */ + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) + { + taskENTER_CRITICAL(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME( pxTCB ); + + /* The ready list can be accessed even if the scheduler is + * suspended because this is inside a critical section. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* A higher priority task may have just been resumed. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, + * but will leave the lists in the correct state for the + * next yield. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* INCLUDE_vTaskSuspend */ @@ -1909,1483 +1902,1494 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) #if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) - BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) - { - BaseType_t xYieldRequired = pdFALSE; - TCB_t * const pxTCB = xTaskToResume; - UBaseType_t uxSavedInterruptStatus; - - configASSERT( xTaskToResume ); - - /* RTOS ports that support interrupt nesting have the concept of a - maximum system call (or maximum API call) interrupt priority. - Interrupts that are above the maximum system call priority are keep - permanently enabled, even when the RTOS kernel is in a critical section, - but cannot make any calls to FreeRTOS API functions. If configASSERT() - is defined in FreeRTOSConfig.h then - portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has - been assigned a priority above the configured maximum system call - priority. Only FreeRTOS functions that end in FromISR can be called - from interrupts that have been assigned a priority at or (logically) - below the maximum system call interrupt priority. FreeRTOS maintains a - separate interrupt safe API to ensure interrupt entry is as fast and as - simple as possible. More information (albeit Cortex-M specific) is - provided on the following link: - https://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) - { - traceTASK_RESUME_FROM_ISR( pxTCB ); - - /* Check the ready lists can be accessed. */ - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - /* Ready lists can be accessed so move the task from the - suspended list to the ready list directly. */ - if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) - { - xYieldRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - } - else - { - /* The delayed or ready lists cannot be accessed so the task - is held in the pending ready list until the scheduler is - unsuspended. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - - return xYieldRequired; - } + BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) + { + BaseType_t xYieldRequired = pdFALSE; + TCB_t * const pxTCB = xTaskToResume; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToResume ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + /* Check the ready lists can be accessed. */ + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Ready lists can be accessed so move the task from the + * suspended list to the ready list directly. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed or ready lists cannot be accessed so the task + * is held in the pending ready list until the scheduler is + * unsuspended. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xYieldRequired; + } #endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */ /*-----------------------------------------------------------*/ void vTaskStartScheduler( void ) { -BaseType_t xReturn; - - /* Add the idle task at the lowest priority. */ - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - StaticTask_t *pxIdleTaskTCBBuffer = NULL; - StackType_t *pxIdleTaskStackBuffer = NULL; - uint32_t ulIdleTaskStackSize; - - /* The Idle task is created using user provided RAM - obtain the - address of the RAM then create the idle task. */ - vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); - xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, - configIDLE_TASK_NAME, - ulIdleTaskStackSize, - ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ - portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ - pxIdleTaskStackBuffer, - pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ - - if( xIdleTaskHandle != NULL ) - { - xReturn = pdPASS; - } - else - { - xReturn = pdFAIL; - } - } - #else - { - /* The Idle task is being created using dynamically allocated RAM. */ - xReturn = xTaskCreate( prvIdleTask, - configIDLE_TASK_NAME, - configMINIMAL_STACK_SIZE, - ( void * ) NULL, - portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ - &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ - } - #endif /* configSUPPORT_STATIC_ALLOCATION */ - - #if ( configUSE_TIMERS == 1 ) - { - if( xReturn == pdPASS ) - { - xReturn = xTimerCreateTimerTask(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_TIMERS */ - - if( xReturn == pdPASS ) - { - /* freertos_tasks_c_additions_init() should only be called if the user - definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is - the only macro called by the function. */ - #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT - { - freertos_tasks_c_additions_init(); - } - #endif - - /* Interrupts are turned off here, to ensure a tick does not occur - before or during the call to xPortStartScheduler(). The stacks of - the created tasks contain a status word with interrupts switched on - so interrupts will automatically get re-enabled when the first task - starts to run. */ - portDISABLE_INTERRUPTS(); - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to the task that will run first. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ - _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); - } - #endif /* configUSE_NEWLIB_REENTRANT */ - - xNextTaskUnblockTime = portMAX_DELAY; - xSchedulerRunning = pdTRUE; - xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; - - /* If configGENERATE_RUN_TIME_STATS is defined then the following - macro must be defined to configure the timer/counter used to generate - the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS - is set to 0 and the following line fails to build then ensure you do not - have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your - FreeRTOSConfig.h file. */ - portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); - - traceTASK_SWITCHED_IN(); - - /* Setting up the timer tick is hardware specific and thus in the - portable interface. */ - if( xPortStartScheduler() != pdFALSE ) - { - /* Should not reach here as if the scheduler is running the - function will not return. */ - } - else - { - /* Should only reach here if a task calls xTaskEndScheduler(). */ - } - } - else - { - /* This line will only be reached if the kernel could not be started, - because there was not enough FreeRTOS heap to create the idle task - or the timer task. */ - configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); - } - - /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, - meaning xIdleTaskHandle is not used anywhere else. */ - ( void ) xIdleTaskHandle; - - /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority - * from getting optimized out as it is no longer used by the kernel. */ - ( void ) uxTopUsedPriority; + BaseType_t xReturn; + + /* Add the idle task at the lowest priority. */ + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t * pxIdleTaskTCBBuffer = NULL; + StackType_t * pxIdleTaskStackBuffer = NULL; + uint32_t ulIdleTaskStackSize; + + /* The Idle task is created using user provided RAM - obtain the + * address of the RAM then create the idle task. */ + vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); + xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, + configIDLE_TASK_NAME, + ulIdleTaskStackSize, + ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + pxIdleTaskStackBuffer, + pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + + if( xIdleTaskHandle != NULL ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + /* The Idle task is being created using dynamically allocated RAM. */ + xReturn = xTaskCreate( prvIdleTask, + configIDLE_TASK_NAME, + configMINIMAL_STACK_SIZE, + ( void * ) NULL, + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + + if( xReturn == pdPASS ) + { + /* freertos_tasks_c_additions_init() should only be called if the user + * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + * the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + + /* Interrupts are turned off here, to ensure a tick does not occur + * before or during the call to xPortStartScheduler(). The stacks of + * the created tasks contain a status word with interrupts switched on + * so interrupts will automatically get re-enabled when the first task + * starts to run. */ + portDISABLE_INTERRUPTS(); + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + * structure specific to the task that will run first. + * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * for additional information. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + + xNextTaskUnblockTime = portMAX_DELAY; + xSchedulerRunning = pdTRUE; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + * macro must be defined to configure the timer/counter used to generate + * the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + * is set to 0 and the following line fails to build then ensure you do not + * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + * FreeRTOSConfig.h file. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + traceTASK_SWITCHED_IN(); + + /* Setting up the timer tick is hardware specific and thus in the + * portable interface. */ + if( xPortStartScheduler() != pdFALSE ) + { + /* Should not reach here as if the scheduler is running the + * function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } + else + { + /* This line will only be reached if the kernel could not be started, + * because there was not enough FreeRTOS heap to create the idle task + * or the timer task. */ + configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); + } + + /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, + * meaning xIdleTaskHandle is not used anywhere else. */ + ( void ) xIdleTaskHandle; } /*-----------------------------------------------------------*/ void vTaskEndScheduler( void ) { - /* Stop the scheduler interrupts and call the portable scheduler end - routine so the original ISRs can be restored if necessary. The port - layer must ensure interrupts enable bit is left in the correct state. */ - portDISABLE_INTERRUPTS(); - xSchedulerRunning = pdFALSE; - vPortEndScheduler(); + /* Stop the scheduler interrupts and call the portable scheduler end + * routine so the original ISRs can be restored if necessary. The port + * layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); } /*----------------------------------------------------------*/ void vTaskSuspendAll( void ) { - /* A critical section is not required as the variable is of type - BaseType_t. Please read Richard Barry's reply in the following link to a - post in the FreeRTOS support forum before reporting this as a bug! - - http://goo.gl/wu4acr */ - - /* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that - do not otherwise exhibit real time behaviour. */ - portSOFTWARE_BARRIER(); - - /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment - is used to allow calls to vTaskSuspendAll() to nest. */ - ++uxSchedulerSuspended; - - /* Enforces ordering for ports and optimised compilers that may otherwise place - the above increment elsewhere. */ - portMEMORY_BARRIER(); + /* A critical section is not required as the variable is of type + * BaseType_t. Please read Richard Barry's reply in the following link to a + * post in the FreeRTOS support forum before reporting this as a bug! - + * http://goo.gl/wu4acr */ + + /* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that + * do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + * is used to allow calls to vTaskSuspendAll() to nest. */ + ++uxSchedulerSuspended; + + /* Enforces ordering for ports and optimised compilers that may otherwise place + * the above increment elsewhere. */ + portMEMORY_BARRIER(); } /*----------------------------------------------------------*/ #if ( configUSE_TICKLESS_IDLE != 0 ) - static TickType_t prvGetExpectedIdleTime( void ) - { - TickType_t xReturn; - UBaseType_t uxHigherPriorityReadyTasks = pdFALSE; - - /* uxHigherPriorityReadyTasks takes care of the case where - configUSE_PREEMPTION is 0, so there may be tasks above the idle priority - task that are in the Ready state, even though the idle task is - running. */ - #if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) - { - if( uxTopReadyPriority > tskIDLE_PRIORITY ) - { - uxHigherPriorityReadyTasks = pdTRUE; - } - } - #else - { - const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; - - /* When port optimised task selection is used the uxTopReadyPriority - variable is used as a bit map. If bits other than the least - significant bit are set then there are tasks that have a priority - above the idle priority that are in the Ready state. This takes - care of the case where the co-operative scheduler is in use. */ - if( uxTopReadyPriority > uxLeastSignificantBit ) - { - uxHigherPriorityReadyTasks = pdTRUE; - } - } - #endif - - if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) - { - xReturn = 0; - } - else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) - { - /* There are other idle priority tasks in the ready state. If - time slicing is used then the very next tick interrupt must be - processed. */ - xReturn = 0; - } - else if( uxHigherPriorityReadyTasks != pdFALSE ) - { - /* There are tasks in the Ready state that have a priority above the - idle priority. This path can only be reached if - configUSE_PREEMPTION is 0. */ - xReturn = 0; - } - else - { - xReturn = xNextTaskUnblockTime - xTickCount; - } - - return xReturn; - } + static TickType_t prvGetExpectedIdleTime( void ) + { + TickType_t xReturn; + UBaseType_t uxHigherPriorityReadyTasks = pdFALSE; + + /* uxHigherPriorityReadyTasks takes care of the case where + * configUSE_PREEMPTION is 0, so there may be tasks above the idle priority + * task that are in the Ready state, even though the idle task is + * running. */ + #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + { + if( uxTopReadyPriority > tskIDLE_PRIORITY ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #else + { + const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; + + /* When port optimised task selection is used the uxTopReadyPriority + * variable is used as a bit map. If bits other than the least + * significant bit are set then there are tasks that have a priority + * above the idle priority that are in the Ready state. This takes + * care of the case where the co-operative scheduler is in use. */ + if( uxTopReadyPriority > uxLeastSignificantBit ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #endif /* if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) */ + + if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) + { + xReturn = 0; + } + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) + { + /* There are other idle priority tasks in the ready state. If + * time slicing is used then the very next tick interrupt must be + * processed. */ + xReturn = 0; + } + else if( uxHigherPriorityReadyTasks != pdFALSE ) + { + /* There are tasks in the Ready state that have a priority above the + * idle priority. This path can only be reached if + * configUSE_PREEMPTION is 0. */ + xReturn = 0; + } + else + { + xReturn = xNextTaskUnblockTime - xTickCount; + } + + return xReturn; + } #endif /* configUSE_TICKLESS_IDLE */ /*----------------------------------------------------------*/ BaseType_t xTaskResumeAll( void ) { -TCB_t *pxTCB = NULL; -BaseType_t xAlreadyYielded = pdFALSE; - - /* If uxSchedulerSuspended is zero then this function does not match a - previous call to vTaskSuspendAll(). */ - configASSERT( uxSchedulerSuspended ); - - /* It is possible that an ISR caused a task to be removed from an event - list while the scheduler was suspended. If this was the case then the - removed task will have been added to the xPendingReadyList. Once the - scheduler has been resumed it is safe to move all the pending ready - tasks from this list into their appropriate ready list. */ - taskENTER_CRITICAL(); - { - --uxSchedulerSuspended; - - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) - { - /* Move any readied tasks from the pending list into the - appropriate ready list. */ - while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) - { - pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - - /* If the moved task has a priority higher than the current - task then a yield must be performed. */ - if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) - { - xYieldPending = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - if( pxTCB != NULL ) - { - /* A task was unblocked while the scheduler was suspended, - which may have prevented the next unblock time from being - re-calculated, in which case re-calculate it now. Mainly - important for low power tickless implementations, where - this can prevent an unnecessary exit from low power - state. */ - prvResetNextTaskUnblockTime(); - } - - /* If any ticks occurred while the scheduler was suspended then - they should be processed now. This ensures the tick count does - not slip, and that any delayed tasks are resumed at the correct - time. */ - { - TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ - - if( xPendedCounts > ( TickType_t ) 0U ) - { - do - { - if( xTaskIncrementTick() != pdFALSE ) - { - xYieldPending = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - --xPendedCounts; - } while( xPendedCounts > ( TickType_t ) 0U ); - - xPendedTicks = 0; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - if( xYieldPending != pdFALSE ) - { - #if( configUSE_PREEMPTION != 0 ) - { - xAlreadyYielded = pdTRUE; - } - #endif - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - return xAlreadyYielded; + TCB_t * pxTCB = NULL; + BaseType_t xAlreadyYielded = pdFALSE; + + /* If uxSchedulerSuspended is zero then this function does not match a + * previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended ); + + /* It is possible that an ISR caused a task to be removed from an event + * list while the scheduler was suspended. If this was the case then the + * removed task will have been added to the xPendingReadyList. Once the + * scheduler has been resumed it is safe to move all the pending ready + * tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) + { + /* Move any readied tasks from the pending list into the + * appropriate ready list. */ + while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* If the moved task has a priority higher than the current + * task then a yield must be performed. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( pxTCB != NULL ) + { + /* A task was unblocked while the scheduler was suspended, + * which may have prevented the next unblock time from being + * re-calculated, in which case re-calculate it now. Mainly + * important for low power tickless implementations, where + * this can prevent an unnecessary exit from low power + * state. */ + prvResetNextTaskUnblockTime(); + } + + /* If any ticks occurred while the scheduler was suspended then + * they should be processed now. This ensures the tick count does + * not slip, and that any delayed tasks are resumed at the correct + * time. */ + { + TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ + + if( xPendedCounts > ( TickType_t ) 0U ) + { + do + { + if( xTaskIncrementTick() != pdFALSE ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --xPendedCounts; + } while( xPendedCounts > ( TickType_t ) 0U ); + + xPendedTicks = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( xYieldPending != pdFALSE ) + { + #if ( configUSE_PREEMPTION != 0 ) + { + xAlreadyYielded = pdTRUE; + } + #endif + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xAlreadyYielded; } /*-----------------------------------------------------------*/ TickType_t xTaskGetTickCount( void ) { -TickType_t xTicks; + TickType_t xTicks; - /* Critical section required if running on a 16 bit processor. */ - portTICK_TYPE_ENTER_CRITICAL(); - { - xTicks = xTickCount; - } - portTICK_TYPE_EXIT_CRITICAL(); + /* Critical section required if running on a 16 bit processor. */ + portTICK_TYPE_ENTER_CRITICAL(); + { + xTicks = xTickCount; + } + portTICK_TYPE_EXIT_CRITICAL(); - return xTicks; + return xTicks; } /*-----------------------------------------------------------*/ TickType_t xTaskGetTickCountFromISR( void ) { -TickType_t xReturn; -UBaseType_t uxSavedInterruptStatus; - - /* RTOS ports that support interrupt nesting have the concept of a maximum - system call (or maximum API call) interrupt priority. Interrupts that are - above the maximum system call priority are kept permanently enabled, even - when the RTOS kernel is in a critical section, but cannot make any calls to - FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h - then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has been - assigned a priority above the configured maximum system call priority. - Only FreeRTOS functions that end in FromISR can be called from interrupts - that have been assigned a priority at or (logically) below the maximum - system call interrupt priority. FreeRTOS maintains a separate interrupt - safe API to ensure interrupt entry is as fast and as simple as possible. - More information (albeit Cortex-M specific) is provided on the following - link: https://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); - { - xReturn = xTickCount; - } - portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - - return xReturn; + TickType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = xTickCount; + } + portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; } /*-----------------------------------------------------------*/ UBaseType_t uxTaskGetNumberOfTasks( void ) { - /* A critical section is not required because the variables are of type - BaseType_t. */ - return uxCurrentNumberOfTasks; + /* A critical section is not required because the variables are of type + * BaseType_t. */ + return uxCurrentNumberOfTasks; } /*-----------------------------------------------------------*/ -char *pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { -TCB_t *pxTCB; + TCB_t * pxTCB; - /* If null is passed in here then the name of the calling task is being - queried. */ - pxTCB = prvGetTCBFromHandle( xTaskToQuery ); - configASSERT( pxTCB ); - return &( pxTCB->pcTaskName[ 0 ] ); + /* If null is passed in here then the name of the calling task is being + * queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB ); + return &( pxTCB->pcTaskName[ 0 ] ); } /*-----------------------------------------------------------*/ #if ( INCLUDE_xTaskGetHandle == 1 ) - static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) - { - TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; - UBaseType_t x; - char cNextChar; - BaseType_t xBreakLoop; - - /* This function is called with the scheduler suspended. */ - - if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) - { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - - do - { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - - /* Check each character in the name looking for a match or - mismatch. */ - xBreakLoop = pdFALSE; - for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) - { - cNextChar = pxNextTCB->pcTaskName[ x ]; - - if( cNextChar != pcNameToQuery[ x ] ) - { - /* Characters didn't match. */ - xBreakLoop = pdTRUE; - } - else if( cNextChar == ( char ) 0x00 ) - { - /* Both strings terminated, a match must have been - found. */ - pxReturn = pxNextTCB; - xBreakLoop = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - if( xBreakLoop != pdFALSE ) - { - break; - } - } - - if( pxReturn != NULL ) - { - /* The handle has been found. */ - break; - } - - } while( pxNextTCB != pxFirstTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return pxReturn; - } + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) + { + TCB_t * pxNextTCB, * pxFirstTCB, * pxReturn = NULL; + UBaseType_t x; + char cNextChar; + BaseType_t xBreakLoop; + + /* This function is called with the scheduler suspended. */ + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Check each character in the name looking for a match or + * mismatch. */ + xBreakLoop = pdFALSE; + + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + cNextChar = pxNextTCB->pcTaskName[ x ]; + + if( cNextChar != pcNameToQuery[ x ] ) + { + /* Characters didn't match. */ + xBreakLoop = pdTRUE; + } + else if( cNextChar == ( char ) 0x00 ) + { + /* Both strings terminated, a match must have been + * found. */ + pxReturn = pxNextTCB; + xBreakLoop = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xBreakLoop != pdFALSE ) + { + break; + } + } + + if( pxReturn != NULL ) + { + /* The handle has been found. */ + break; + } + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return pxReturn; + } #endif /* INCLUDE_xTaskGetHandle */ /*-----------------------------------------------------------*/ #if ( INCLUDE_xTaskGetHandle == 1 ) - TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - { - UBaseType_t uxQueue = configMAX_PRIORITIES; - TCB_t* pxTCB; - - /* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */ - configASSERT( strlen( pcNameToQuery ) < configMAX_TASK_NAME_LEN ); - - vTaskSuspendAll(); - { - /* Search the ready lists. */ - do - { - uxQueue--; - pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), pcNameToQuery ); - - if( pxTCB != NULL ) - { - /* Found the handle. */ - break; - } - - } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - /* Search the delayed lists. */ - if( pxTCB == NULL ) - { - pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxDelayedTaskList, pcNameToQuery ); - } - - if( pxTCB == NULL ) - { - pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, pcNameToQuery ); - } - - #if ( INCLUDE_vTaskSuspend == 1 ) - { - if( pxTCB == NULL ) - { - /* Search the suspended list. */ - pxTCB = prvSearchForNameWithinSingleList( &xSuspendedTaskList, pcNameToQuery ); - } - } - #endif - - #if( INCLUDE_vTaskDelete == 1 ) - { - if( pxTCB == NULL ) - { - /* Search the deleted list. */ - pxTCB = prvSearchForNameWithinSingleList( &xTasksWaitingTermination, pcNameToQuery ); - } - } - #endif - } - ( void ) xTaskResumeAll(); - - return pxTCB; - } + TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t uxQueue = configMAX_PRIORITIES; + TCB_t * pxTCB; + + /* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */ + configASSERT( strlen( pcNameToQuery ) < configMAX_TASK_NAME_LEN ); + + vTaskSuspendAll(); + { + /* Search the ready lists. */ + do + { + uxQueue--; + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), pcNameToQuery ); + + if( pxTCB != NULL ) + { + /* Found the handle. */ + break; + } + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Search the delayed lists. */ + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxDelayedTaskList, pcNameToQuery ); + } + + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, pcNameToQuery ); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the suspended list. */ + pxTCB = prvSearchForNameWithinSingleList( &xSuspendedTaskList, pcNameToQuery ); + } + } + #endif + + #if ( INCLUDE_vTaskDelete == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the deleted list. */ + pxTCB = prvSearchForNameWithinSingleList( &xTasksWaitingTermination, pcNameToQuery ); + } + } + #endif + } + ( void ) xTaskResumeAll(); + + return pxTCB; + } #endif /* INCLUDE_xTaskGetHandle */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) - { - UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; - - vTaskSuspendAll(); - { - /* Is there a space in the array for each task in the system? */ - if( uxArraySize >= uxCurrentNumberOfTasks ) - { - /* Fill in an TaskStatus_t structure with information on each - task in the Ready state. */ - do - { - uxQueue--; - uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ); - - } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - - /* Fill in an TaskStatus_t structure with information on each - task in the Blocked state. */ - uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ); - uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ); - - #if( INCLUDE_vTaskDelete == 1 ) - { - /* Fill in an TaskStatus_t structure with information on - each task that has been deleted but not yet cleaned up. */ - uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ); - } - #endif - - #if ( INCLUDE_vTaskSuspend == 1 ) - { - /* Fill in an TaskStatus_t structure with information on - each task in the Suspended state. */ - uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ); - } - #endif - - #if ( configGENERATE_RUN_TIME_STATS == 1) - { - if( pulTotalRunTime != NULL ) - { - #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE - portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); - #else - *pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); - #endif - } - } - #else - { - if( pulTotalRunTime != NULL ) - { - *pulTotalRunTime = 0; - } - } - #endif - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - ( void ) xTaskResumeAll(); - - return uxTask; - } + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + uint32_t * const pulTotalRunTime ) + { + UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; + + vTaskSuspendAll(); + { + /* Is there a space in the array for each task in the system? */ + if( uxArraySize >= uxCurrentNumberOfTasks ) + { + /* Fill in an TaskStatus_t structure with information on each + * task in the Ready state. */ + do + { + uxQueue--; + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ); + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + * task in the Blocked state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ); + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task that has been deleted but not yet cleaned up. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task in the Suspended state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ); + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + if( pulTotalRunTime != NULL ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); + #else + *pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + } + } + #else /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + { + if( pulTotalRunTime != NULL ) + { + *pulTotalRunTime = 0; + } + } + #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + + return uxTask; + } #endif /* configUSE_TRACE_FACILITY */ /*----------------------------------------------------------*/ #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) - TaskHandle_t xTaskGetIdleTaskHandle( void ) - { - /* If xTaskGetIdleTaskHandle() is called before the scheduler has been - started, then xIdleTaskHandle will be NULL. */ - configASSERT( ( xIdleTaskHandle != NULL ) ); - return xIdleTaskHandle; - } + TaskHandle_t xTaskGetIdleTaskHandle( void ) + { + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + * started, then xIdleTaskHandle will be NULL. */ + configASSERT( ( xIdleTaskHandle != NULL ) ); + return xIdleTaskHandle; + } #endif /* INCLUDE_xTaskGetIdleTaskHandle */ /*----------------------------------------------------------*/ /* This conditional compilation should use inequality to 0, not equality to 1. -This is to ensure vTaskStepTick() is available when user defined low power mode -implementations require configUSE_TICKLESS_IDLE to be set to a value other than -1. */ + * This is to ensure vTaskStepTick() is available when user defined low power mode + * implementations require configUSE_TICKLESS_IDLE to be set to a value other than + * 1. */ #if ( configUSE_TICKLESS_IDLE != 0 ) - void vTaskStepTick( const TickType_t xTicksToJump ) - { - /* Correct the tick count value after a period during which the tick - was suppressed. Note this does *not* call the tick hook function for - each stepped tick. */ - configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); - xTickCount += xTicksToJump; - traceINCREASE_TICK_COUNT( xTicksToJump ); - } + void vTaskStepTick( const TickType_t xTicksToJump ) + { + /* Correct the tick count value after a period during which the tick + * was suppressed. Note this does *not* call the tick hook function for + * each stepped tick. */ + configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); + xTickCount += xTicksToJump; + traceINCREASE_TICK_COUNT( xTicksToJump ); + } #endif /* configUSE_TICKLESS_IDLE */ /*----------------------------------------------------------*/ BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) { -BaseType_t xYieldRequired = pdFALSE; + BaseType_t xYieldOccurred; - /* Must not be called with the scheduler suspended as the implementation - relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ - configASSERT( uxSchedulerSuspended == 0 ); + /* Must not be called with the scheduler suspended as the implementation + * relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ + configASSERT( uxSchedulerSuspended == 0 ); - /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when - the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ - vTaskSuspendAll(); - xPendedTicks += xTicksToCatchUp; - xYieldRequired = xTaskResumeAll(); + /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when + * the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ + vTaskSuspendAll(); + xPendedTicks += xTicksToCatchUp; + xYieldOccurred = xTaskResumeAll(); - return xYieldRequired; + return xYieldOccurred; } /*----------------------------------------------------------*/ #if ( INCLUDE_xTaskAbortDelay == 1 ) - BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) - { - TCB_t *pxTCB = xTask; - BaseType_t xReturn; - - configASSERT( pxTCB ); - - vTaskSuspendAll(); - { - /* A task can only be prematurely removed from the Blocked state if - it is actually in the Blocked state. */ - if( eTaskGetState( xTask ) == eBlocked ) - { - xReturn = pdPASS; - - /* Remove the reference to the task from the blocked list. An - interrupt won't touch the xStateListItem because the - scheduler is suspended. */ - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - - /* Is the task waiting on an event also? If so remove it from - the event list too. Interrupts can touch the event list item, - even though the scheduler is suspended, so a critical section - is used. */ - taskENTER_CRITICAL(); - { - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - - /* This lets the task know it was forcibly removed from the - blocked state so it should not re-evaluate its block time and - then block again. */ - pxTCB->ucDelayAborted = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - /* Place the unblocked task into the appropriate ready list. */ - prvAddTaskToReadyList( pxTCB ); - - /* A task being unblocked cannot cause an immediate context - switch if preemption is turned off. */ - #if ( configUSE_PREEMPTION == 1 ) - { - /* Preemption is on, but a context switch should only be - performed if the unblocked task has a priority that is - equal to or higher than the currently executing task. */ - if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* Pend the yield to be performed when the scheduler - is unsuspended. */ - xYieldPending = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_PREEMPTION */ - } - else - { - xReturn = pdFAIL; - } - } - ( void ) xTaskResumeAll(); - - return xReturn; - } + BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) + { + TCB_t * pxTCB = xTask; + BaseType_t xReturn; + + configASSERT( pxTCB ); + + vTaskSuspendAll(); + { + /* A task can only be prematurely removed from the Blocked state if + * it is actually in the Blocked state. */ + if( eTaskGetState( xTask ) == eBlocked ) + { + xReturn = pdPASS; + + /* Remove the reference to the task from the blocked list. An + * interrupt won't touch the xStateListItem because the + * scheduler is suspended. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove it from + * the event list too. Interrupts can touch the event list item, + * even though the scheduler is suspended, so a critical section + * is used. */ + taskENTER_CRITICAL(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + + /* This lets the task know it was forcibly removed from the + * blocked state so it should not re-evaluate its block time and + * then block again. */ + pxTCB->ucDelayAborted = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* Place the unblocked task into the appropriate ready list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate context + * switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should only be + * performed if the unblocked task has a priority that is + * equal to or higher than the currently executing task. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Pend the yield to be performed when the scheduler + * is unsuspended. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + xReturn = pdFAIL; + } + } + ( void ) xTaskResumeAll(); + + return xReturn; + } #endif /* INCLUDE_xTaskAbortDelay */ /*----------------------------------------------------------*/ BaseType_t xTaskIncrementTick( void ) { -TCB_t * pxTCB; -TickType_t xItemValue; -BaseType_t xSwitchRequired = pdFALSE; - - /* Called by the portable layer each time a tick interrupt occurs. - Increments the tick then checks to see if the new tick value will cause any - tasks to be unblocked. */ - traceTASK_INCREMENT_TICK( xTickCount ); - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - /* Minor optimisation. The tick count cannot change in this - block. */ - const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; - - /* Increment the RTOS tick, switching the delayed and overflowed - delayed lists if it wraps to 0. */ - xTickCount = xConstTickCount; - - if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ - { - taskSWITCH_DELAYED_LISTS(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* See if this tick has made a timeout expire. Tasks are stored in - the queue in the order of their wake time - meaning once one task - has been found whose block time has not expired there is no need to - look any further down the list. */ - if( xConstTickCount >= xNextTaskUnblockTime ) - { - for( ;; ) - { - if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) - { - /* The delayed list is empty. Set xNextTaskUnblockTime - to the maximum possible value so it is extremely - unlikely that the - if( xTickCount >= xNextTaskUnblockTime ) test will pass - next time through. */ - xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - break; - } - else - { - /* The delayed list is not empty, get the value of the - item at the head of the delayed list. This is the time - at which the task at the head of the delayed list must - be removed from the Blocked state. */ - pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); - - if( xConstTickCount < xItemValue ) - { - /* It is not time to unblock this item yet, but the - item value is the time at which the task at the head - of the blocked list must be removed from the Blocked - state - so record the item value in - xNextTaskUnblockTime. */ - xNextTaskUnblockTime = xItemValue; - break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* It is time to remove the item from the Blocked state. */ - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - - /* Is the task waiting on an event also? If so remove - it from the event list. */ - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Place the unblocked task into the appropriate ready - list. */ - prvAddTaskToReadyList( pxTCB ); - - /* A task being unblocked cannot cause an immediate - context switch if preemption is turned off. */ - #if ( configUSE_PREEMPTION == 1 ) - { - /* Preemption is on, but a context switch should - only be performed if the unblocked task has a - priority that is equal to or higher than the - currently executing task. */ - if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) - { - xSwitchRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_PREEMPTION */ - } - } - } - - /* Tasks of equal priority to the currently running task will share - processing time (time slice) if preemption is on, and the application - writer has not explicitly turned time slicing off. */ - #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) - { - if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) - { - xSwitchRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ - - #if ( configUSE_TICK_HOOK == 1 ) - { - /* Guard against the tick hook being called when the pended tick - count is being unwound (when the scheduler is being unlocked). */ - if( xPendedTicks == ( TickType_t ) 0 ) - { - vApplicationTickHook(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_TICK_HOOK */ - - #if ( configUSE_PREEMPTION == 1 ) - { - if( xYieldPending != pdFALSE ) - { - xSwitchRequired = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_PREEMPTION */ - } - else - { - ++xPendedTicks; - - /* The tick hook gets called at regular intervals, even if the - scheduler is locked. */ - #if ( configUSE_TICK_HOOK == 1 ) - { - vApplicationTickHook(); - } - #endif - } - - return xSwitchRequired; + TCB_t * pxTCB; + TickType_t xItemValue; + BaseType_t xSwitchRequired = pdFALSE; + + /* Called by the portable layer each time a tick interrupt occurs. + * Increments the tick then checks to see if the new tick value will cause any + * tasks to be unblocked. */ + traceTASK_INCREMENT_TICK( xTickCount ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; + + /* Increment the RTOS tick, switching the delayed and overflowed + * delayed lists if it wraps to 0. */ + xTickCount = xConstTickCount; + + if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ + { + taskSWITCH_DELAYED_LISTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* See if this tick has made a timeout expire. Tasks are stored in + * the queue in the order of their wake time - meaning once one task + * has been found whose block time has not expired there is no need to + * look any further down the list. */ + if( xConstTickCount >= xNextTaskUnblockTime ) + { + for( ; ; ) + { + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The delayed list is empty. Set xNextTaskUnblockTime + * to the maximum possible value so it is extremely + * unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass + * next time through. */ + xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + break; + } + else + { + /* The delayed list is not empty, get the value of the + * item at the head of the delayed list. This is the time + * at which the task at the head of the delayed list must + * be removed from the Blocked state. */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); + + if( xConstTickCount < xItemValue ) + { + /* It is not time to unblock this item yet, but the + * item value is the time at which the task at the head + * of the blocked list must be removed from the Blocked + * state - so record the item value in + * xNextTaskUnblockTime. */ + xNextTaskUnblockTime = xItemValue; + break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* It is time to remove the item from the Blocked state. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove + * it from the event list. */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Place the unblocked task into the appropriate ready + * list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate + * context switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should + * only be performed if the unblocked task has a + * priority that is equal to or higher than the + * currently executing task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + } + } + + /* Tasks of equal priority to the currently running task will share + * processing time (time slice) if preemption is on, and the application + * writer has not explicitly turned time slicing off. */ + #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + * count is being unwound (when the scheduler is being unlocked). */ + if( xPendedTicks == ( TickType_t ) 0 ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + ++xPendedTicks; + + /* The tick hook gets called at regular intervals, even if the + * scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + + return xSwitchRequired; } /*-----------------------------------------------------------*/ #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) - { - TCB_t *xTCB; - - /* If xTask is NULL then it is the task hook of the calling task that is - getting set. */ - if( xTask == NULL ) - { - xTCB = ( TCB_t * ) pxCurrentTCB; - } - else - { - xTCB = xTask; - } - - /* Save the hook function in the TCB. A critical section is required as - the value can be accessed from an interrupt. */ - taskENTER_CRITICAL(); - { - xTCB->pxTaskTag = pxHookFunction; - } - taskEXIT_CRITICAL(); - } + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) + { + TCB_t * xTCB; + + /* If xTask is NULL then it is the task hook of the calling task that is + * getting set. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xTCB->pxTaskTag = pxHookFunction; + } + taskEXIT_CRITICAL(); + } #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) - { - TCB_t *pxTCB; - TaskHookFunction_t xReturn; + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; - /* If xTask is NULL then set the calling task's hook. */ - pxTCB = prvGetTCBFromHandle( xTask ); + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); - /* Save the hook function in the TCB. A critical section is required as - the value can be accessed from an interrupt. */ - taskENTER_CRITICAL(); - { - xReturn = pxTCB->pxTaskTag; - } - taskEXIT_CRITICAL(); + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xReturn = pxTCB->pxTaskTag; + } + taskEXIT_CRITICAL(); - return xReturn; - } + return xReturn; + } #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) - { - TCB_t *pxTCB; - TaskHookFunction_t xReturn; - UBaseType_t uxSavedInterruptStatus; + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; - /* If xTask is NULL then set the calling task's hook. */ - pxTCB = prvGetTCBFromHandle( xTask ); + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); - /* Save the hook function in the TCB. A critical section is required as - the value can be accessed from an interrupt. */ - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - xReturn = pxTCB->pxTaskTag; - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - return xReturn; - } + return xReturn; + } #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ #if ( configUSE_APPLICATION_TASK_TAG == 1 ) - BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) - { - TCB_t *xTCB; - BaseType_t xReturn; - - /* If xTask is NULL then we are calling our own task hook. */ - if( xTask == NULL ) - { - xTCB = pxCurrentTCB; - } - else - { - xTCB = xTask; - } - - if( xTCB->pxTaskTag != NULL ) - { - xReturn = xTCB->pxTaskTag( pvParameter ); - } - else - { - xReturn = pdFAIL; - } - - return xReturn; - } + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) + { + TCB_t * xTCB; + BaseType_t xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } #endif /* configUSE_APPLICATION_TASK_TAG */ /*-----------------------------------------------------------*/ void vTaskSwitchContext( void ) { - if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) - { - /* The scheduler is currently suspended - do not allow a context - switch. */ - xYieldPending = pdTRUE; - } - else - { - xYieldPending = pdFALSE; - traceTASK_SWITCHED_OUT(); - - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - { - #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE - portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); - #else - ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); - #endif - - /* Add the amount of time the task has been running to the - accumulated time so far. The time the task started running was - stored in ulTaskSwitchedInTime. Note that there is no overflow - protection here so count values are only valid until the timer - overflows. The guard against negative values is to protect - against suspect run time stat counter implementations - which - are provided by the application, not the kernel. */ - if( ulTotalRunTime > ulTaskSwitchedInTime ) - { - pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - ulTaskSwitchedInTime = ulTotalRunTime; - } - #endif /* configGENERATE_RUN_TIME_STATS */ - - /* Check for stack overflow, if configured. */ - taskCHECK_FOR_STACK_OVERFLOW(); - - /* Before the currently running task is switched out, save its errno. */ - #if( configUSE_POSIX_ERRNO == 1 ) - { - pxCurrentTCB->iTaskErrno = FreeRTOS_errno; - } - #endif - - /* Select a new task to run using either the generic C or port - optimised asm code. */ - taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - traceTASK_SWITCHED_IN(); - - /* After the new task is switched in, update the global errno. */ - #if( configUSE_POSIX_ERRNO == 1 ) - { - FreeRTOS_errno = pxCurrentTCB->iTaskErrno; - } - #endif - - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - /* Switch Newlib's _impure_ptr variable to point to the _reent - structure specific to this task. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ - _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); - } - #endif /* configUSE_NEWLIB_REENTRANT */ - } + if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + * switch. */ + xYieldPending = pdTRUE; + } + else + { + xYieldPending = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + * accumulated time so far. The time the task started running was + * stored in ulTaskSwitchedInTime. Note that there is no overflow + * protection here so count values are only valid until the timer + * overflows. The guard against negative values is to protect + * against suspect run time stat counter implementations - which + * are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ulTaskSwitchedInTime = ulTotalRunTime; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskCHECK_FOR_STACK_OVERFLOW(); + + /* Before the currently running task is switched out, save its errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + + /* Select a new task to run using either the generic C or port + * optimised asm code. */ + taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + traceTASK_SWITCHED_IN(); + + /* After the new task is switched in, update the global errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + * structure specific to this task. + * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * for additional information. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + } } /*-----------------------------------------------------------*/ -void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) { - configASSERT( pxEventList ); + configASSERT( pxEventList ); - /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE - SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ + /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE + * SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ - /* Place the event list item of the TCB in the appropriate event list. - This is placed in the list in priority order so the highest priority task - is the first to be woken by the event. The queue that contains the event - list is locked, preventing simultaneous access from interrupts. */ - vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + /* Place the event list item of the TCB in the appropriate event list. + * This is placed in the list in priority order so the highest priority task + * is the first to be woken by the event. The queue that contains the event + * list is locked, preventing simultaneous access from interrupts. */ + vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); - prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); } /*-----------------------------------------------------------*/ -void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) { - configASSERT( pxEventList ); + configASSERT( pxEventList ); - /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by - the event groups implementation. */ - configASSERT( uxSchedulerSuspended != 0 ); + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event groups implementation. */ + configASSERT( uxSchedulerSuspended != 0 ); - /* Store the item value in the event list item. It is safe to access the - event list item here as interrupts won't access the event list item of a - task that is not in the Blocked state. */ - listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + /* Store the item value in the event list item. It is safe to access the + * event list item here as interrupts won't access the event list item of a + * task that is not in the Blocked state. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); - /* Place the event list item of the TCB at the end of the appropriate event - list. It is safe to access the event list here because it is part of an - event group implementation - and interrupts don't access event groups - directly (instead they access them indirectly by pending function calls to - the task level). */ - vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + /* Place the event list item of the TCB at the end of the appropriate event + * list. It is safe to access the event list here because it is part of an + * event group implementation - and interrupts don't access event groups + * directly (instead they access them indirectly by pending function calls to + * the task level). */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); - prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); } /*-----------------------------------------------------------*/ -#if( configUSE_TIMERS == 1 ) +#if ( configUSE_TIMERS == 1 ) - void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) - { - configASSERT( pxEventList ); + void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + { + configASSERT( pxEventList ); - /* This function should not be called by application code hence the - 'Restricted' in its name. It is not part of the public API. It is - designed for use by kernel code, and has special calling requirements - - it should be called with the scheduler suspended. */ + /* This function should not be called by application code hence the + * 'Restricted' in its name. It is not part of the public API. It is + * designed for use by kernel code, and has special calling requirements - + * it should be called with the scheduler suspended. */ - /* Place the event list item of the TCB in the appropriate event list. - In this case it is assume that this is the only task that is going to - be waiting on this event list, so the faster vListInsertEnd() function - can be used in place of vListInsert. */ - vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + /* Place the event list item of the TCB in the appropriate event list. + * In this case it is assume that this is the only task that is going to + * be waiting on this event list, so the faster vListInsertEnd() function + * can be used in place of vListInsert. */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); - /* If the task should block indefinitely then set the block time to a - value that will be recognised as an indefinite delay inside the - prvAddCurrentTaskToDelayedList() function. */ - if( xWaitIndefinitely != pdFALSE ) - { - xTicksToWait = portMAX_DELAY; - } + /* If the task should block indefinitely then set the block time to a + * value that will be recognised as an indefinite delay inside the + * prvAddCurrentTaskToDelayedList() function. */ + if( xWaitIndefinitely != pdFALSE ) + { + xTicksToWait = portMAX_DELAY; + } - traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); - prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); - } + traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); + prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); + } #endif /* configUSE_TIMERS */ /*-----------------------------------------------------------*/ BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) { -TCB_t *pxUnblockedTCB; -BaseType_t xReturn; - - /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be - called from a critical section within an ISR. */ - - /* The event list is sorted in priority order, so the first in the list can - be removed as it is known to be the highest priority. Remove the TCB from - the delayed list, and add it to the ready list. - - If an event is for a queue that is locked then this function will never - get called - the lock count on the queue will get modified instead. This - means exclusive access to the event list is guaranteed here. - - This function assumes that a check has already been made to ensure that - pxEventList is not empty. */ - pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - configASSERT( pxUnblockedTCB ); - ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); - - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxUnblockedTCB ); - - #if( configUSE_TICKLESS_IDLE != 0 ) - { - /* If a task is blocked on a kernel object then xNextTaskUnblockTime - might be set to the blocked task's time out time. If the task is - unblocked for a reason other than a timeout xNextTaskUnblockTime is - normally left unchanged, because it is automatically reset to a new - value when the tick count equals xNextTaskUnblockTime. However if - tickless idling is used it might be more important to enter sleep mode - at the earliest possible time - so reset xNextTaskUnblockTime here to - ensure it is updated at the earliest possible time. */ - prvResetNextTaskUnblockTime(); - } - #endif - } - else - { - /* The delayed and ready lists cannot be accessed, so hold this task - pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); - } - - if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* Return true if the task removed from the event list has a higher - priority than the calling task. This allows the calling task to know if - it should force a context switch now. */ - xReturn = pdTRUE; - - /* Mark that a yield is pending in case the user is not using the - "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } - else - { - xReturn = pdFALSE; - } - - return xReturn; + TCB_t * pxUnblockedTCB; + BaseType_t xReturn; + + /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be + * called from a critical section within an ISR. */ + + /* The event list is sorted in priority order, so the first in the list can + * be removed as it is known to be the highest priority. Remove the TCB from + * the delayed list, and add it to the ready list. + * + * If an event is for a queue that is locked then this function will never + * get called - the lock count on the queue will get modified instead. This + * means exclusive access to the event list is guaranteed here. + * + * This function assumes that a check has already been made to ensure that + * pxEventList is not empty. */ + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold this task + * pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has a higher + * priority than the calling task. This allows the calling task to know if + * it should force a context switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + * "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; } /*-----------------------------------------------------------*/ -void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) { -TCB_t *pxUnblockedTCB; - - /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by - the event flags implementation. */ - configASSERT( uxSchedulerSuspended != pdFALSE ); - - /* Store the new item value in the event list. */ - listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); - - /* Remove the event list form the event flag. Interrupts do not access - event flags. */ - pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - configASSERT( pxUnblockedTCB ); - ( void ) uxListRemove( pxEventListItem ); - - #if( configUSE_TICKLESS_IDLE != 0 ) - { - /* If a task is blocked on a kernel object then xNextTaskUnblockTime - might be set to the blocked task's time out time. If the task is - unblocked for a reason other than a timeout xNextTaskUnblockTime is - normally left unchanged, because it is automatically reset to a new - value when the tick count equals xNextTaskUnblockTime. However if - tickless idling is used it might be more important to enter sleep mode - at the earliest possible time - so reset xNextTaskUnblockTime here to - ensure it is updated at the earliest possible time. */ - prvResetNextTaskUnblockTime(); - } - #endif - - /* Remove the task from the delayed list and add it to the ready list. The - scheduler is suspended so interrupts will not be accessing the ready - lists. */ - ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxUnblockedTCB ); - - if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* The unblocked task has a priority above that of the calling task, so - a context switch is required. This function is called with the - scheduler suspended so xYieldPending is set so the context switch - occurs immediately that the scheduler is resumed (unsuspended). */ - xYieldPending = pdTRUE; - } + TCB_t * pxUnblockedTCB; + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event flags implementation. */ + configASSERT( uxSchedulerSuspended != pdFALSE ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + * event flags. */ + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( pxEventListItem ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + /* Remove the task from the delayed list and add it to the ready list. The + * scheduler is suspended so interrupts will not be accessing the ready + * lists. */ + ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The unblocked task has a priority above that of the calling task, so + * a context switch is required. This function is called with the + * scheduler suspended so xYieldPending is set so the context switch + * occurs immediately that the scheduler is resumed (unsuspended). */ + xYieldPending = pdTRUE; + } } /*-----------------------------------------------------------*/ void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) { - configASSERT( pxTimeOut ); - taskENTER_CRITICAL(); - { - pxTimeOut->xOverflowCount = xNumOfOverflows; - pxTimeOut->xTimeOnEntering = xTickCount; - } - taskEXIT_CRITICAL(); + configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); } /*-----------------------------------------------------------*/ void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) { - /* For internal use only as it does not use a critical section. */ - pxTimeOut->xOverflowCount = xNumOfOverflows; - pxTimeOut->xTimeOnEntering = xTickCount; + /* For internal use only as it does not use a critical section. */ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; } /*-----------------------------------------------------------*/ -BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) { -BaseType_t xReturn; - - configASSERT( pxTimeOut ); - configASSERT( pxTicksToWait ); - - taskENTER_CRITICAL(); - { - /* Minor optimisation. The tick count cannot change in this block. */ - const TickType_t xConstTickCount = xTickCount; - const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; - - #if( INCLUDE_xTaskAbortDelay == 1 ) - if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) - { - /* The delay was aborted, which is not the same as a time out, - but has the same result. */ - pxCurrentTCB->ucDelayAborted = pdFALSE; - xReturn = pdTRUE; - } - else - #endif - - #if ( INCLUDE_vTaskSuspend == 1 ) - if( *pxTicksToWait == portMAX_DELAY ) - { - /* If INCLUDE_vTaskSuspend is set to 1 and the block time - specified is the maximum block time then the task should block - indefinitely, and therefore never time out. */ - xReturn = pdFALSE; - } - else - #endif - - if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */ - { - /* The tick count is greater than the time at which - vTaskSetTimeout() was called, but has also overflowed since - vTaskSetTimeOut() was called. It must have wrapped all the way - around and gone past again. This passed since vTaskSetTimeout() - was called. */ - xReturn = pdTRUE; - } - else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ - { - /* Not a genuine timeout. Adjust parameters for time remaining. */ - *pxTicksToWait -= xElapsedTime; - vTaskInternalSetTimeOutState( pxTimeOut ); - xReturn = pdFALSE; - } - else - { - *pxTicksToWait = 0; - xReturn = pdTRUE; - } - } - taskEXIT_CRITICAL(); - - return xReturn; + BaseType_t xReturn; + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + /* Minor optimisation. The tick count cannot change in this block. */ + const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) + { + /* The delay was aborted, which is not the same as a time out, + * but has the same result. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + xReturn = pdTRUE; + } + else + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + if( *pxTicksToWait == portMAX_DELAY ) + { + /* If INCLUDE_vTaskSuspend is set to 1 and the block time + * specified is the maximum block time then the task should block + * indefinitely, and therefore never time out. */ + xReturn = pdFALSE; + } + else + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */ + { + /* The tick count is greater than the time at which + * vTaskSetTimeout() was called, but has also overflowed since + * vTaskSetTimeOut() was called. It must have wrapped all the way + * around and gone past again. This passed since vTaskSetTimeout() + * was called. */ + xReturn = pdTRUE; + *pxTicksToWait = ( TickType_t ) 0; + } + else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + *pxTicksToWait = ( TickType_t ) 0; + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; } /*-----------------------------------------------------------*/ void vTaskMissedYield( void ) { - xYieldPending = pdTRUE; + xYieldPending = pdTRUE; } /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) - { - UBaseType_t uxReturn; - TCB_t const *pxTCB; + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t const * pxTCB; - if( xTask != NULL ) - { - pxTCB = xTask; - uxReturn = pxTCB->uxTaskNumber; - } - else - { - uxReturn = 0U; - } + if( xTask != NULL ) + { + pxTCB = xTask; + uxReturn = pxTCB->uxTaskNumber; + } + else + { + uxReturn = 0U; + } - return uxReturn; - } + return uxReturn; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) - void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) - { - TCB_t * pxTCB; + void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) + { + TCB_t * pxTCB; - if( xTask != NULL ) - { - pxTCB = xTask; - pxTCB->uxTaskNumber = uxHandle; - } - } + if( xTask != NULL ) + { + pxTCB = xTask; + pxTCB->uxTaskNumber = uxHandle; + } + } #endif /* configUSE_TRACE_FACILITY */ @@ -3402,1920 +3406,1974 @@ void vTaskMissedYield( void ) */ static portTASK_FUNCTION( prvIdleTask, pvParameters ) { - /* Stop warnings. */ - ( void ) pvParameters; - - /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE - SCHEDULER IS STARTED. **/ - - /* In case a task that has a secure context deletes itself, in which case - the idle task is responsible for deleting the task's secure context, if - any. */ - portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); - - for( ;; ) - { - /* See if any tasks have deleted themselves - if so then the idle task - is responsible for freeing the deleted task's TCB and stack. */ - prvCheckTasksWaitingTermination(); - - #if ( configUSE_PREEMPTION == 0 ) - { - /* If we are not using preemption we keep forcing a task switch to - see if any other task has become available. If we are using - preemption we don't need to do this as any task becoming available - will automatically get the processor anyway. */ - taskYIELD(); - } - #endif /* configUSE_PREEMPTION */ - - #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) - { - /* When using preemption tasks of equal priority will be - timesliced. If a task that is sharing the idle priority is ready - to run then the idle task should yield before the end of the - timeslice. - - A critical region is not required here as we are just reading from - the list, and an occasional incorrect value will not matter. If - the ready list at the idle priority contains more than one task - then a task other than the idle task is ready to execute. */ - if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) - { - taskYIELD(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ - - #if ( configUSE_IDLE_HOOK == 1 ) - { - extern void vApplicationIdleHook( void ); - - /* Call the user defined function from within the idle task. This - allows the application designer to add background functionality - without the overhead of a separate task. - NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, - CALL A FUNCTION THAT MIGHT BLOCK. */ - vApplicationIdleHook(); - } - #endif /* configUSE_IDLE_HOOK */ - - /* This conditional compilation should use inequality to 0, not equality - to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when - user defined low power mode implementations require - configUSE_TICKLESS_IDLE to be set to a value other than 1. */ - #if ( configUSE_TICKLESS_IDLE != 0 ) - { - TickType_t xExpectedIdleTime; - - /* It is not desirable to suspend then resume the scheduler on - each iteration of the idle task. Therefore, a preliminary - test of the expected idle time is performed without the - scheduler suspended. The result here is not necessarily - valid. */ - xExpectedIdleTime = prvGetExpectedIdleTime(); - - if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) - { - vTaskSuspendAll(); - { - /* Now the scheduler is suspended, the expected idle - time can be sampled again, and this time its value can - be used. */ - configASSERT( xNextTaskUnblockTime >= xTickCount ); - xExpectedIdleTime = prvGetExpectedIdleTime(); - - /* Define the following macro to set xExpectedIdleTime to 0 - if the application does not want - portSUPPRESS_TICKS_AND_SLEEP() to be called. */ - configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); - - if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) - { - traceLOW_POWER_IDLE_BEGIN(); - portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); - traceLOW_POWER_IDLE_END(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - ( void ) xTaskResumeAll(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configUSE_TICKLESS_IDLE */ - } + /* Stop warnings. */ + ( void ) pvParameters; + + /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE + * SCHEDULER IS STARTED. **/ + + /* In case a task that has a secure context deletes itself, in which case + * the idle task is responsible for deleting the task's secure context, if + * any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + + for( ; ; ) + { + /* See if any tasks have deleted themselves - if so then the idle task + * is responsible for freeing the deleted task's TCB and stack. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + * see if any other task has become available. If we are using + * preemption we don't need to do this as any task becoming available + * will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + * timesliced. If a task that is sharing the idle priority is ready + * to run then the idle task should yield before the end of the + * timeslice. + * + * A critical region is not required here as we are just reading from + * the list, and an occasional incorrect value will not matter. If + * the ready list at the idle priority contains more than one task + * then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + * allows the application designer to add background functionality + * without the overhead of a separate task. + * NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + * CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif /* configUSE_IDLE_HOOK */ + + /* This conditional compilation should use inequality to 0, not equality + * to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when + * user defined low power mode implementations require + * configUSE_TICKLESS_IDLE to be set to a value other than 1. */ + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + TickType_t xExpectedIdleTime; + + /* It is not desirable to suspend then resume the scheduler on + * each iteration of the idle task. Therefore, a preliminary + * test of the expected idle time is performed without the + * scheduler suspended. The result here is not necessarily + * valid. */ + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + vTaskSuspendAll(); + { + /* Now the scheduler is suspended, the expected idle + * time can be sampled again, and this time its value can + * be used. */ + configASSERT( xNextTaskUnblockTime >= xTickCount ); + xExpectedIdleTime = prvGetExpectedIdleTime(); + + /* Define the following macro to set xExpectedIdleTime to 0 + * if the application does not want + * portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + traceLOW_POWER_IDLE_BEGIN(); + portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + traceLOW_POWER_IDLE_END(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICKLESS_IDLE */ + } } /*-----------------------------------------------------------*/ -#if( configUSE_TICKLESS_IDLE != 0 ) - - eSleepModeStatus eTaskConfirmSleepModeStatus( void ) - { - /* The idle task exists in addition to the application tasks. */ - const UBaseType_t uxNonApplicationTasks = 1; - eSleepModeStatus eReturn = eStandardSleep; - - /* This function must be called from a critical section. */ - - if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) - { - /* A task was made ready while the scheduler was suspended. */ - eReturn = eAbortSleep; - } - else if( xYieldPending != pdFALSE ) - { - /* A yield was pended while the scheduler was suspended. */ - eReturn = eAbortSleep; - } - else - { - /* If all the tasks are in the suspended list (which might mean they - have an infinite block time rather than actually being suspended) - then it is safe to turn all clocks off and just wait for external - interrupts. */ - if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) - { - eReturn = eNoTasksWaitingTimeout; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - return eReturn; - } +#if ( configUSE_TICKLESS_IDLE != 0 ) + + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) + { + /* The idle task exists in addition to the application tasks. */ + const UBaseType_t uxNonApplicationTasks = 1; + eSleepModeStatus eReturn = eStandardSleep; + + /* This function must be called from a critical section. */ + + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + { + /* A task was made ready while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xYieldPending != pdFALSE ) + { + /* A yield was pended while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xPendedTicks != 0 ) + { + /* A tick interrupt has already occurred but was held pending + * because the scheduler is suspended. */ + eReturn = eAbortSleep; + } + else + { + /* If all the tasks are in the suspended list (which might mean they + * have an infinite block time rather than actually being suspended) + * then it is safe to turn all clocks off and just wait for external + * interrupts. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) + { + eReturn = eNoTasksWaitingTimeout; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return eReturn; + } #endif /* configUSE_TICKLESS_IDLE */ /*-----------------------------------------------------------*/ #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) - void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) - { - TCB_t *pxTCB; + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) + { + TCB_t * pxTCB; - if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) - { - pxTCB = prvGetTCBFromHandle( xTaskToSet ); - configASSERT( pxTCB != NULL ); - pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; - } - } + if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) + { + pxTCB = prvGetTCBFromHandle( xTaskToSet ); + configASSERT( pxTCB != NULL ); + pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + } + } #endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ /*-----------------------------------------------------------*/ #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) - void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) - { - void *pvReturn = NULL; - TCB_t *pxTCB; - - if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) - { - pxTCB = prvGetTCBFromHandle( xTaskToQuery ); - pvReturn = pxTCB->pvThreadLocalStoragePointers[ xIndex ]; - } - else - { - pvReturn = NULL; - } - - return pvReturn; - } + void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) + { + void * pvReturn = NULL; + TCB_t * pxTCB; + + if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) + { + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + pvReturn = pxTCB->pvThreadLocalStoragePointers[ xIndex ]; + } + else + { + pvReturn = NULL; + } + + return pvReturn; + } #endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ /*-----------------------------------------------------------*/ #if ( portUSING_MPU_WRAPPERS == 1 ) - void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, const MemoryRegion_t * const xRegions ) - { - TCB_t *pxTCB; + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, + const MemoryRegion_t * const xRegions ) + { + TCB_t * pxTCB; - /* If null is passed in here then we are modifying the MPU settings of - the calling task. */ - pxTCB = prvGetTCBFromHandle( xTaskToModify ); + /* If null is passed in here then we are modifying the MPU settings of + * the calling task. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); - vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); - } + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } #endif /* portUSING_MPU_WRAPPERS */ /*-----------------------------------------------------------*/ static void prvInitialiseTaskLists( void ) { -UBaseType_t uxPriority; - - for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) - { - vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); - } - - vListInitialise( &xDelayedTaskList1 ); - vListInitialise( &xDelayedTaskList2 ); - vListInitialise( &xPendingReadyList ); - - #if ( INCLUDE_vTaskDelete == 1 ) - { - vListInitialise( &xTasksWaitingTermination ); - } - #endif /* INCLUDE_vTaskDelete */ - - #if ( INCLUDE_vTaskSuspend == 1 ) - { - vListInitialise( &xSuspendedTaskList ); - } - #endif /* INCLUDE_vTaskSuspend */ - - /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList - using list2. */ - pxDelayedTaskList = &xDelayedTaskList1; - pxOverflowDelayedTaskList = &xDelayedTaskList2; + UBaseType_t uxPriority; + + for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( &xDelayedTaskList1 ); + vListInitialise( &xDelayedTaskList2 ); + vListInitialise( &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( &xTasksWaitingTermination ); + } + #endif /* INCLUDE_vTaskDelete */ + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( &xSuspendedTaskList ); + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + * using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; } /*-----------------------------------------------------------*/ static void prvCheckTasksWaitingTermination( void ) { - - /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ - - #if ( INCLUDE_vTaskDelete == 1 ) - { - TCB_t *pxTCB; - - /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() - being called too often in the idle task. */ - while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) - { - taskENTER_CRITICAL(); - { - pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - --uxCurrentNumberOfTasks; - --uxDeletedTasksWaitingCleanUp; - } - taskEXIT_CRITICAL(); - - prvDeleteTCB( pxTCB ); - } - } - #endif /* INCLUDE_vTaskDelete */ + /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ + + #if ( INCLUDE_vTaskDelete == 1 ) + { + TCB_t * pxTCB; + + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + * being called too often in the idle task. */ + while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) + { + taskENTER_CRITICAL(); + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + #endif /* INCLUDE_vTaskDelete */ } /*-----------------------------------------------------------*/ -#if( configUSE_TRACE_FACILITY == 1 ) - - void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) - { - TCB_t *pxTCB; - - /* xTask is NULL then get the state of the calling task. */ - pxTCB = prvGetTCBFromHandle( xTask ); - - pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB; - pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName [ 0 ] ); - pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority; - pxTaskStatus->pxStackBase = pxTCB->pxStack; - pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; - - #if ( configUSE_MUTEXES == 1 ) - { - pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; - } - #else - { - pxTaskStatus->uxBasePriority = 0; - } - #endif - - #if ( configGENERATE_RUN_TIME_STATS == 1 ) - { - pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter; - } - #else - { - pxTaskStatus->ulRunTimeCounter = 0; - } - #endif - - /* Obtaining the task state is a little fiddly, so is only done if the - value of eState passed into this function is eInvalid - otherwise the - state is just set to whatever is passed in. */ - if( eState != eInvalid ) - { - if( pxTCB == pxCurrentTCB ) - { - pxTaskStatus->eCurrentState = eRunning; - } - else - { - pxTaskStatus->eCurrentState = eState; - - #if ( INCLUDE_vTaskSuspend == 1 ) - { - /* If the task is in the suspended list then there is a - chance it is actually just blocked indefinitely - so really - it should be reported as being in the Blocked state. */ - if( eState == eSuspended ) - { - vTaskSuspendAll(); - { - if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) - { - pxTaskStatus->eCurrentState = eBlocked; - } - } - ( void ) xTaskResumeAll(); - } - } - #endif /* INCLUDE_vTaskSuspend */ - } - } - else - { - pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); - } - - /* Obtaining the stack space takes some time, so the xGetFreeStackSpace - parameter is provided to allow it to be skipped. */ - if( xGetFreeStackSpace != pdFALSE ) - { - #if ( portSTACK_GROWTH > 0 ) - { - pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack ); - } - #else - { - pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ); - } - #endif - } - else - { - pxTaskStatus->usStackHighWaterMark = 0; - } - } +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) + { + TCB_t * pxTCB; + + /* xTask is NULL then get the state of the calling task. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB; + pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName[ 0 ] ); + pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority; + pxTaskStatus->pxStackBase = pxTCB->pxStack; + pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; + + #if ( configUSE_MUTEXES == 1 ) + { + pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; + } + #else + { + pxTaskStatus->uxBasePriority = 0; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter; + } + #else + { + pxTaskStatus->ulRunTimeCounter = 0; + } + #endif + + /* Obtaining the task state is a little fiddly, so is only done if the + * value of eState passed into this function is eInvalid - otherwise the + * state is just set to whatever is passed in. */ + if( eState != eInvalid ) + { + if( pxTCB == pxCurrentTCB ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + * chance it is actually just blocked indefinitely - so really + * it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + } + } + else + { + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); + } + + /* Obtaining the stack space takes some time, so the xGetFreeStackSpace + * parameter is provided to allow it to be skipped. */ + if( xGetFreeStackSpace != pdFALSE ) + { + #if ( portSTACK_GROWTH > 0 ) + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack ); + } + #else + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ); + } + #endif + } + else + { + pxTaskStatus->usStackHighWaterMark = 0; + } + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) - static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) - { - configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB; - UBaseType_t uxTask = 0; - - if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) - { - listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - - /* Populate an TaskStatus_t structure within the - pxTaskStatusArray array for each task that is referenced from - pxList. See the definition of TaskStatus_t in task.h for the - meaning of each TaskStatus_t structure member. */ - do - { - listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); - uxTask++; - } while( pxNextTCB != pxFirstTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return uxTask; - } + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) + { + configLIST_VOLATILE TCB_t * pxNextTCB, * pxFirstTCB; + UBaseType_t uxTask = 0; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Populate an TaskStatus_t structure within the + * pxTaskStatusArray array for each task that is referenced from + * pxList. See the definition of TaskStatus_t in task.h for the + * meaning of each TaskStatus_t structure member. */ + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); + uxTask++; + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return uxTask; + } #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ #if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) - static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) - { - uint32_t ulCount = 0U; + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + { + uint32_t ulCount = 0U; - while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) - { - pucStackByte -= portSTACK_GROWTH; - ulCount++; - } + while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + ulCount++; + } - ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ + ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ - return ( configSTACK_DEPTH_TYPE ) ulCount; - } + return ( configSTACK_DEPTH_TYPE ) ulCount; + } #endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) - /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the - same except for their return type. Using configSTACK_DEPTH_TYPE allows the - user to determine the return type. It gets around the problem of the value - overflowing on 8-bit types without breaking backward compatibility for - applications that expect an 8-bit return type. */ - configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) - { - TCB_t *pxTCB; - uint8_t *pucEndOfStack; - configSTACK_DEPTH_TYPE uxReturn; - - /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are - the same except for their return type. Using configSTACK_DEPTH_TYPE - allows the user to determine the return type. It gets around the - problem of the value overflowing on 8-bit types without breaking - backward compatibility for applications that expect an 8-bit return - type. */ - - pxTCB = prvGetTCBFromHandle( xTask ); - - #if portSTACK_GROWTH < 0 - { - pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; - } - #else - { - pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; - } - #endif - - uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); - - return uxReturn; - } +/* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + * the same except for their return type. Using configSTACK_DEPTH_TYPE + * allows the user to determine the return type. It gets around the + * problem of the value overflowing on 8-bit types without breaking + * backward compatibility for applications that expect an 8-bit return + * type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } #endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ /*-----------------------------------------------------------*/ #if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) - UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) - { - TCB_t *pxTCB; - uint8_t *pucEndOfStack; - UBaseType_t uxReturn; + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + UBaseType_t uxReturn; - pxTCB = prvGetTCBFromHandle( xTask ); + pxTCB = prvGetTCBFromHandle( xTask ); - #if portSTACK_GROWTH < 0 - { - pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; - } - #else - { - pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; - } - #endif + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif - uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); + uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); - return uxReturn; - } + return uxReturn; + } #endif /* INCLUDE_uxTaskGetStackHighWaterMark */ /*-----------------------------------------------------------*/ #if ( INCLUDE_vTaskDelete == 1 ) - static void prvDeleteTCB( TCB_t *pxTCB ) - { - /* This call is required specifically for the TriCore port. It must be - above the vPortFree() calls. The call is also used by ports/demos that - want to allocate and clean RAM statically. */ - portCLEAN_UP_TCB( pxTCB ); - - /* Free up the memory allocated by the scheduler for the task. It is up - to the task to free any memory allocated at the application level. - See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html - for additional information. */ - #if ( configUSE_NEWLIB_REENTRANT == 1 ) - { - _reclaim_reent( &( pxTCB->xNewLib_reent ) ); - } - #endif /* configUSE_NEWLIB_REENTRANT */ - - #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) - { - /* The task can only have been allocated dynamically - free both - the stack and TCB. */ - vPortFree( pxTCB->pxStack ); - vPortFree( pxTCB ); - } - #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ - { - /* The task could have been allocated statically or dynamically, so - check what was statically allocated before trying to free the - memory. */ - if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) - { - /* Both the stack and TCB were allocated dynamically, so both - must be freed. */ - vPortFree( pxTCB->pxStack ); - vPortFree( pxTCB ); - } - else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) - { - /* Only the stack was statically allocated, so the TCB is the - only memory that must be freed. */ - vPortFree( pxTCB ); - } - else - { - /* Neither the stack nor the TCB were allocated dynamically, so - nothing needs to be freed. */ - configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - } + static void prvDeleteTCB( TCB_t * pxTCB ) + { + /* This call is required specifically for the TriCore port. It must be + * above the vPortFree() calls. The call is also used by ports/demos that + * want to allocate and clean RAM statically. */ + portCLEAN_UP_TCB( pxTCB ); + + /* Free up the memory allocated by the scheduler for the task. It is up + * to the task to free any memory allocated at the application level. + * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + * for additional information. */ + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + _reclaim_reent( &( pxTCB->xNewLib_reent ) ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) + { + /* The task can only have been allocated dynamically - free both + * the stack and TCB. */ + vPortFree( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* The task could have been allocated statically or dynamically, so + * check what was statically allocated before trying to free the + * memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) + { + /* Both the stack and TCB were allocated dynamically, so both + * must be freed. */ + vPortFree( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + * only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + * nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } #endif /* INCLUDE_vTaskDelete */ /*-----------------------------------------------------------*/ static void prvResetNextTaskUnblockTime( void ) { -TCB_t *pxTCB; - - if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) - { - /* The new current delayed list is empty. Set xNextTaskUnblockTime to - the maximum possible value so it is extremely unlikely that the - if( xTickCount >= xNextTaskUnblockTime ) test will pass until - there is an item in the delayed list. */ - xNextTaskUnblockTime = portMAX_DELAY; - } - else - { - /* The new current delayed list is not empty, get the value of - the item at the head of the delayed list. This is the time at - which the task at the head of the delayed list should be removed - from the Blocked state. */ - ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) ); - } + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set xNextTaskUnblockTime to + * the maximum possible value so it is extremely unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass until + * there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + * the item at the head of the delayed list. This is the time at + * which the task at the head of the delayed list should be removed + * from the Blocked state. */ + xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList ); + } } /*-----------------------------------------------------------*/ #if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) - TaskHandle_t xTaskGetCurrentTaskHandle( void ) - { - TaskHandle_t xReturn; + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; - /* A critical section is not required as this is not called from - an interrupt and the current TCB will always be the same for any - individual execution thread. */ - xReturn = pxCurrentTCB; + /* A critical section is not required as this is not called from + * an interrupt and the current TCB will always be the same for any + * individual execution thread. */ + xReturn = pxCurrentTCB; - return xReturn; - } + return xReturn; + } #endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ /*-----------------------------------------------------------*/ #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) - BaseType_t xTaskGetSchedulerState( void ) - { - BaseType_t xReturn; - - if( xSchedulerRunning == pdFALSE ) - { - xReturn = taskSCHEDULER_NOT_STARTED; - } - else - { - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - xReturn = taskSCHEDULER_RUNNING; - } - else - { - xReturn = taskSCHEDULER_SUSPENDED; - } - } - - return xReturn; - } + BaseType_t xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } #endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ /*-----------------------------------------------------------*/ #if ( configUSE_MUTEXES == 1 ) - BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) - { - TCB_t * const pxMutexHolderTCB = pxMutexHolder; - BaseType_t xReturn = pdFALSE; - - /* If the mutex was given back by an interrupt while the queue was - locked then the mutex holder might now be NULL. _RB_ Is this still - needed as interrupts can no longer use mutexes? */ - if( pxMutexHolder != NULL ) - { - /* If the holder of the mutex has a priority below the priority of - the task attempting to obtain the mutex then it will temporarily - inherit the priority of the task attempting to obtain the mutex. */ - if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) - { - /* Adjust the mutex holder state to account for its new - priority. Only reset the event list item value if the value is - not being used for anything else. */ - if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) - { - listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* If the task being modified is in the ready state it will need - to be moved into a new list. */ - if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) - { - if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - /* It is known that the task is in its ready list so - there is no need to check again and the port level - reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Inherit the priority before being moved into the new list. */ - pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; - prvAddTaskToReadyList( pxMutexHolderTCB ); - } - else - { - /* Just inherit the priority. */ - pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; - } - - traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); - - /* Inheritance occurred. */ - xReturn = pdTRUE; - } - else - { - if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) - { - /* The base priority of the mutex holder is lower than the - priority of the task attempting to take the mutex, but the - current priority of the mutex holder is not lower than the - priority of the task attempting to take the mutex. - Therefore the mutex holder must have already inherited a - priority, but inheritance would have occurred if that had - not been the case. */ - xReturn = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return xReturn; - } + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + /* If the mutex was given back by an interrupt while the queue was + * locked then the mutex holder might now be NULL. _RB_ Is this still + * needed as interrupts can no longer use mutexes? */ + if( pxMutexHolder != NULL ) + { + /* If the holder of the mutex has a priority below the priority of + * the task attempting to obtain the mutex then it will temporarily + * inherit the priority of the task attempting to obtain the mutex. */ + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new + * priority. Only reset the event list item value if the value is + * not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task being modified is in the ready state it will need + * to be moved into a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Inherit the priority before being moved into the new list. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); + } + else + { + /* Just inherit the priority. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + } + + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; + } + else + { + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + * priority of the task attempting to take the mutex, but the + * current priority of the mutex holder is not lower than the + * priority of the task attempting to take the mutex. + * Therefore the mutex holder must have already inherited a + * priority, but inheritance would have occurred if that had + * not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ #if ( configUSE_MUTEXES == 1 ) - BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) - { - TCB_t * const pxTCB = pxMutexHolder; - BaseType_t xReturn = pdFALSE; - - if( pxMutexHolder != NULL ) - { - /* A task can only have an inherited priority if it holds the mutex. - If the mutex is held by a task then it cannot be given from an - interrupt, and if a mutex is given by the holding task then it must - be the running state task. */ - configASSERT( pxTCB == pxCurrentTCB ); - configASSERT( pxTCB->uxMutexesHeld ); - ( pxTCB->uxMutexesHeld )--; - - /* Has the holder of the mutex inherited the priority of another - task? */ - if( pxTCB->uxPriority != pxTCB->uxBasePriority ) - { - /* Only disinherit if no other mutexes are held. */ - if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) - { - /* A task can only have an inherited priority if it holds - the mutex. If the mutex is held by a task then it cannot be - given from an interrupt, and if a mutex is given by the - holding task then it must be the running state task. Remove - the holding task from the ready/delayed list. */ - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - taskRESET_READY_PRIORITY( pxTCB->uxPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Disinherit the priority before adding the task into the - new ready list. */ - traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); - pxTCB->uxPriority = pxTCB->uxBasePriority; - - /* Reset the event list item value. It cannot be in use for - any other purpose if this task is running, and it must be - running to give back the mutex. */ - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - prvAddTaskToReadyList( pxTCB ); - - /* Return true to indicate that a context switch is required. - This is only actually required in the corner case whereby - multiple mutexes were held and the mutexes were given back - in an order different to that in which they were taken. - If a context switch did not occur when the first mutex was - returned, even if a task was waiting on it, then a context - switch should occur when the last mutex is returned whether - a task is waiting on it or not. */ - xReturn = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return xReturn; - } + BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + if( pxMutexHolder != NULL ) + { + /* A task can only have an inherited priority if it holds the mutex. + * If the mutex is held by a task then it cannot be given from an + * interrupt, and if a mutex is given by the holding task then it must + * be the running state task. */ + configASSERT( pxTCB == pxCurrentTCB ); + configASSERT( pxTCB->uxMutexesHeld ); + ( pxTCB->uxMutexesHeld )--; + + /* Has the holder of the mutex inherited the priority of another + * task? */ + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* Only disinherit if no other mutexes are held. */ + if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) + { + /* A task can only have an inherited priority if it holds + * the mutex. If the mutex is held by a task then it cannot be + * given from an interrupt, and if a mutex is given by the + * holding task then it must be the running state task. Remove + * the holding task from the ready list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Disinherit the priority before adding the task into the + * new ready list. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + pxTCB->uxPriority = pxTCB->uxBasePriority; + + /* Reset the event list item value. It cannot be in use for + * any other purpose if this task is running, and it must be + * running to give back the mutex. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + prvAddTaskToReadyList( pxTCB ); + + /* Return true to indicate that a context switch is required. + * This is only actually required in the corner case whereby + * multiple mutexes were held and the mutexes were given back + * in an order different to that in which they were taken. + * If a context switch did not occur when the first mutex was + * returned, even if a task was waiting on it, then a context + * switch should occur when the last mutex is returned whether + * a task is waiting on it or not. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ #if ( configUSE_MUTEXES == 1 ) - void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) - { - TCB_t * const pxTCB = pxMutexHolder; - UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; - const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; - - if( pxMutexHolder != NULL ) - { - /* If pxMutexHolder is not NULL then the holder must hold at least - one mutex. */ - configASSERT( pxTCB->uxMutexesHeld ); - - /* Determine the priority to which the priority of the task that - holds the mutex should be set. This will be the greater of the - holding task's base priority and the priority of the highest - priority task that is waiting to obtain the mutex. */ - if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) - { - uxPriorityToUse = uxHighestPriorityWaitingTask; - } - else - { - uxPriorityToUse = pxTCB->uxBasePriority; - } - - /* Does the priority need to change? */ - if( pxTCB->uxPriority != uxPriorityToUse ) - { - /* Only disinherit if no other mutexes are held. This is a - simplification in the priority inheritance implementation. If - the task that holds the mutex is also holding other mutexes then - the other mutexes may have caused the priority inheritance. */ - if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) - { - /* If a task has timed out because it already holds the - mutex it was trying to obtain then it cannot of inherited - its own priority. */ - configASSERT( pxTCB != pxCurrentTCB ); - - /* Disinherit the priority, remembering the previous - priority to facilitate determining the subject task's - state. */ - traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); - uxPriorityUsedOnEntry = pxTCB->uxPriority; - pxTCB->uxPriority = uxPriorityToUse; - - /* Only reset the event list item value if the value is not - being used for anything else. */ - if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) - { - listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* If the running task is not the task that holds the mutex - then the task that holds the mutex could be in either the - Ready, Blocked or Suspended states. Only remove the task - from its current state list if it is in the Ready state as - the task's priority is going to change and there is one - Ready list per priority. */ - if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) - { - if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - /* It is known that the task is in its ready list so - there is no need to check again and the port level - reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - prvAddTaskToReadyList( pxTCB ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + * one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + * holds the mutex should be set. This will be the greater of the + * holding task's base priority and the priority of the highest + * priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + * simplification in the priority inheritance implementation. If + * the task that holds the mutex is also holding other mutexes then + * the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + * mutex it was trying to obtain then it cannot of inherited + * its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + * priority to facilitate determining the subject task's + * state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, uxPriorityToUse ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + * then the task that holds the mutex could be in either the + * Ready, Blocked or Suspended states. Only remove the task + * from its current state list if it is in the Ready state as + * the task's priority is going to change and there is one + * Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - void vTaskEnterCritical( void ) - { - portDISABLE_INTERRUPTS(); - - if( xSchedulerRunning != pdFALSE ) - { - ( pxCurrentTCB->uxCriticalNesting )++; - - /* This is not the interrupt safe version of the enter critical - function so assert() if it is being called from an interrupt - context. Only API functions that end in "FromISR" can be used in an - interrupt. Only assert if the critical nesting count is 1 to - protect against recursive calls if the assert function also uses a - critical section. */ - if( pxCurrentTCB->uxCriticalNesting == 1 ) - { - portASSERT_IF_IN_ISR(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + + /* This is not the interrupt safe version of the enter critical + * function so assert() if it is being called from an interrupt + * context. Only API functions that end in "FromISR" can be used in an + * interrupt. Only assert if the critical nesting count is 1 to + * protect against recursive calls if the assert function also uses a + * critical section. */ + if( pxCurrentTCB->uxCriticalNesting == 1 ) + { + portASSERT_IF_IN_ISR(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* portCRITICAL_NESTING_IN_TCB */ /*-----------------------------------------------------------*/ #if ( portCRITICAL_NESTING_IN_TCB == 1 ) - void vTaskExitCritical( void ) - { - if( xSchedulerRunning != pdFALSE ) - { - if( pxCurrentTCB->uxCriticalNesting > 0U ) - { - ( pxCurrentTCB->uxCriticalNesting )--; - - if( pxCurrentTCB->uxCriticalNesting == 0U ) - { - portENABLE_INTERRUPTS(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskExitCritical( void ) + { + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* portCRITICAL_NESTING_IN_TCB */ /*-----------------------------------------------------------*/ #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) - static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) - { - size_t x; + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) + { + size_t x; - /* Start by copying the entire string. */ - strcpy( pcBuffer, pcTaskName ); + /* Start by copying the entire string. */ + strcpy( pcBuffer, pcTaskName ); - /* Pad the end of the string with spaces to ensure columns line up when - printed out. */ - for( x = strlen( pcBuffer ); x < ( size_t ) ( configMAX_TASK_NAME_LEN - 1 ); x++ ) - { - pcBuffer[ x ] = ' '; - } + /* Pad the end of the string with spaces to ensure columns line up when + * printed out. */ + for( x = strlen( pcBuffer ); x < ( size_t ) ( configMAX_TASK_NAME_LEN - 1 ); x++ ) + { + pcBuffer[ x ] = ' '; + } - /* Terminate. */ - pcBuffer[ x ] = ( char ) 0x00; + /* Terminate. */ + pcBuffer[ x ] = ( char ) 0x00; - /* Return the new end of string. */ - return &( pcBuffer[ x ] ); - } + /* Return the new end of string. */ + return &( pcBuffer[ x ] ); + } #endif /* ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ /*-----------------------------------------------------------*/ #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - void vTaskList( char * pcWriteBuffer ) - { - TaskStatus_t *pxTaskStatusArray; - UBaseType_t uxArraySize, x; - char cStatus; - - /* - * PLEASE NOTE: - * - * This function is provided for convenience only, and is used by many - * of the demo applications. Do not consider it to be part of the - * scheduler. - * - * vTaskList() calls uxTaskGetSystemState(), then formats part of the - * uxTaskGetSystemState() output into a human readable table that - * displays task names, states and stack usage. - * - * vTaskList() has a dependency on the sprintf() C library function that - * might bloat the code size, use a lot of stack, and provide different - * results on different platforms. An alternative, tiny, third party, - * and limited functionality implementation of sprintf() is provided in - * many of the FreeRTOS/Demo sub-directories in a file called - * printf-stdarg.c (note printf-stdarg.c does not provide a full - * snprintf() implementation!). - * - * It is recommended that production systems call uxTaskGetSystemState() - * directly to get access to raw stats data, rather than indirectly - * through a call to vTaskList(). - */ - - - /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = ( char ) 0x00; - - /* Take a snapshot of the number of tasks in case it changes while this - function is executing. */ - uxArraySize = uxCurrentNumberOfTasks; - - /* Allocate an array index for each task. NOTE! if - configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will - equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ - - if( pxTaskStatusArray != NULL ) - { - /* Generate the (binary) data. */ - uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); - - /* Create a human readable table from the binary data. */ - for( x = 0; x < uxArraySize; x++ ) - { - switch( pxTaskStatusArray[ x ].eCurrentState ) - { - case eRunning: cStatus = tskRUNNING_CHAR; - break; - - case eReady: cStatus = tskREADY_CHAR; - break; - - case eBlocked: cStatus = tskBLOCKED_CHAR; - break; - - case eSuspended: cStatus = tskSUSPENDED_CHAR; - break; - - case eDeleted: cStatus = tskDELETED_CHAR; - break; - - case eInvalid: /* Fall through. */ - default: /* Should not get here, but it is included - to prevent static checking errors. */ - cStatus = ( char ) 0x00; - break; - } - - /* Write the task name to the string, padding with spaces so it - can be printed in tabular form more easily. */ - pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); - - /* Write the rest of the string. */ - sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ - pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ - } - - /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION - is 0 then vPortFree() will be #defined to nothing. */ - vPortFree( pxTaskStatusArray ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskList( char * pcWriteBuffer ) + { + TaskStatus_t * pxTaskStatusArray; + UBaseType_t uxArraySize, x; + char cStatus; + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that + * displays task names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that + * might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, + * and limited functionality implementation of sprintf() is provided in + * many of the FreeRTOS/Demo sub-directories in a file called + * printf-stdarg.c (note printf-stdarg.c does not provide a full + * snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskList(). + */ + + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! if + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); + + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + switch( pxTaskStatusArray[ x ].eCurrentState ) + { + case eRunning: + cStatus = tskRUNNING_CHAR; + break; + + case eReady: + cStatus = tskREADY_CHAR; + break; + + case eBlocked: + cStatus = tskBLOCKED_CHAR; + break; + + case eSuspended: + cStatus = tskSUSPENDED_CHAR; + break; + + case eDeleted: + cStatus = tskDELETED_CHAR; + break; + + case eInvalid: /* Fall through. */ + default: /* Should not get here, but it is included + * to prevent static checking errors. */ + cStatus = ( char ) 0x00; + break; + } + + /* Write the task name to the string, padding with spaces so it + * can be printed in tabular form more easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + /* Write the rest of the string. */ + sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ /*----------------------------------------------------------*/ #if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) - void vTaskGetRunTimeStats( char *pcWriteBuffer ) - { - TaskStatus_t *pxTaskStatusArray; - UBaseType_t uxArraySize, x; - uint32_t ulTotalTime, ulStatsAsPercentage; - - #if( configUSE_TRACE_FACILITY != 1 ) - { - #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats(). - } - #endif - - /* - * PLEASE NOTE: - * - * This function is provided for convenience only, and is used by many - * of the demo applications. Do not consider it to be part of the - * scheduler. - * - * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part - * of the uxTaskGetSystemState() output into a human readable table that - * displays the amount of time each task has spent in the Running state - * in both absolute and percentage terms. - * - * vTaskGetRunTimeStats() has a dependency on the sprintf() C library - * function that might bloat the code size, use a lot of stack, and - * provide different results on different platforms. An alternative, - * tiny, third party, and limited functionality implementation of - * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in - * a file called printf-stdarg.c (note printf-stdarg.c does not provide - * a full snprintf() implementation!). - * - * It is recommended that production systems call uxTaskGetSystemState() - * directly to get access to raw stats data, rather than indirectly - * through a call to vTaskGetRunTimeStats(). - */ - - /* Make sure the write buffer does not contain a string. */ - *pcWriteBuffer = ( char ) 0x00; - - /* Take a snapshot of the number of tasks in case it changes while this - function is executing. */ - uxArraySize = uxCurrentNumberOfTasks; - - /* Allocate an array index for each task. NOTE! If - configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will - equate to NULL. */ - pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ - - if( pxTaskStatusArray != NULL ) - { - /* Generate the (binary) data. */ - uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); - - /* For percentage calculations. */ - ulTotalTime /= 100UL; - - /* Avoid divide by zero errors. */ - if( ulTotalTime > 0UL ) - { - /* Create a human readable table from the binary data. */ - for( x = 0; x < uxArraySize; x++ ) - { - /* What percentage of the total run time has the task used? - This will always be rounded down to the nearest integer. - ulTotalRunTimeDiv100 has already been divided by 100. */ - ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; - - /* Write the task name to the string, padding with - spaces so it can be printed in tabular form more - easily. */ - pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); - - if( ulStatsAsPercentage > 0UL ) - { - #ifdef portLU_PRINTF_SPECIFIER_REQUIRED - { - sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); - } - #else - { - /* sizeof( int ) == sizeof( long ) so a smaller - printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ - } - #endif - } - else - { - /* If the percentage is zero here then the task has - consumed less than 1% of the total run time. */ - #ifdef portLU_PRINTF_SPECIFIER_REQUIRED - { - sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter ); - } - #else - { - /* sizeof( int ) == sizeof( long ) so a smaller - printf() library can be used. */ - sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ - } - #endif - } - - pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION - is 0 then vPortFree() will be #defined to nothing. */ - vPortFree( pxTaskStatusArray ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void vTaskGetRunTimeStats( char * pcWriteBuffer ) + { + TaskStatus_t * pxTaskStatusArray; + UBaseType_t uxArraySize, x; + uint32_t ulTotalTime, ulStatsAsPercentage; + + #if ( configUSE_TRACE_FACILITY != 1 ) + { + #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats(). + } + #endif + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part + * of the uxTaskGetSystemState() output into a human readable table that + * displays the amount of time each task has spent in the Running state + * in both absolute and percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library + * function that might bloat the code size, use a lot of stack, and + * provide different results on different platforms. An alternative, + * tiny, third party, and limited functionality implementation of + * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in + * a file called printf-stdarg.c (note printf-stdarg.c does not provide + * a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskGetRunTimeStats(). + */ + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! If + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); + + /* For percentage calculations. */ + ulTotalTime /= 100UL; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > 0UL ) + { + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + /* What percentage of the total run time has the task used? + * This will always be rounded down to the nearest integer. + * ulTotalRunTimeDiv100 has already been divided by 100. */ + ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; + + /* Write the task name to the string, padding with + * spaces so it can be printed in tabular form more + * easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + if( ulStatsAsPercentage > 0UL ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + else + { + /* If the percentage is zero here then the task has + * consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } #endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */ /*-----------------------------------------------------------*/ TickType_t uxTaskResetEventItemValue( void ) { -TickType_t uxReturn; + TickType_t uxReturn; - uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); + uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); - /* Reset the event list item to its normal value - so it can be used with - queues and semaphores. */ - listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + /* Reset the event list item to its normal value - so it can be used with + * queues and semaphores. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - return uxReturn; + return uxReturn; } /*-----------------------------------------------------------*/ #if ( configUSE_MUTEXES == 1 ) - TaskHandle_t pvTaskIncrementMutexHeldCount( void ) - { - /* If xSemaphoreCreateMutex() is called before any tasks have been created - then pxCurrentTCB will be NULL. */ - if( pxCurrentTCB != NULL ) - { - ( pxCurrentTCB->uxMutexesHeld )++; - } + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) + { + /* If xSemaphoreCreateMutex() is called before any tasks have been created + * then pxCurrentTCB will be NULL. */ + if( pxCurrentTCB != NULL ) + { + ( pxCurrentTCB->uxMutexesHeld )++; + } - return pxCurrentTCB; - } + return pxCurrentTCB; + } #endif /* configUSE_MUTEXES */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) - { - uint32_t ulReturn; - - taskENTER_CRITICAL(); - { - /* Only block if the notification count is not already non-zero. */ - if( pxCurrentTCB->ulNotifiedValue == 0UL ) - { - /* Mark this task as waiting for a notification. */ - pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; - - if( xTicksToWait > ( TickType_t ) 0 ) - { - prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); - traceTASK_NOTIFY_TAKE_BLOCK(); - - /* All ports are written to allow a yield in a critical - section (some will yield immediately, others wait until the - critical section exits) - but it is not something that - application code should ever do. */ - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - taskENTER_CRITICAL(); - { - traceTASK_NOTIFY_TAKE(); - ulReturn = pxCurrentTCB->ulNotifiedValue; - - if( ulReturn != 0UL ) - { - if( xClearCountOnExit != pdFALSE ) - { - pxCurrentTCB->ulNotifiedValue = 0UL; - } - else - { - pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1; - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; - } - taskEXIT_CRITICAL(); - - return ulReturn; - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWait, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) + { + uint32_t ulReturn; + + configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + taskENTER_CRITICAL(); + { + /* Only block if the notification count is not already non-zero. */ + if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] == 0UL ) + { + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWait ); + + /* All ports are written to allow a yield in a critical + * section (some will yield immediately, others wait until the + * critical section exits) - but it is not something that + * application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_TAKE( uxIndexToWait ); + ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ]; + + if( ulReturn != 0UL ) + { + if( xClearCountOnExit != pdFALSE ) + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] = 0UL; + } + else + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] = ulReturn - ( uint32_t ) 1; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) - { - BaseType_t xReturn; - - taskENTER_CRITICAL(); - { - /* Only block if a notification is not already pending. */ - if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) - { - /* Clear bits in the task's notification value as bits may get - set by the notifying task or interrupt. This can be used to - clear the value to zero. */ - pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry; - - /* Mark this task as waiting for a notification. */ - pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; - - if( xTicksToWait > ( TickType_t ) 0 ) - { - prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); - traceTASK_NOTIFY_WAIT_BLOCK(); - - /* All ports are written to allow a yield in a critical - section (some will yield immediately, others wait until the - critical section exits) - but it is not something that - application code should ever do. */ - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - taskENTER_CRITICAL(); - { - traceTASK_NOTIFY_WAIT(); - - if( pulNotificationValue != NULL ) - { - /* Output the current notification value, which may or may not - have changed. */ - *pulNotificationValue = pxCurrentTCB->ulNotifiedValue; - } - - /* If ucNotifyValue is set then either the task never entered the - blocked state (because a notification was already pending) or the - task unblocked because of a notification. Otherwise the task - unblocked because of a timeout. */ - if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) - { - /* A notification was not received. */ - xReturn = pdFALSE; - } - else - { - /* A notification was already pending or a notification was - received while the task was waiting. */ - pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit; - xReturn = pdTRUE; - } - - pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; - } - taskEXIT_CRITICAL(); - - return xReturn; - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + + configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + taskENTER_CRITICAL(); + { + /* Only block if a notification is not already pending. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) + { + /* Clear bits in the task's notification value as bits may get + * set by the notifying task or interrupt. This can be used to + * clear the value to zero. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry; + + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait ); + + /* All ports are written to allow a yield in a critical + * section (some will yield immediately, others wait until the + * critical section exits) - but it is not something that + * application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_WAIT( uxIndexToWait ); + + if( pulNotificationValue != NULL ) + { + /* Output the current notification value, which may or may not + * have changed. */ + *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ]; + } + + /* If ucNotifyValue is set then either the task never entered the + * blocked state (because a notification was already pending) or the + * task unblocked because of a notification. Otherwise the task + * unblocked because of a timeout. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) + { + /* A notification was not received. */ + xReturn = pdFALSE; + } + else + { + /* A notification was already pending or a notification was + * received while the task was waiting. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit; + xReturn = pdTRUE; + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return xReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) - { - TCB_t * pxTCB; - BaseType_t xReturn = pdPASS; - uint8_t ucOriginalNotifyState; - - configASSERT( xTaskToNotify ); - pxTCB = xTaskToNotify; - - taskENTER_CRITICAL(); - { - if( pulPreviousNotificationValue != NULL ) - { - *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; - } - - ucOriginalNotifyState = pxTCB->ucNotifyState; - - pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; - - switch( eAction ) - { - case eSetBits : - pxTCB->ulNotifiedValue |= ulValue; - break; - - case eIncrement : - ( pxTCB->ulNotifiedValue )++; - break; - - case eSetValueWithOverwrite : - pxTCB->ulNotifiedValue = ulValue; - break; - - case eSetValueWithoutOverwrite : - if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) - { - pxTCB->ulNotifiedValue = ulValue; - } - else - { - /* The value could not be written to the task. */ - xReturn = pdFAIL; - } - break; - - case eNoAction: - /* The task is being notified without its notify value being - updated. */ - break; - - default: - /* Should not get here if all enums are handled. - Artificially force an assert by testing a value the - compiler can't assume is const. */ - configASSERT( pxTCB->ulNotifiedValue == ~0UL ); - - break; - } - - traceTASK_NOTIFY(); - - /* If the task is in the blocked state specifically to wait for a - notification then unblock it now. */ - if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) - { - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - - /* The task should not have been on an event list. */ - configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - - #if( configUSE_TICKLESS_IDLE != 0 ) - { - /* If a task is blocked waiting for a notification then - xNextTaskUnblockTime might be set to the blocked task's time - out time. If the task is unblocked for a reason other than - a timeout xNextTaskUnblockTime is normally left unchanged, - because it will automatically get reset to a new value when - the tick count equals xNextTaskUnblockTime. However if - tickless idling is used it might be more important to enter - sleep mode at the earliest possible time - so reset - xNextTaskUnblockTime here to ensure it is updated at the - earliest possible time. */ - prvResetNextTaskUnblockTime(); - } - #endif - - if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* The notified task has a priority above the currently - executing task so a yield is required. */ - taskYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); - - return xReturn; - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) + { + TCB_t * pxTCB; + BaseType_t xReturn = pdPASS; + uint8_t ucOriginalNotifyState; + + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + configASSERT( xTaskToNotify ); + pxTCB = xTaskToNotify; + + taskENTER_CRITICAL(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + + break; + } + + traceTASK_NOTIFY( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + * xNextTaskUnblockTime might be set to the blocked task's time + * out time. If the task is unblocked for a reason other than + * a timeout xNextTaskUnblockTime is normally left unchanged, + * because it will automatically get reset to a new value when + * the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter + * sleep mode at the earliest possible time - so reset + * xNextTaskUnblockTime here to ensure it is updated at the + * earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) - { - TCB_t * pxTCB; - uint8_t ucOriginalNotifyState; - BaseType_t xReturn = pdPASS; - UBaseType_t uxSavedInterruptStatus; - - configASSERT( xTaskToNotify ); - - /* RTOS ports that support interrupt nesting have the concept of a - maximum system call (or maximum API call) interrupt priority. - Interrupts that are above the maximum system call priority are keep - permanently enabled, even when the RTOS kernel is in a critical section, - but cannot make any calls to FreeRTOS API functions. If configASSERT() - is defined in FreeRTOSConfig.h then - portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has - been assigned a priority above the configured maximum system call - priority. Only FreeRTOS functions that end in FromISR can be called - from interrupts that have been assigned a priority at or (logically) - below the maximum system call interrupt priority. FreeRTOS maintains a - separate interrupt safe API to ensure interrupt entry is as fast and as - simple as possible. More information (albeit Cortex-M specific) is - provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - pxTCB = xTaskToNotify; - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - if( pulPreviousNotificationValue != NULL ) - { - *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; - } - - ucOriginalNotifyState = pxTCB->ucNotifyState; - pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; - - switch( eAction ) - { - case eSetBits : - pxTCB->ulNotifiedValue |= ulValue; - break; - - case eIncrement : - ( pxTCB->ulNotifiedValue )++; - break; - - case eSetValueWithOverwrite : - pxTCB->ulNotifiedValue = ulValue; - break; - - case eSetValueWithoutOverwrite : - if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) - { - pxTCB->ulNotifiedValue = ulValue; - } - else - { - /* The value could not be written to the task. */ - xReturn = pdFAIL; - } - break; - - case eNoAction : - /* The task is being notified without its notify value being - updated. */ - break; - - default: - /* Should not get here if all enums are handled. - Artificially force an assert by testing a value the - compiler can't assume is const. */ - configASSERT( pxTCB->ulNotifiedValue == ~0UL ); - break; - } - - traceTASK_NOTIFY_FROM_ISR(); - - /* If the task is in the blocked state specifically to wait for a - notification then unblock it now. */ - if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) - { - /* The task should not have been on an event list. */ - configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - } - else - { - /* The delayed and ready lists cannot be accessed, so hold - this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); - } - - if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* The notified task has a priority above the currently - executing task so a yield is required. */ - if( pxHigherPriorityTaskWoken != NULL ) - { - *pxHigherPriorityTaskWoken = pdTRUE; - } - - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter to an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - - return xReturn; - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + BaseType_t xReturn = pdPASS; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + break; + } + + traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter to an ISR + * safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) - { - TCB_t * pxTCB; - uint8_t ucOriginalNotifyState; - UBaseType_t uxSavedInterruptStatus; - - configASSERT( xTaskToNotify ); - - /* RTOS ports that support interrupt nesting have the concept of a - maximum system call (or maximum API call) interrupt priority. - Interrupts that are above the maximum system call priority are keep - permanently enabled, even when the RTOS kernel is in a critical section, - but cannot make any calls to FreeRTOS API functions. If configASSERT() - is defined in FreeRTOSConfig.h then - portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion - failure if a FreeRTOS API function is called from an interrupt that has - been assigned a priority above the configured maximum system call - priority. Only FreeRTOS functions that end in FromISR can be called - from interrupts that have been assigned a priority at or (logically) - below the maximum system call interrupt priority. FreeRTOS maintains a - separate interrupt safe API to ensure interrupt entry is as fast and as - simple as possible. More information (albeit Cortex-M specific) is - provided on the following link: - http://www.freertos.org/RTOS-Cortex-M3-M4.html */ - portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); - - pxTCB = xTaskToNotify; - - uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); - { - ucOriginalNotifyState = pxTCB->ucNotifyState; - pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; - - /* 'Giving' is equivalent to incrementing a count in a counting - semaphore. */ - ( pxTCB->ulNotifiedValue )++; - - traceTASK_NOTIFY_GIVE_FROM_ISR(); - - /* If the task is in the blocked state specifically to wait for a - notification then unblock it now. */ - if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) - { - /* The task should not have been on an event list. */ - configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); - - if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) - { - ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); - prvAddTaskToReadyList( pxTCB ); - } - else - { - /* The delayed and ready lists cannot be accessed, so hold - this task pending until the scheduler is resumed. */ - vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); - } - - if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) - { - /* The notified task has a priority above the currently - executing task so a yield is required. */ - if( pxHigherPriorityTaskWoken != NULL ) - { - *pxHigherPriorityTaskWoken = pdTRUE; - } - - /* Mark that a yield is pending in case the user is not - using the "xHigherPriorityTaskWoken" parameter in an ISR - safe FreeRTOS function. */ - xYieldPending = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + /* 'Giving' is equivalent to incrementing a count in a counting + * semaphore. */ + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + + traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter in an ISR + * safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) - - BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ) - { - TCB_t *pxTCB; - BaseType_t xReturn; - - /* If null is passed in here then it is the calling task that is having - its notification state cleared. */ - pxTCB = prvGetTCBFromHandle( xTask ); - - taskENTER_CRITICAL(); - { - if( pxTCB->ucNotifyState == taskNOTIFICATION_RECEIVED ) - { - pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; - xReturn = pdPASS; - } - else - { - xReturn = pdFAIL; - } - } - taskEXIT_CRITICAL(); - - return xReturn; - } +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) + { + TCB_t * pxTCB; + BaseType_t xReturn; + + configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + if( pxTCB->ucNotifyState[ uxIndexToClear ] == taskNOTIFICATION_RECEIVED ) + { + pxTCB->ucNotifyState[ uxIndexToClear ] = taskNOT_WAITING_NOTIFICATION; + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( configUSE_TASK_NOTIFICATIONS == 1 ) +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) - uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) - { - TCB_t *pxTCB; - uint32_t ulReturn; + uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) + { + TCB_t * pxTCB; + uint32_t ulReturn; - /* If null is passed in here then it is the calling task that is having - its notification state cleared. */ - pxTCB = prvGetTCBFromHandle( xTask ); + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); - taskENTER_CRITICAL(); - { - /* Return the notification as it was before the bits were cleared, - then clear the bit mask. */ - ulReturn = pxCurrentTCB->ulNotifiedValue; - pxTCB->ulNotifiedValue &= ~ulBitsToClear; - } - taskEXIT_CRITICAL(); + taskENTER_CRITICAL(); + { + /* Return the notification as it was before the bits were cleared, + * then clear the bit mask. */ + ulReturn = pxTCB->ulNotifiedValue[ uxIndexToClear ]; + pxTCB->ulNotifiedValue[ uxIndexToClear ] &= ~ulBitsToClear; + } + taskEXIT_CRITICAL(); - return ulReturn; - } + return ulReturn; + } #endif /* configUSE_TASK_NOTIFICATIONS */ /*-----------------------------------------------------------*/ -#if( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) - uint32_t ulTaskGetIdleRunTimeCounter( void ) - { - return xIdleTaskHandle->ulRunTimeCounter; - } + uint32_t ulTaskGetIdleRunTimeCounter( void ) + { + return xIdleTaskHandle->ulRunTimeCounter; + } #endif /*-----------------------------------------------------------*/ -static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) { -TickType_t xTimeToWake; -const TickType_t xConstTickCount = xTickCount; - - #if( INCLUDE_xTaskAbortDelay == 1 ) - { - /* About to enter a delayed list, so ensure the ucDelayAborted flag is - reset to pdFALSE so it can be detected as having been set to pdTRUE - when the task leaves the Blocked state. */ - pxCurrentTCB->ucDelayAborted = pdFALSE; - } - #endif - - /* Remove the task from the ready list before adding it to the blocked list - as the same list item is used for both lists. */ - if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) - { - /* The current task must be in a ready list, so there is no need to - check, and the port reset macro can be called directly. */ - portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - #if ( INCLUDE_vTaskSuspend == 1 ) - { - if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) - { - /* Add the task to the suspended task list instead of a delayed task - list to ensure it is not woken by a timing event. It will block - indefinitely. */ - vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); - } - else - { - /* Calculate the time at which the task should be woken if the event - does not occur. This may overflow but this doesn't matter, the - kernel will manage it correctly. */ - xTimeToWake = xConstTickCount + xTicksToWait; - - /* The list item will be inserted in wake time order. */ - listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); - - if( xTimeToWake < xConstTickCount ) - { - /* Wake time has overflowed. Place this item in the overflow - list. */ - vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); - } - else - { - /* The wake time has not overflowed, so the current block list - is used. */ - vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); - - /* If the task entering the blocked state was placed at the - head of the list of blocked tasks then xNextTaskUnblockTime - needs to be updated too. */ - if( xTimeToWake < xNextTaskUnblockTime ) - { - xNextTaskUnblockTime = xTimeToWake; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - } - #else /* INCLUDE_vTaskSuspend */ - { - /* Calculate the time at which the task should be woken if the event - does not occur. This may overflow but this doesn't matter, the kernel - will manage it correctly. */ - xTimeToWake = xConstTickCount + xTicksToWait; - - /* The list item will be inserted in wake time order. */ - listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); - - if( xTimeToWake < xConstTickCount ) - { - /* Wake time has overflowed. Place this item in the overflow list. */ - vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); - } - else - { - /* The wake time has not overflowed, so the current block list is used. */ - vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); - - /* If the task entering the blocked state was placed at the head of the - list of blocked tasks then xNextTaskUnblockTime needs to be updated - too. */ - if( xTimeToWake < xNextTaskUnblockTime ) - { - xNextTaskUnblockTime = xTimeToWake; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - /* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */ - ( void ) xCanBlockIndefinitely; - } - #endif /* INCLUDE_vTaskSuspend */ + TickType_t xTimeToWake; + const TickType_t xConstTickCount = xTickCount; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + { + /* About to enter a delayed list, so ensure the ucDelayAborted flag is + * reset to pdFALSE so it can be detected as having been set to pdTRUE + * when the task leaves the Blocked state. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Remove the task from the ready list before adding it to the blocked list + * as the same list item is used for both lists. */ + if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + * check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) + { + /* Add the task to the suspended task list instead of a delayed task + * list to ensure it is not woken by a timing event. It will block + * indefinitely. */ + vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the + * kernel will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow + * list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list + * is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the + * head of the list of blocked tasks then xNextTaskUnblockTime + * needs to be updated too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the kernel + * will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + * list of blocked tasks then xNextTaskUnblockTime needs to be updated + * too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */ + ( void ) xCanBlockIndefinitely; + } + #endif /* INCLUDE_vTaskSuspend */ } /* Code below here allows additional code to be inserted into this source file, -especially where access to file scope functions and data is needed (for example -when performing module tests). */ + * especially where access to file scope functions and data is needed (for example + * when performing module tests). */ #ifdef FREERTOS_MODULE_TEST - #include "tasks_test_access_functions.h" + #include "tasks_test_access_functions.h" #endif -#if( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) - - #include "freertos_tasks_c_additions.h" +#if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) - #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT - static void freertos_tasks_c_additions_init( void ) - { - FREERTOS_TASKS_C_ADDITIONS_INIT(); - } - #endif - -#endif + #include "freertos_tasks_c_additions.h" + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif +#endif /* if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) */ diff --git a/source/Middlewares/Third_Party/FreeRTOS/Source/timers.c b/source/Middlewares/Third_Party/FreeRTOS/Source/timers.c index d10c83208c..d614602c2f 100644 --- a/source/Middlewares/Third_Party/FreeRTOS/Source/timers.c +++ b/source/Middlewares/Third_Party/FreeRTOS/Source/timers.c @@ -1,5 +1,5 @@ /* - * FreeRTOS Kernel V10.3.1 + * FreeRTOS Kernel V10.4.1 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -19,18 +19,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS * - * 1 tab == 4 spaces! */ /* Standard includes. */ #include /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining -all the API functions to use the MPU wrappers. That should only be done when -task.h is included from an application file. */ + * all the API functions to use the MPU wrappers. That should only be done when + * task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE #include "FreeRTOS.h" @@ -39,164 +38,158 @@ task.h is included from an application file. */ #include "timers.h" #if ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 0 ) - #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. + #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. #endif /* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified -because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined -for the header files above, but not in this file, in order to generate the -correct privileged Vs unprivileged linkage and placement. */ + * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined + * for the header files above, but not in this file, in order to generate the + * correct privileged Vs unprivileged linkage and placement. */ #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e9021 !e961 !e750. */ /* This entire source file will be skipped if the application is not configured -to include software timer functionality. This #if is closed at the very bottom -of this file. If you want to include software timer functionality then ensure -configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ + * to include software timer functionality. This #if is closed at the very bottom + * of this file. If you want to include software timer functionality then ensure + * configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ #if ( configUSE_TIMERS == 1 ) /* Misc definitions. */ -#define tmrNO_DELAY ( TickType_t ) 0U + #define tmrNO_DELAY ( TickType_t ) 0U /* The name assigned to the timer service task. This can be overridden by -defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ -#ifndef configTIMER_SERVICE_TASK_NAME - #define configTIMER_SERVICE_TASK_NAME "Tmr Svc" -#endif + * defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ + #ifndef configTIMER_SERVICE_TASK_NAME + #define configTIMER_SERVICE_TASK_NAME "Tmr Svc" + #endif /* Bit definitions used in the ucStatus member of a timer structure. */ -#define tmrSTATUS_IS_ACTIVE ( ( uint8_t ) 0x01 ) -#define tmrSTATUS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 0x02 ) -#define tmrSTATUS_IS_AUTORELOAD ( ( uint8_t ) 0x04 ) + #define tmrSTATUS_IS_ACTIVE ( ( uint8_t ) 0x01 ) + #define tmrSTATUS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 0x02 ) + #define tmrSTATUS_IS_AUTORELOAD ( ( uint8_t ) 0x04 ) /* The definition of the timers themselves. */ -typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */ -{ - const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ - TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ - void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ - TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ - #if( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ - #endif - uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ -} xTIMER; + typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */ + { + const char * pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ + TickType_t xTimerPeriodInTicks; /*<< How quickly and often the timer expires. */ + void * pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ + TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ + #endif + uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ + } xTIMER; /* The old xTIMER name is maintained above then typedefed to the new Timer_t -name below to enable the use of older kernel aware debuggers. */ -typedef xTIMER Timer_t; + * name below to enable the use of older kernel aware debuggers. */ + typedef xTIMER Timer_t; /* The definition of messages that can be sent and received on the timer queue. -Two types of message can be queued - messages that manipulate a software timer, -and messages that request the execution of a non-timer related callback. The -two message types are defined in two separate structures, xTimerParametersType -and xCallbackParametersType respectively. */ -typedef struct tmrTimerParameters -{ - TickType_t xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ - Timer_t * pxTimer; /*<< The timer to which the command will be applied. */ -} TimerParameter_t; - - -typedef struct tmrCallbackParameters -{ - PendedFunction_t pxCallbackFunction; /* << The callback function to execute. */ - void *pvParameter1; /* << The value that will be used as the callback functions first parameter. */ - uint32_t ulParameter2; /* << The value that will be used as the callback functions second parameter. */ -} CallbackParameters_t; + * Two types of message can be queued - messages that manipulate a software timer, + * and messages that request the execution of a non-timer related callback. The + * two message types are defined in two separate structures, xTimerParametersType + * and xCallbackParametersType respectively. */ + typedef struct tmrTimerParameters + { + TickType_t xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ + Timer_t * pxTimer; /*<< The timer to which the command will be applied. */ + } TimerParameter_t; + + + typedef struct tmrCallbackParameters + { + PendedFunction_t pxCallbackFunction; /* << The callback function to execute. */ + void * pvParameter1; /* << The value that will be used as the callback functions first parameter. */ + uint32_t ulParameter2; /* << The value that will be used as the callback functions second parameter. */ + } CallbackParameters_t; /* The structure that contains the two message types, along with an identifier -that is used to determine which message type is valid. */ -typedef struct tmrTimerQueueMessage -{ - BaseType_t xMessageID; /*<< The command being sent to the timer service task. */ - union - { - TimerParameter_t xTimerParameters; - - /* Don't include xCallbackParameters if it is not going to be used as - it makes the structure (and therefore the timer queue) larger. */ - #if ( INCLUDE_xTimerPendFunctionCall == 1 ) - CallbackParameters_t xCallbackParameters; - #endif /* INCLUDE_xTimerPendFunctionCall */ - } u; -} DaemonTaskMessage_t; + * that is used to determine which message type is valid. */ + typedef struct tmrTimerQueueMessage + { + BaseType_t xMessageID; /*<< The command being sent to the timer service task. */ + union + { + TimerParameter_t xTimerParameters; + + /* Don't include xCallbackParameters if it is not going to be used as + * it makes the structure (and therefore the timer queue) larger. */ + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + CallbackParameters_t xCallbackParameters; + #endif /* INCLUDE_xTimerPendFunctionCall */ + } u; + } DaemonTaskMessage_t; /*lint -save -e956 A manual analysis and inspection has been used to determine -which static variables must be declared volatile. */ + * which static variables must be declared volatile. */ /* The list in which active timers are stored. Timers are referenced in expire -time order, with the nearest expiry time at the front of the list. Only the -timer service task is allowed to access these lists. -xActiveTimerList1 and xActiveTimerList2 could be at function scope but that -breaks some kernel aware debuggers, and debuggers that reply on removing the -static qualifier. */ -PRIVILEGED_DATA static List_t xActiveTimerList1; -PRIVILEGED_DATA static List_t xActiveTimerList2; -PRIVILEGED_DATA static List_t *pxCurrentTimerList; -PRIVILEGED_DATA static List_t *pxOverflowTimerList; + * time order, with the nearest expiry time at the front of the list. Only the + * timer service task is allowed to access these lists. + * xActiveTimerList1 and xActiveTimerList2 could be at function scope but that + * breaks some kernel aware debuggers, and debuggers that reply on removing the + * static qualifier. */ + PRIVILEGED_DATA static List_t xActiveTimerList1; + PRIVILEGED_DATA static List_t xActiveTimerList2; + PRIVILEGED_DATA static List_t * pxCurrentTimerList; + PRIVILEGED_DATA static List_t * pxOverflowTimerList; /* A queue that is used to send commands to the timer service task. */ -PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; -PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; + PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; + PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; /*lint -restore */ /*-----------------------------------------------------------*/ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - - /* If static allocation is supported then the application must provide the - following callback function - which enables the application to optionally - provide the memory that will be used by the timer task as the task's stack - and TCB. */ - extern void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ); - -#endif - /* * Initialise the infrastructure used by the timer service task if it has not * been initialised already. */ -static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; + static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; /* * The timer service task (daemon). Timer functionality is controlled by this * task. Other tasks communicate with the timer service task using the * xTimerQueue queue. */ -static portTASK_FUNCTION_PROTO( prvTimerTask, pvParameters ) PRIVILEGED_FUNCTION; + static portTASK_FUNCTION_PROTO( prvTimerTask, pvParameters ) PRIVILEGED_FUNCTION; /* * Called by the timer service task to interpret and process a command it * received on the timer queue. */ -static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; + static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; /* * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, * depending on if the expire time causes a timer counter overflow. */ -static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; + static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, + const TickType_t xNextExpiryTime, + const TickType_t xTimeNow, + const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; /* * An active timer has reached its expire time. Reload the timer if it is an * auto-reload timer, then call its callback. */ -static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; + static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, + const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; /* * The tick count has overflowed. Switch the timer lists after ensuring the * current timer list does not still reference some timers. */ -static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; + static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; /* * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE * if a tick count overflow occurred since prvSampleTimeNow() was last called. */ -static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; + static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; /* * If the timer list contains any active timers then return the expire time of @@ -204,924 +197,948 @@ static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched * timer list does not contain any timers then return 0 and set *pxListWasEmpty * to pdTRUE. */ -static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; + static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; /* * If a timer has expired, process it. Otherwise, block the timer service task * until either a timer does expire or a command is received. */ -static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; + static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, + BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; /* * Called after a Timer_t structure has been allocated either statically or * dynamically to fill in the structure's members. */ -static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; + static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t * pxNewTimer ) PRIVILEGED_FUNCTION; /*-----------------------------------------------------------*/ -BaseType_t xTimerCreateTimerTask( void ) -{ -BaseType_t xReturn = pdFAIL; - - /* This function is called when the scheduler is started if - configUSE_TIMERS is set to 1. Check that the infrastructure used by the - timer service task has been created/initialised. If timers have already - been created then the initialisation will already have been performed. */ - prvCheckForValidListAndQueue(); - - if( xTimerQueue != NULL ) - { - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - StaticTask_t *pxTimerTaskTCBBuffer = NULL; - StackType_t *pxTimerTaskStackBuffer = NULL; - uint32_t ulTimerTaskStackSize; - - vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); - xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, - configTIMER_SERVICE_TASK_NAME, - ulTimerTaskStackSize, - NULL, - ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, - pxTimerTaskStackBuffer, - pxTimerTaskTCBBuffer ); - - if( xTimerTaskHandle != NULL ) - { - xReturn = pdPASS; - } - } - #else - { - xReturn = xTaskCreate( prvTimerTask, - configTIMER_SERVICE_TASK_NAME, - configTIMER_TASK_STACK_DEPTH, - NULL, - ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, - &xTimerTaskHandle ); - } - #endif /* configSUPPORT_STATIC_ALLOCATION */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - configASSERT( xReturn ); - return xReturn; -} + BaseType_t xTimerCreateTimerTask( void ) + { + BaseType_t xReturn = pdFAIL; + + /* This function is called when the scheduler is started if + * configUSE_TIMERS is set to 1. Check that the infrastructure used by the + * timer service task has been created/initialised. If timers have already + * been created then the initialisation will already have been performed. */ + prvCheckForValidListAndQueue(); + + if( xTimerQueue != NULL ) + { + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t * pxTimerTaskTCBBuffer = NULL; + StackType_t * pxTimerTaskStackBuffer = NULL; + uint32_t ulTimerTaskStackSize; + + vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); + xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, + configTIMER_SERVICE_TASK_NAME, + ulTimerTaskStackSize, + NULL, + ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, + pxTimerTaskStackBuffer, + pxTimerTaskTCBBuffer ); + + if( xTimerTaskHandle != NULL ) + { + xReturn = pdPASS; + } + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + xReturn = xTaskCreate( prvTimerTask, + configTIMER_SERVICE_TASK_NAME, + configTIMER_TASK_STACK_DEPTH, + NULL, + ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, + &xTimerTaskHandle ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + configASSERT( xReturn ); + return xReturn; + } /*-----------------------------------------------------------*/ -#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction ) - { - Timer_t *pxNewTimer; + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) + { + Timer_t * pxNewTimer; - pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's mame. */ + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's mame. */ - if( pxNewTimer != NULL ) - { - /* Status is thus far zero as the timer is not created statically - and has not been started. The auto-reload bit may get set in - prvInitialiseNewTimer. */ - pxNewTimer->ucStatus = 0x00; - prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); - } + if( pxNewTimer != NULL ) + { + /* Status is thus far zero as the timer is not created statically + * and has not been started. The auto-reload bit may get set in + * prvInitialiseNewTimer. */ + pxNewTimer->ucStatus = 0x00; + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + } - return pxNewTimer; - } + return pxNewTimer; + } -#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ -#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - - TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - StaticTimer_t *pxTimerBuffer ) - { - Timer_t *pxNewTimer; - - #if( configASSERT_DEFINED == 1 ) - { - /* Sanity check that the size of the structure used to declare a - variable of type StaticTimer_t equals the size of the real timer - structure. */ - volatile size_t xSize = sizeof( StaticTimer_t ); - configASSERT( xSize == sizeof( Timer_t ) ); - ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ - } - #endif /* configASSERT_DEFINED */ - - /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ - configASSERT( pxTimerBuffer ); - pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 !e9087 StaticTimer_t is a pointer to a Timer_t, so guaranteed to be aligned and sized correctly (checked by an assert()), so this is safe. */ - - if( pxNewTimer != NULL ) - { - /* Timers can be created statically or dynamically so note this - timer was created statically in case it is later deleted. The - auto-reload bit may get set in prvInitialiseNewTimer(). */ - pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; - - prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); - } - - return pxNewTimer; - } - -#endif /* configSUPPORT_STATIC_ALLOCATION */ + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t * pxTimerBuffer ) + { + Timer_t * pxNewTimer; + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticTimer_t equals the size of the real timer + * structure. */ + volatile size_t xSize = sizeof( StaticTimer_t ); + configASSERT( xSize == sizeof( Timer_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ + } + #endif /* configASSERT_DEFINED */ + + /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ + configASSERT( pxTimerBuffer ); + pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 !e9087 StaticTimer_t is a pointer to a Timer_t, so guaranteed to be aligned and sized correctly (checked by an assert()), so this is safe. */ + + if( pxNewTimer != NULL ) + { + /* Timers can be created statically or dynamically so note this + * timer was created statically in case it is later deleted. The + * auto-reload bit may get set in prvInitialiseNewTimer(). */ + pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; + + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + } + + return pxNewTimer; + } + + #endif /* configSUPPORT_STATIC_ALLOCATION */ /*-----------------------------------------------------------*/ -static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ - const TickType_t xTimerPeriodInTicks, - const UBaseType_t uxAutoReload, - void * const pvTimerID, - TimerCallbackFunction_t pxCallbackFunction, - Timer_t *pxNewTimer ) -{ - /* 0 is not a valid value for xTimerPeriodInTicks. */ - configASSERT( ( xTimerPeriodInTicks > 0 ) ); - - if( pxNewTimer != NULL ) - { - /* Ensure the infrastructure used by the timer service task has been - created/initialised. */ - prvCheckForValidListAndQueue(); - - /* Initialise the timer structure members using the function - parameters. */ - pxNewTimer->pcTimerName = pcTimerName; - pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; - pxNewTimer->pvTimerID = pvTimerID; - pxNewTimer->pxCallbackFunction = pxCallbackFunction; - vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); - if( uxAutoReload != pdFALSE ) - { - pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; - } - traceTIMER_CREATE( pxNewTimer ); - } -} + static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t * pxNewTimer ) + { + /* 0 is not a valid value for xTimerPeriodInTicks. */ + configASSERT( ( xTimerPeriodInTicks > 0 ) ); + + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + * created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function + * parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + + if( uxAutoReload != pdFALSE ) + { + pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + + traceTIMER_CREATE( pxNewTimer ); + } + } /*-----------------------------------------------------------*/ -BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) -{ -BaseType_t xReturn = pdFAIL; -DaemonTaskMessage_t xMessage; - - configASSERT( xTimer ); - - /* Send a message to the timer service task to perform a particular action - on a particular timer definition. */ - if( xTimerQueue != NULL ) - { - /* Send a command to the timer service task to start the xTimer timer. */ - xMessage.xMessageID = xCommandID; - xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; - xMessage.u.xTimerParameters.pxTimer = xTimer; - - if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) - { - if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) - { - xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); - } - else - { - xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); - } - } - else - { - xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); - } - - traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - return xReturn; -} + BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t * const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait ) + { + BaseType_t xReturn = pdFAIL; + DaemonTaskMessage_t xMessage; + + configASSERT( xTimer ); + + /* Send a message to the timer service task to perform a particular action + * on a particular timer definition. */ + if( xTimerQueue != NULL ) + { + /* Send a command to the timer service task to start the xTimer timer. */ + xMessage.xMessageID = xCommandID; + xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; + xMessage.u.xTimerParameters.pxTimer = xTimer; + + if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) + { + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + } + else + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); + } + } + else + { + xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + } + + traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } /*-----------------------------------------------------------*/ -TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) -{ - /* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been - started, then xTimerTaskHandle will be NULL. */ - configASSERT( ( xTimerTaskHandle != NULL ) ); - return xTimerTaskHandle; -} + TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) + { + /* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been + * started, then xTimerTaskHandle will be NULL. */ + configASSERT( ( xTimerTaskHandle != NULL ) ); + return xTimerTaskHandle; + } /*-----------------------------------------------------------*/ -TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) -{ -Timer_t *pxTimer = xTimer; + TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) + { + Timer_t * pxTimer = xTimer; - configASSERT( xTimer ); - return pxTimer->xTimerPeriodInTicks; -} + configASSERT( xTimer ); + return pxTimer->xTimerPeriodInTicks; + } /*-----------------------------------------------------------*/ -void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) -{ -Timer_t * pxTimer = xTimer; - - configASSERT( xTimer ); - taskENTER_CRITICAL(); - { - if( uxAutoReload != pdFALSE ) - { - pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; - } - else - { - pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD; - } - } - taskEXIT_CRITICAL(); -} + void vTimerSetReloadMode( TimerHandle_t xTimer, + const UBaseType_t uxAutoReload ) + { + Timer_t * pxTimer = xTimer; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( uxAutoReload != pdFALSE ) + { + pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD; + } + } + taskEXIT_CRITICAL(); + } /*-----------------------------------------------------------*/ -UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) -{ -Timer_t * pxTimer = xTimer; -UBaseType_t uxReturn; - - configASSERT( xTimer ); - taskENTER_CRITICAL(); - { - if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) == 0 ) - { - /* Not an auto-reload timer. */ - uxReturn = ( UBaseType_t ) pdFALSE; - } - else - { - /* Is an auto-reload timer. */ - uxReturn = ( UBaseType_t ) pdTRUE; - } - } - taskEXIT_CRITICAL(); - - return uxReturn; -} + UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) + { + Timer_t * pxTimer = xTimer; + UBaseType_t uxReturn; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) == 0 ) + { + /* Not an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdFALSE; + } + else + { + /* Is an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return uxReturn; + } /*-----------------------------------------------------------*/ -TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) -{ -Timer_t * pxTimer = xTimer; -TickType_t xReturn; + TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) + { + Timer_t * pxTimer = xTimer; + TickType_t xReturn; - configASSERT( xTimer ); - xReturn = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) ); - return xReturn; -} + configASSERT( xTimer ); + xReturn = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) ); + return xReturn; + } /*-----------------------------------------------------------*/ -const char * pcTimerGetName( TimerHandle_t xTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ -Timer_t *pxTimer = xTimer; + const char * pcTimerGetName( TimerHandle_t xTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + Timer_t * pxTimer = xTimer; - configASSERT( xTimer ); - return pxTimer->pcTimerName; -} + configASSERT( xTimer ); + return pxTimer->pcTimerName; + } /*-----------------------------------------------------------*/ -static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) -{ -BaseType_t xResult; -Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - - /* Remove the timer from the list of active timers. A check has already - been performed to ensure the list is not empty. */ - ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); - traceTIMER_EXPIRED( pxTimer ); - - /* If the timer is an auto-reload timer then calculate the next - expiry time and re-insert the timer in the list of active timers. */ - if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) - { - /* The timer is inserted into a list using a time relative to anything - other than the current time. It will therefore be inserted into the - correct list relative to the time this task thinks it is now. */ - if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE ) - { - /* The timer expired before it was added to the active timer - list. Reload it now. */ - xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); - configASSERT( xResult ); - ( void ) xResult; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; - mtCOVERAGE_TEST_MARKER(); - } - - /* Call the timer callback. */ - pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); -} + static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, + const TickType_t xTimeNow ) + { + BaseType_t xResult; + Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Remove the timer from the list of active timers. A check has already + * been performed to ensure the list is not empty. */ + + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* If the timer is an auto-reload timer then calculate the next + * expiry time and re-insert the timer in the list of active timers. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + /* The timer is inserted into a list using a time relative to anything + * other than the current time. It will therefore be inserted into the + * correct list relative to the time this task thinks it is now. */ + if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE ) + { + /* The timer expired before it was added to the active timer + * list. Reload it now. */ + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + mtCOVERAGE_TEST_MARKER(); + } + + /* Call the timer callback. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + } /*-----------------------------------------------------------*/ -static portTASK_FUNCTION( prvTimerTask, pvParameters ) -{ -TickType_t xNextExpireTime; -BaseType_t xListWasEmpty; - - /* Just to avoid compiler warnings. */ - ( void ) pvParameters; - - #if( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ) - { - extern void vApplicationDaemonTaskStartupHook( void ); - - /* Allow the application writer to execute some code in the context of - this task at the point the task starts executing. This is useful if the - application includes initialisation code that would benefit from - executing after the scheduler has been started. */ - vApplicationDaemonTaskStartupHook(); - } - #endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */ - - for( ;; ) - { - /* Query the timers list to see if it contains any timers, and if so, - obtain the time at which the next timer will expire. */ - xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); - - /* If a timer has expired, process it. Otherwise, block this task - until either a timer does expire, or a command is received. */ - prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); - - /* Empty the command queue. */ - prvProcessReceivedCommands(); - } -} + static portTASK_FUNCTION( prvTimerTask, pvParameters ) + { + TickType_t xNextExpireTime; + BaseType_t xListWasEmpty; + + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + #if ( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ) + { + extern void vApplicationDaemonTaskStartupHook( void ); + + /* Allow the application writer to execute some code in the context of + * this task at the point the task starts executing. This is useful if the + * application includes initialisation code that would benefit from + * executing after the scheduler has been started. */ + vApplicationDaemonTaskStartupHook(); + } + #endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */ + + for( ; ; ) + { + /* Query the timers list to see if it contains any timers, and if so, + * obtain the time at which the next timer will expire. */ + xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); + + /* If a timer has expired, process it. Otherwise, block this task + * until either a timer does expire, or a command is received. */ + prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); + + /* Empty the command queue. */ + prvProcessReceivedCommands(); + } + } /*-----------------------------------------------------------*/ -static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) -{ -TickType_t xTimeNow; -BaseType_t xTimerListsWereSwitched; - - vTaskSuspendAll(); - { - /* Obtain the time now to make an assessment as to whether the timer - has expired or not. If obtaining the time causes the lists to switch - then don't process this timer as any timers that remained in the list - when the lists were switched will have been processed within the - prvSampleTimeNow() function. */ - xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); - if( xTimerListsWereSwitched == pdFALSE ) - { - /* The tick count has not overflowed, has the timer expired? */ - if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) - { - ( void ) xTaskResumeAll(); - prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); - } - else - { - /* The tick count has not overflowed, and the next expire - time has not been reached yet. This task should therefore - block to wait for the next expire time or a command to be - received - whichever comes first. The following line cannot - be reached unless xNextExpireTime > xTimeNow, except in the - case when the current timer list is empty. */ - if( xListWasEmpty != pdFALSE ) - { - /* The current timer list is empty - is the overflow list - also empty? */ - xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList ); - } - - vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty ); - - if( xTaskResumeAll() == pdFALSE ) - { - /* Yield to wait for either a command to arrive, or the - block time to expire. If a command arrived between the - critical section being exited and this yield then the yield - will not cause the task to block. */ - portYIELD_WITHIN_API(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - } - else - { - ( void ) xTaskResumeAll(); - } - } -} + static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, + BaseType_t xListWasEmpty ) + { + TickType_t xTimeNow; + BaseType_t xTimerListsWereSwitched; + + vTaskSuspendAll(); + { + /* Obtain the time now to make an assessment as to whether the timer + * has expired or not. If obtaining the time causes the lists to switch + * then don't process this timer as any timers that remained in the list + * when the lists were switched will have been processed within the + * prvSampleTimeNow() function. */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + + if( xTimerListsWereSwitched == pdFALSE ) + { + /* The tick count has not overflowed, has the timer expired? */ + if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) + { + ( void ) xTaskResumeAll(); + prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); + } + else + { + /* The tick count has not overflowed, and the next expire + * time has not been reached yet. This task should therefore + * block to wait for the next expire time or a command to be + * received - whichever comes first. The following line cannot + * be reached unless xNextExpireTime > xTimeNow, except in the + * case when the current timer list is empty. */ + if( xListWasEmpty != pdFALSE ) + { + /* The current timer list is empty - is the overflow list + * also empty? */ + xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList ); + } + + vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty ); + + if( xTaskResumeAll() == pdFALSE ) + { + /* Yield to wait for either a command to arrive, or the + * block time to expire. If a command arrived between the + * critical section being exited and this yield then the yield + * will not cause the task to block. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + ( void ) xTaskResumeAll(); + } + } + } /*-----------------------------------------------------------*/ -static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) -{ -TickType_t xNextExpireTime; - - /* Timers are listed in expiry time order, with the head of the list - referencing the task that will expire first. Obtain the time at which - the timer with the nearest expiry time will expire. If there are no - active timers then just set the next expire time to 0. That will cause - this task to unblock when the tick count overflows, at which point the - timer lists will be switched and the next expiry time can be - re-assessed. */ - *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); - if( *pxListWasEmpty == pdFALSE ) - { - xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); - } - else - { - /* Ensure the task unblocks when the tick count rolls over. */ - xNextExpireTime = ( TickType_t ) 0U; - } - - return xNextExpireTime; -} + static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) + { + TickType_t xNextExpireTime; + + /* Timers are listed in expiry time order, with the head of the list + * referencing the task that will expire first. Obtain the time at which + * the timer with the nearest expiry time will expire. If there are no + * active timers then just set the next expire time to 0. That will cause + * this task to unblock when the tick count overflows, at which point the + * timer lists will be switched and the next expiry time can be + * re-assessed. */ + *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); + + if( *pxListWasEmpty == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + } + else + { + /* Ensure the task unblocks when the tick count rolls over. */ + xNextExpireTime = ( TickType_t ) 0U; + } + + return xNextExpireTime; + } /*-----------------------------------------------------------*/ -static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) -{ -TickType_t xTimeNow; -PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ + static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) + { + TickType_t xTimeNow; + PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ - xTimeNow = xTaskGetTickCount(); + xTimeNow = xTaskGetTickCount(); - if( xTimeNow < xLastTime ) - { - prvSwitchTimerLists(); - *pxTimerListsWereSwitched = pdTRUE; - } - else - { - *pxTimerListsWereSwitched = pdFALSE; - } + if( xTimeNow < xLastTime ) + { + prvSwitchTimerLists(); + *pxTimerListsWereSwitched = pdTRUE; + } + else + { + *pxTimerListsWereSwitched = pdFALSE; + } - xLastTime = xTimeNow; + xLastTime = xTimeNow; - return xTimeNow; -} + return xTimeNow; + } /*-----------------------------------------------------------*/ -static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) -{ -BaseType_t xProcessTimerNow = pdFALSE; - - listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); - listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); - - if( xNextExpiryTime <= xTimeNow ) - { - /* Has the expiry time elapsed between the command to start/reset a - timer was issued, and the time the command was processed? */ - if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks ) /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ - { - /* The time between a command being issued and the command being - processed actually exceeds the timers period. */ - xProcessTimerNow = pdTRUE; - } - else - { - vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); - } - } - else - { - if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) - { - /* If, since the command was issued, the tick count has overflowed - but the expiry time has not, then the timer must have already passed - its expiry time and should be processed immediately. */ - xProcessTimerNow = pdTRUE; - } - else - { - vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); - } - } - - return xProcessTimerNow; -} + static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, + const TickType_t xNextExpiryTime, + const TickType_t xTimeNow, + const TickType_t xCommandTime ) + { + BaseType_t xProcessTimerNow = pdFALSE; + + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + + if( xNextExpiryTime <= xTimeNow ) + { + /* Has the expiry time elapsed between the command to start/reset a + * timer was issued, and the time the command was processed? */ + if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks ) /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + { + /* The time between a command being issued and the command being + * processed actually exceeds the timers period. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); + } + } + else + { + if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) + { + /* If, since the command was issued, the tick count has overflowed + * but the expiry time has not, then the timer must have already passed + * its expiry time and should be processed immediately. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + } + + return xProcessTimerNow; + } /*-----------------------------------------------------------*/ -static void prvProcessReceivedCommands( void ) -{ -DaemonTaskMessage_t xMessage; -Timer_t *pxTimer; -BaseType_t xTimerListsWereSwitched, xResult; -TickType_t xTimeNow; - - while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ - { - #if ( INCLUDE_xTimerPendFunctionCall == 1 ) - { - /* Negative commands are pended function calls rather than timer - commands. */ - if( xMessage.xMessageID < ( BaseType_t ) 0 ) - { - const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); - - /* The timer uses the xCallbackParameters member to request a - callback be executed. Check the callback is not NULL. */ - configASSERT( pxCallback ); - - /* Call the function. */ - pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* INCLUDE_xTimerPendFunctionCall */ - - /* Commands that are positive are timer commands rather than pended - function calls. */ - if( xMessage.xMessageID >= ( BaseType_t ) 0 ) - { - /* The messages uses the xTimerParameters member to work on a - software timer. */ - pxTimer = xMessage.u.xTimerParameters.pxTimer; - - if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */ - { - /* The timer is in a list, remove it. */ - ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - - traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); - - /* In this case the xTimerListsWereSwitched parameter is not used, but - it must be present in the function call. prvSampleTimeNow() must be - called after the message is received from xTimerQueue so there is no - possibility of a higher priority task adding a message to the message - queue with a time that is ahead of the timer daemon task (because it - pre-empted the timer daemon task after the xTimeNow value was set). */ - xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); - - switch( xMessage.xMessageID ) - { - case tmrCOMMAND_START : - case tmrCOMMAND_START_FROM_ISR : - case tmrCOMMAND_RESET : - case tmrCOMMAND_RESET_FROM_ISR : - case tmrCOMMAND_START_DONT_TRACE : - /* Start or restart a timer. */ - pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; - if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ) - { - /* The timer expired before it was added to the active - timer list. Process it now. */ - pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); - traceTIMER_EXPIRED( pxTimer ); - - if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) - { - xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); - configASSERT( xResult ); - ( void ) xResult; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - break; - - case tmrCOMMAND_STOP : - case tmrCOMMAND_STOP_FROM_ISR : - /* The timer has already been removed from the active list. */ - pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; - break; - - case tmrCOMMAND_CHANGE_PERIOD : - case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : - pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; - pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; - configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); - - /* The new period does not really have a reference, and can - be longer or shorter than the old one. The command time is - therefore set to the current time, and as the period cannot - be zero the next expiry time can only be in the future, - meaning (unlike for the xTimerStart() case above) there is - no fail case that needs to be handled here. */ - ( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); - break; - - case tmrCOMMAND_DELETE : - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - /* The timer has already been removed from the active list, - just free up the memory if the memory was dynamically - allocated. */ - if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ) - { - vPortFree( pxTimer ); - } - else - { - pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; - } - } - #else - { - /* If dynamic allocation is not enabled, the memory - could not have been dynamically allocated. So there is - no need to free the memory - just mark the timer as - "not active". */ - pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; - } - #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ - break; - - default : - /* Don't expect to get here. */ - break; - } - } - } -} + static void prvProcessReceivedCommands( void ) + { + DaemonTaskMessage_t xMessage; + Timer_t * pxTimer; + BaseType_t xTimerListsWereSwitched, xResult; + TickType_t xTimeNow; + + while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ + { + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + { + /* Negative commands are pended function calls rather than timer + * commands. */ + if( xMessage.xMessageID < ( BaseType_t ) 0 ) + { + const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); + + /* The timer uses the xCallbackParameters member to request a + * callback be executed. Check the callback is not NULL. */ + configASSERT( pxCallback ); + + /* Call the function. */ + pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* INCLUDE_xTimerPendFunctionCall */ + + /* Commands that are positive are timer commands rather than pended + * function calls. */ + if( xMessage.xMessageID >= ( BaseType_t ) 0 ) + { + /* The messages uses the xTimerParameters member to work on a + * software timer. */ + pxTimer = xMessage.u.xTimerParameters.pxTimer; + + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */ + { + /* The timer is in a list, remove it. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); + + /* In this case the xTimerListsWereSwitched parameter is not used, but + * it must be present in the function call. prvSampleTimeNow() must be + * called after the message is received from xTimerQueue so there is no + * possibility of a higher priority task adding a message to the message + * queue with a time that is ahead of the timer daemon task (because it + * pre-empted the timer daemon task after the xTimeNow value was set). */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + + switch( xMessage.xMessageID ) + { + case tmrCOMMAND_START: + case tmrCOMMAND_START_FROM_ISR: + case tmrCOMMAND_RESET: + case tmrCOMMAND_RESET_FROM_ISR: + case tmrCOMMAND_START_DONT_TRACE: + /* Start or restart a timer. */ + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; + + if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ) + { + /* The timer expired before it was added to the active + * timer list. Process it now. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + traceTIMER_EXPIRED( pxTimer ); + + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + break; + + case tmrCOMMAND_STOP: + case tmrCOMMAND_STOP_FROM_ISR: + /* The timer has already been removed from the active list. */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + break; + + case tmrCOMMAND_CHANGE_PERIOD: + case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR: + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; + pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; + configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); + + /* The new period does not really have a reference, and can + * be longer or shorter than the old one. The command time is + * therefore set to the current time, and as the period cannot + * be zero the next expiry time can only be in the future, + * meaning (unlike for the xTimerStart() case above) there is + * no fail case that needs to be handled here. */ + ( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); + break; + + case tmrCOMMAND_DELETE: + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* The timer has already been removed from the active list, + * just free up the memory if the memory was dynamically + * allocated. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ) + { + vPortFree( pxTimer ); + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } + } + #else /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + { + /* If dynamic allocation is not enabled, the memory + * could not have been dynamically allocated. So there is + * no need to free the memory - just mark the timer as + * "not active". */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + break; + + default: + /* Don't expect to get here. */ + break; + } + } + } + } /*-----------------------------------------------------------*/ -static void prvSwitchTimerLists( void ) -{ -TickType_t xNextExpireTime, xReloadTime; -List_t *pxTemp; -Timer_t *pxTimer; -BaseType_t xResult; - - /* The tick count has overflowed. The timer lists must be switched. - If there are any timers still referenced from the current timer list - then they must have expired and should be processed before the lists - are switched. */ - while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) - { - xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); - - /* Remove the timer from the list. */ - pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ - ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); - traceTIMER_EXPIRED( pxTimer ); - - /* Execute its callback, then send a command to restart the timer if - it is an auto-reload timer. It cannot be restarted here as the lists - have not yet been switched. */ - pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); - - if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) - { - /* Calculate the reload value, and if the reload value results in - the timer going into the same timer list then it has already expired - and the timer should be re-inserted into the current list so it is - processed again within this loop. Otherwise a command should be sent - to restart the timer to ensure it is only inserted into a list after - the lists have been swapped. */ - xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); - if( xReloadTime > xNextExpireTime ) - { - listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); - listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); - vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); - } - else - { - xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); - configASSERT( xResult ); - ( void ) xResult; - } - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - - pxTemp = pxCurrentTimerList; - pxCurrentTimerList = pxOverflowTimerList; - pxOverflowTimerList = pxTemp; -} + static void prvSwitchTimerLists( void ) + { + TickType_t xNextExpireTime, xReloadTime; + List_t * pxTemp; + Timer_t * pxTimer; + BaseType_t xResult; + + /* The tick count has overflowed. The timer lists must be switched. + * If there are any timers still referenced from the current timer list + * then they must have expired and should be processed before the lists + * are switched. */ + while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + + /* Remove the timer from the list. */ + pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* Execute its callback, then send a command to restart the timer if + * it is an auto-reload timer. It cannot be restarted here as the lists + * have not yet been switched. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + /* Calculate the reload value, and if the reload value results in + * the timer going into the same timer list then it has already expired + * and the timer should be re-inserted into the current list so it is + * processed again within this loop. Otherwise a command should be sent + * to restart the timer to ensure it is only inserted into a list after + * the lists have been swapped. */ + xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); + + if( xReloadTime > xNextExpireTime ) + { + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + else + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + pxTemp = pxCurrentTimerList; + pxCurrentTimerList = pxOverflowTimerList; + pxOverflowTimerList = pxTemp; + } /*-----------------------------------------------------------*/ -static void prvCheckForValidListAndQueue( void ) -{ - /* Check that the list from which active timers are referenced, and the - queue used to communicate with the timer service, have been - initialised. */ - taskENTER_CRITICAL(); - { - if( xTimerQueue == NULL ) - { - vListInitialise( &xActiveTimerList1 ); - vListInitialise( &xActiveTimerList2 ); - pxCurrentTimerList = &xActiveTimerList1; - pxOverflowTimerList = &xActiveTimerList2; - - #if( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - /* The timer queue is allocated statically in case - configSUPPORT_DYNAMIC_ALLOCATION is 0. */ - static StaticQueue_t xStaticTimerQueue; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ - static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ - - xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); - } - #else - { - xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) ); - } - #endif - - #if ( configQUEUE_REGISTRY_SIZE > 0 ) - { - if( xTimerQueue != NULL ) - { - vQueueAddToRegistry( xTimerQueue, "TmrQ" ); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - #endif /* configQUEUE_REGISTRY_SIZE */ - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } - taskEXIT_CRITICAL(); -} + static void prvCheckForValidListAndQueue( void ) + { + /* Check that the list from which active timers are referenced, and the + * queue used to communicate with the timer service, have been + * initialised. */ + taskENTER_CRITICAL(); + { + if( xTimerQueue == NULL ) + { + vListInitialise( &xActiveTimerList1 ); + vListInitialise( &xActiveTimerList2 ); + pxCurrentTimerList = &xActiveTimerList1; + pxOverflowTimerList = &xActiveTimerList2; + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* The timer queue is allocated statically in case + * configSUPPORT_DYNAMIC_ALLOCATION is 0. */ + PRIVILEGED_DATA static StaticQueue_t xStaticTimerQueue; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + PRIVILEGED_DATA static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + + xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); + } + #else + { + xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) ); + } + #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + if( xTimerQueue != NULL ) + { + vQueueAddToRegistry( xTimerQueue, "TmrQ" ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configQUEUE_REGISTRY_SIZE */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } /*-----------------------------------------------------------*/ -BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) -{ -BaseType_t xReturn; -Timer_t *pxTimer = xTimer; - - configASSERT( xTimer ); - - /* Is the timer in the list of active timers? */ - taskENTER_CRITICAL(); - { - if( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) == 0 ) - { - xReturn = pdFALSE; - } - else - { - xReturn = pdTRUE; - } - } - taskEXIT_CRITICAL(); - - return xReturn; -} /*lint !e818 Can't be pointer to const due to the typedef. */ + BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) + { + BaseType_t xReturn; + Timer_t * pxTimer = xTimer; + + configASSERT( xTimer ); + + /* Is the timer in the list of active timers? */ + taskENTER_CRITICAL(); + { + if( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } /*lint !e818 Can't be pointer to const due to the typedef. */ /*-----------------------------------------------------------*/ -void *pvTimerGetTimerID( const TimerHandle_t xTimer ) -{ -Timer_t * const pxTimer = xTimer; -void *pvReturn; + void * pvTimerGetTimerID( const TimerHandle_t xTimer ) + { + Timer_t * const pxTimer = xTimer; + void * pvReturn; - configASSERT( xTimer ); + configASSERT( xTimer ); - taskENTER_CRITICAL(); - { - pvReturn = pxTimer->pvTimerID; - } - taskEXIT_CRITICAL(); + taskENTER_CRITICAL(); + { + pvReturn = pxTimer->pvTimerID; + } + taskEXIT_CRITICAL(); - return pvReturn; -} + return pvReturn; + } /*-----------------------------------------------------------*/ -void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) -{ -Timer_t * const pxTimer = xTimer; + void vTimerSetTimerID( TimerHandle_t xTimer, + void * pvNewID ) + { + Timer_t * const pxTimer = xTimer; - configASSERT( xTimer ); + configASSERT( xTimer ); - taskENTER_CRITICAL(); - { - pxTimer->pvTimerID = pvNewID; - } - taskEXIT_CRITICAL(); -} + taskENTER_CRITICAL(); + { + pxTimer->pvTimerID = pvNewID; + } + taskEXIT_CRITICAL(); + } /*-----------------------------------------------------------*/ -#if( INCLUDE_xTimerPendFunctionCall == 1 ) + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) - BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) - { - DaemonTaskMessage_t xMessage; - BaseType_t xReturn; + BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + BaseType_t * pxHigherPriorityTaskWoken ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; - /* Complete the message with the function parameters and post it to the - daemon task. */ - xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR; - xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; - xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; - xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + /* Complete the message with the function parameters and post it to the + * daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; - xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); - tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); - return xReturn; - } + return xReturn; + } -#endif /* INCLUDE_xTimerPendFunctionCall */ + #endif /* INCLUDE_xTimerPendFunctionCall */ /*-----------------------------------------------------------*/ -#if( INCLUDE_xTimerPendFunctionCall == 1 ) + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) - BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) - { - DaemonTaskMessage_t xMessage; - BaseType_t xReturn; + BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + TickType_t xTicksToWait ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; - /* This function can only be called after a timer has been created or - after the scheduler has been started because, until then, the timer - queue does not exist. */ - configASSERT( xTimerQueue ); + /* This function can only be called after a timer has been created or + * after the scheduler has been started because, until then, the timer + * queue does not exist. */ + configASSERT( xTimerQueue ); - /* Complete the message with the function parameters and post it to the - daemon task. */ - xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK; - xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; - xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; - xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + /* Complete the message with the function parameters and post it to the + * daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; - xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); - tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); - return xReturn; - } + return xReturn; + } -#endif /* INCLUDE_xTimerPendFunctionCall */ + #endif /* INCLUDE_xTimerPendFunctionCall */ /*-----------------------------------------------------------*/ -#if ( configUSE_TRACE_FACILITY == 1 ) + #if ( configUSE_TRACE_FACILITY == 1 ) - UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) - { - return ( ( Timer_t * ) xTimer )->uxTimerNumber; - } + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) + { + return ( ( Timer_t * ) xTimer )->uxTimerNumber; + } -#endif /* configUSE_TRACE_FACILITY */ + #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ -#if ( configUSE_TRACE_FACILITY == 1 ) + #if ( configUSE_TRACE_FACILITY == 1 ) - void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) - { - ( ( Timer_t * ) xTimer )->uxTimerNumber = uxTimerNumber; - } + void vTimerSetTimerNumber( TimerHandle_t xTimer, + UBaseType_t uxTimerNumber ) + { + ( ( Timer_t * ) xTimer )->uxTimerNumber = uxTimerNumber; + } -#endif /* configUSE_TRACE_FACILITY */ + #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ /* This entire source file will be skipped if the application is not configured -to include software timer functionality. If you want to include software timer -functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ + * to include software timer functionality. If you want to include software timer + * functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ #endif /* configUSE_TIMERS == 1 */ - - - From 1162094682f4dae14c2089ae4bed9ca7a2bfe8b6 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 14:55:59 +1100 Subject: [PATCH 014/211] Match their ld --- source/Core/BSP/Pinecilv2/FreeRTOSConfig.h | 2 +- .../drivers/bl702_driver/bl702_flash.ld | 118 +++++------------- 2 files changed, 30 insertions(+), 90 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h index 166c5f7ee7..4877b93685 100644 --- a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h +++ b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h @@ -14,7 +14,7 @@ #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (7) #define configMINIMAL_STACK_SIZE ((unsigned short)160) /* Only needs to be this high as some demo tasks also use this constant. In production only the idle task would use this. */ -#define configTOTAL_HEAP_SIZE ((size_t)1024 * 16) +#define configTOTAL_HEAP_SIZE ((size_t)1024 * 4) #define configMAX_TASK_NAME_LEN (24) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld index b6b6d96d20..ec1714b7a1 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld @@ -18,21 +18,24 @@ OUTPUT_ARCH( "riscv" ) ENTRY(_enter) StackSize = 0x1000; /* 4KB */ +HeapSize = 0x1000; /* 4KB */ +__EM_SIZE = DEFINED(ble_controller_init) ? 8K : 0K; MEMORY { - xip_memory (rx) : ORIGIN = 0x23000000, LENGTH = 1022K /* 1024K, less the two pages we are using for settings and logo*/ + xip_memory (rx) : ORIGIN = 0x23000000, LENGTH = 1022K itcm_memory (rx) : ORIGIN = 0x22014000, LENGTH = 16K - dtcm_memory (rx) : ORIGIN = 0x42018000, LENGTH = 16K - ram_memory (!rx) : ORIGIN = 0x4201C000, LENGTH = 80K - hbn_memory (rx) : ORIGIN = 0x40010000, LENGTH = 0xE00 /* hbn ram 4K used 3.5K*/ + dtcm_memory (rx) : ORIGIN = 0x42018000, LENGTH = 32K + ram_memory (!rx) : ORIGIN = 0x42020000, LENGTH = 32K + rsvd_memory (!rx) : ORIGIN = 0x42028000, LENGTH = 1K + ram2_memory (!rx) : ORIGIN = 0x42028400, LENGTH = (31K - __EM_SIZE) } SECTIONS { PROVIDE(__metal_chicken_bit = 0); - .text : + .text : { . = ALIGN(4); __text_code_start__ = .; @@ -85,78 +88,11 @@ SECTIONS _bt_l2cap_fixed_chan_list_start = .; KEEP(*(SORT_BY_NAME("._bt_l2cap_fixed_chan.static.*"))) _bt_l2cap_fixed_chan_list_end = .; - _bt_l2cap_br_fixed_chan_list_start = .; - KEEP(*(SORT_BY_NAME("._bt_l2cap_br_fixed_chan.static.*"))) - _bt_l2cap_br_fixed_chan_list_end = .; . = ALIGN(4); __text_code_end__ = .; } > xip_memory - - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >xip_memory AT>xip_memory - - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) - KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) - PROVIDE_HIDDEN (__init_array_end = .); - } >xip_memory AT>xip_memory - - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) - KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >xip_memory AT>xip_memory - - .ctors : - { - /* gcc uses crtbegin.o to find the start of - * the constructors, so we make sure it is - * first. Because this is a wildcard, it - * doesn't matter if the user does not - * actually link against crtbegin.o; the - * linker won't look for a file to match a - * wildcard. The wildcard also means that it - * doesn't matter which directory crtbegin.o - * is in. - */ - KEEP (*crtbegin.o(.ctors)) - KEEP (*crtbegin?.o(.ctors)) - /* We don't want to include the .ctor section from - * the crtend.o file until after the sorted ctors. - * The .ctor section from the crtend file contains the - * end of ctors marker and it must be last - */ - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - } >xip_memory AT>xip_memory - - .dtors : - { - KEEP (*crtbegin.o(.dtors)) - KEEP (*crtbegin?.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - } >xip_memory AT>xip_memory - - .lalign : - { - . = ALIGN(4); - PROVIDE( _data_lma = . ); - } >xip_memory AT>xip_memory - - . = ALIGN(4); __itcm_load_addr = .; @@ -186,20 +122,7 @@ SECTIONS __tcm_code_end__ = .; } > itcm_memory - __hbn_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); - - .hbn_ram_region : AT (__hbn_load_addr) - { - . = ALIGN(4); - __hbn_ram_start__ = .; - *bl702_hbn_wakeup*.o*(.rodata*) - *(.hbn_ram_code*) - *(.hbn_ram_data) - . = ALIGN(4); - __hbn_ram_end__ = .; - } > hbn_memory - - __dtcm_load_addr = __hbn_load_addr + SIZEOF(.hbn_ram_region); + __dtcm_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); .dtcm_region : AT (__dtcm_load_addr) { @@ -213,6 +136,22 @@ SECTIONS __tcm_data_end__ = .; } > dtcm_memory + /* .heap_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of heap sections, and assign + * values to heap symbols later */ + .heap_dummy (NOLOAD): + { + . = ALIGN(0x4); + . = . + HeapSize; + . = ALIGN(0x4); + } > dtcm_memory + + _HeapBase = ORIGIN(dtcm_memory) + LENGTH(dtcm_memory) - StackSize - HeapSize; + _HeapSize = HeapSize; + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(_HeapBase >= __tcm_data_end__, "region RAM overflowed with stack") + /*************************************************************************/ /* .stack_dummy section doesn't contains any symbols. It is only * used for linker to calculate size of stack sections, and assign @@ -297,16 +236,17 @@ SECTIONS . = ALIGN(4); __HeapBase = .; + /*__end__ = .;*/ + /*end = __end__;*/ KEEP(*(.heap*)) . = ALIGN(4); __HeapLimit = .; } > ram_memory - - PROVIDE (__heap_min_size = 0x400); __HeapLimit = ORIGIN(ram_memory) + LENGTH(ram_memory); - ASSERT((__HeapLimit - __HeapBase ) >= __heap_min_size, "heap size is too short.") + PROVIDE( _heap_start = ORIGIN(ram2_memory) ); + PROVIDE( _heap_size = LENGTH(ram2_memory) ); } From a7ca43da7fa8475f3d67fb093ddb65f0e3eebf16 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 17:27:38 +1100 Subject: [PATCH 015/211] :wave: --- .../components/ble/ble_stack/common/log.h | 4 +- .../components/ble/ble_stack/host/avdtp.c | 716 ------- .../components/ble/ble_stack/host/l2cap_br.c | 1574 --------------- .../components/ble/ble_stack/host/rfcomm.c | 1746 ----------------- 4 files changed, 2 insertions(+), 4038 deletions(-) delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c delete mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h index 1c426de1c8..74f9ec723e 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/common/log.h @@ -89,10 +89,10 @@ extern "C" { #if defined(CONFIG_BT_ASSERT) #if defined(BFLB_BLE) -extern void user_vAssertCalled(void); +extern void vAssertCalled(void); #define BT_ASSERT(cond) \ if ((cond) == 0) \ - user_vAssertCalled() + vAssertCalled() #else #define BT_ASSERT(cond) \ if (!(cond)) { \ diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c deleted file mode 100644 index f7b203e00d..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/avdtp.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * 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; - -static uint8_t tid; - -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; - struct bt_avdtp_single_sig_hdr *hdr; - - BT_DBG("tid = %d", tid); - - 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; - tid = 0; - } 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c deleted file mode 100644 index a282debca8..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/l2cap_br.c +++ /dev/null @@ -1,1574 +0,0 @@ -/* 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) - 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/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c deleted file mode 100644 index 1ce95adac5..0000000000 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/host/rfcomm.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* 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 -#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) - 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); -} From ea3c6e74e68943cef508339d6daac192044519c4 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 17:40:39 +1100 Subject: [PATCH 016/211] Add extra heap --- source/Core/BSP/Pinecilv2/FreeRTOSConfig.h | 2 +- .../Pinecilv2/MemMang/{heap_4.c => heap_5.c} | 256 +++++++++++------- source/Core/BSP/Pinecilv2/Setup.cpp | 13 + source/Core/BSP/Pinecilv2/ble.c | 15 +- 4 files changed, 180 insertions(+), 106 deletions(-) rename source/Core/BSP/Pinecilv2/MemMang/{heap_4.c => heap_5.c} (69%) diff --git a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h index 4877b93685..718036a6a7 100644 --- a/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h +++ b/source/Core/BSP/Pinecilv2/FreeRTOSConfig.h @@ -14,7 +14,7 @@ #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (7) #define configMINIMAL_STACK_SIZE ((unsigned short)160) /* Only needs to be this high as some demo tasks also use this constant. In production only the idle task would use this. */ -#define configTOTAL_HEAP_SIZE ((size_t)1024 * 4) +#define configTOTAL_HEAP_SIZE ((size_t)1024 * 8) #define configMAX_TASK_NAME_LEN (24) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 diff --git a/source/Core/BSP/Pinecilv2/MemMang/heap_4.c b/source/Core/BSP/Pinecilv2/MemMang/heap_5.c similarity index 69% rename from source/Core/BSP/Pinecilv2/MemMang/heap_4.c rename to source/Core/BSP/Pinecilv2/MemMang/heap_5.c index 8008f2706b..aab4f8f46f 100644 --- a/source/Core/BSP/Pinecilv2/MemMang/heap_4.c +++ b/source/Core/BSP/Pinecilv2/MemMang/heap_5.c @@ -22,15 +22,50 @@ * https://www.FreeRTOS.org * https://github.com/FreeRTOS * + * 1 tab == 4 spaces! */ /* - * A sample implementation of pvPortMalloc() and vPortFree() that combines - * (coalescences) adjacent memory blocks as they are freed, and in so doing - * limits memory fragmentation. + * A sample implementation of pvPortMalloc() that allows the heap to be defined + * across multiple non-contigous blocks and combines (coalescences) adjacent + * memory blocks as they are freed. + * + * See heap_1.c, heap_2.c, heap_3.c and heap_4.c for alternative + * implementations, and the memory management pages of https://www.FreeRTOS.org + * for more information. + * + * Usage notes: + * + * vPortDefineHeapRegions() ***must*** be called before pvPortMalloc(). + * pvPortMalloc() will be called if any task objects (tasks, queues, event + * groups, etc.) are created, therefore vPortDefineHeapRegions() ***must*** be + * called before any other objects are defined. + * + * vPortDefineHeapRegions() takes a single parameter. The parameter is an array + * of HeapRegion_t structures. HeapRegion_t is defined in portable.h as + * + * typedef struct HeapRegion + * { + * uint8_t *pucStartAddress; << Start address of a block of memory that will be part of the heap. + * size_t xSizeInBytes; << Size of the block of memory. + * } HeapRegion_t; + * + * The array is terminated using a NULL zero sized region definition, and the + * memory regions defined in the array ***must*** appear in address order from + * low address to high address. So the following is a valid example of how + * to use the function. + * + * HeapRegion_t xHeapRegions[] = + * { + * { ( uint8_t * ) 0x80000000UL, 0x10000 }, << Defines a block of 0x10000 bytes starting at address 0x80000000 + * { ( uint8_t * ) 0x90000000UL, 0xa0000 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000 + * { NULL, 0 } << Terminates the array. + * }; + * + * vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions(). + * + * Note 0x80000000 is the lower address so appears in the array first. * - * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the - * memory management pages of https://www.FreeRTOS.org for more information. */ #include @@ -54,16 +89,6 @@ /* Assumes 8bit bytes! */ #define heapBITS_PER_BYTE ( ( size_t ) 8 ) -/* Allocate the memory for the heap. */ -#if ( configAPPLICATION_ALLOCATED_HEAP == 1 ) - -/* The application writer has already defined the array used for the RTOS -* heap - probably so it can be placed in a special segment or address. */ - extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; -#else - PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; -#endif /* configAPPLICATION_ALLOCATED_HEAP */ - /* Define the linked list structure. This is used to link free blocks in order * of their memory address. */ typedef struct A_BLOCK_LINK @@ -80,13 +105,7 @@ typedef struct A_BLOCK_LINK * the block in front it and/or the block behind it if the memory blocks are * adjacent to each other. */ -static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION; - -/* - * Called automatically to setup the required heap structures the first time - * pvPortMalloc() is called. - */ -static void prvHeapInit( void ) PRIVILEGED_FUNCTION; +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ); /*-----------------------------------------------------------*/ @@ -95,20 +114,20 @@ static void prvHeapInit( void ) PRIVILEGED_FUNCTION; static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); /* Create a couple of list links to mark the start and end of the list. */ -PRIVILEGED_DATA static BlockLink_t xStart, * pxEnd = NULL; +static BlockLink_t xStart, * pxEnd = NULL; /* Keeps track of the number of calls to allocate and free memory as well as the * number of free bytes remaining, but says nothing about fragmentation. */ -PRIVILEGED_DATA static size_t xFreeBytesRemaining = 0U; -PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = 0U; -PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = 0; -PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = 0; +static size_t xFreeBytesRemaining = 0U; +static size_t xMinimumEverFreeBytesRemaining = 0U; +static size_t xNumberOfSuccessfulAllocations = 0; +static size_t xNumberOfSuccessfulFrees = 0; /* Gets set to the top bit of an size_t type. When this bit in the xBlockSize * member of an BlockLink_t structure is set then the block belongs to the * application. When the bit is free the block is still part of the free heap * space. */ -PRIVILEGED_DATA static size_t xBlockAllocatedBit = 0; +static size_t xBlockAllocatedBit = 0; /*-----------------------------------------------------------*/ @@ -117,19 +136,12 @@ void * pvPortMalloc( size_t xWantedSize ) BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink; void * pvReturn = NULL; + /* The heap must be initialised before the first call to + * prvPortMalloc(). */ + configASSERT( pxEnd ); + vTaskSuspendAll(); { - /* If this is the first call to malloc then the heap will require - * initialisation to setup the list of free blocks. */ - if( pxEnd == NULL ) - { - prvHeapInit(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - /* Check the requested block size is not so large that the top bit is * set. The top bit of the block size member of the BlockLink_t structure * is used to determine who owns the block - the application or the @@ -148,7 +160,6 @@ void * pvPortMalloc( size_t xWantedSize ) { /* Byte alignment required. */ xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); - configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); } else { @@ -194,7 +205,6 @@ void * pvPortMalloc( size_t xWantedSize ) * cast is used to prevent byte alignment warnings from the * compiler. */ pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); - configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); /* Calculate the sizes of two blocks split from the * single block. */ @@ -202,7 +212,7 @@ void * pvPortMalloc( size_t xWantedSize ) pxBlock->xBlockSize = xWantedSize; /* Insert the new block into the list of free blocks. */ - prvInsertBlockIntoFreeList( pxNewBlockLink ); + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); } else { @@ -259,7 +269,6 @@ void * pvPortMalloc( size_t xWantedSize ) } #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */ - configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); return pvReturn; } /*-----------------------------------------------------------*/ @@ -325,61 +334,7 @@ size_t xPortGetMinimumEverFreeHeapSize( void ) } /*-----------------------------------------------------------*/ -void vPortInitialiseBlocks( void ) -{ - /* This just exists to keep the linker quiet. */ -} -/*-----------------------------------------------------------*/ - -static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */ -{ - BlockLink_t * pxFirstFreeBlock; - uint8_t * pucAlignedHeap; - size_t uxAddress; - size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; - - /* Ensure the heap starts on a correctly aligned boundary. */ - uxAddress = ( size_t ) ucHeap; - - if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) - { - uxAddress += ( portBYTE_ALIGNMENT - 1 ); - uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); - xTotalHeapSize -= uxAddress - ( size_t ) ucHeap; - } - - pucAlignedHeap = ( uint8_t * ) uxAddress; - - /* xStart is used to hold a pointer to the first item in the list of free - * blocks. The void cast is used to prevent compiler warnings. */ - xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; - xStart.xBlockSize = ( size_t ) 0; - - /* pxEnd is used to mark the end of the list of free blocks and is inserted - * at the end of the heap space. */ - uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize; - uxAddress -= xHeapStructSize; - uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); - pxEnd = ( void * ) uxAddress; - pxEnd->xBlockSize = 0; - pxEnd->pxNextFreeBlock = NULL; - - /* To start with there is a single free block that is sized to take up the - * entire heap space, minus the space taken by pxEnd. */ - pxFirstFreeBlock = ( void * ) pucAlignedHeap; - pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock; - pxFirstFreeBlock->pxNextFreeBlock = pxEnd; - - /* Only one block exists - and it covers the entire usable heap space. */ - xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; - xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; - - /* Work out the position of the top bit in a size_t variable. */ - xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); -} -/*-----------------------------------------------------------*/ - -static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */ +static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) { BlockLink_t * pxIterator; uint8_t * puc; @@ -442,6 +397,101 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI } /*-----------------------------------------------------------*/ +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) +{ + BlockLink_t * pxFirstFreeBlockInRegion = NULL, * pxPreviousFreeBlock; + size_t xAlignedHeap; + size_t xTotalRegionSize, xTotalHeapSize = 0; + BaseType_t xDefinedRegions = 0; + size_t xAddress; + const HeapRegion_t * pxHeapRegion; + + /* Can only call once! */ + configASSERT( pxEnd == NULL ); + + pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); + + while( pxHeapRegion->xSizeInBytes > 0 ) + { + xTotalRegionSize = pxHeapRegion->xSizeInBytes; + + /* Ensure the heap region starts on a correctly aligned boundary. */ + xAddress = ( size_t ) pxHeapRegion->pucStartAddress; + + if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + xAddress += ( portBYTE_ALIGNMENT - 1 ); + xAddress &= ~portBYTE_ALIGNMENT_MASK; + + /* Adjust the size for the bytes lost to alignment. */ + xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress; + } + + xAlignedHeap = xAddress; + + /* Set xStart if it has not already been set. */ + if( xDefinedRegions == 0 ) + { + /* xStart is used to hold a pointer to the first item in the list of + * free blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap; + xStart.xBlockSize = ( size_t ) 0; + } + else + { + /* Should only get here if one region has already been added to the + * heap. */ + configASSERT( pxEnd != NULL ); + + /* Check blocks are passed in with increasing start addresses. */ + configASSERT( xAddress > ( size_t ) pxEnd ); + } + + /* Remember the location of the end marker in the previous region, if + * any. */ + pxPreviousFreeBlock = pxEnd; + + /* pxEnd is used to mark the end of the list of free blocks and is + * inserted at the end of the region space. */ + xAddress = xAlignedHeap + xTotalRegionSize; + xAddress -= xHeapStructSize; + xAddress &= ~portBYTE_ALIGNMENT_MASK; + pxEnd = ( BlockLink_t * ) xAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block in this region that is + * sized to take up the entire heap region minus the space taken by the + * free block structure. */ + pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap; + pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion; + pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; + + /* If this is not the first region that makes up the entire heap space + * then link the previous region to this region. */ + if( pxPreviousFreeBlock != NULL ) + { + pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion; + } + + xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; + + /* Move onto the next HeapRegion_t structure. */ + xDefinedRegions++; + pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); + } + + xMinimumEverFreeBytesRemaining = xTotalHeapSize; + xFreeBytesRemaining = xTotalHeapSize; + + /* Check something was actually defined before it is accessed. */ + configASSERT( xTotalHeapSize ); + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); +} +/*-----------------------------------------------------------*/ + void vPortGetHeapStats( HeapStats_t * pxHeapStats ) { BlockLink_t * pxBlock; @@ -466,9 +516,15 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats ) xMaxSize = pxBlock->xBlockSize; } - if( pxBlock->xBlockSize < xMinSize ) + /* Heap five will have a zero sized block at the end of each + * each region - the block is only used to link to the next + * heap region so it not a real block. */ + if( pxBlock->xBlockSize != 0 ) { - xMinSize = pxBlock->xBlockSize; + if( pxBlock->xBlockSize < xMinSize ) + { + xMinSize = pxBlock->xBlockSize; + } } /* Move to the next block in the chain until the last block is diff --git a/source/Core/BSP/Pinecilv2/Setup.cpp b/source/Core/BSP/Pinecilv2/Setup.cpp index adfbc1c9e6..26f7872c39 100644 --- a/source/Core/BSP/Pinecilv2/Setup.cpp +++ b/source/Core/BSP/Pinecilv2/Setup.cpp @@ -17,12 +17,25 @@ #define ADC_FILTER_LEN 4 uint16_t ADCReadings[ADC_NORM_SAMPLES]; // room for 32 lots of the pair of readings +// Heap + +extern uint8_t _heap_start; +extern uint8_t _heap_size; // @suppress("Type cannot be resolved") +static HeapRegion_t xHeapRegions[] = { + {&_heap_start, (unsigned int)&_heap_size}, + {NULL, 0}, /* Terminates the array. */ + {NULL, 0} /* Terminates the array. */ +}; + // Functions void setup_timer_scheduler(void); void setup_pwm(void); void setup_adc(void); void hardware_init() { + + vPortDefineHeapRegions(xHeapRegions); + gpio_set_mode(OLED_RESET_Pin, GPIO_OUTPUT_MODE); gpio_set_mode(KEY_A_Pin, GPIO_INPUT_PD_MODE); gpio_set_mode(KEY_B_Pin, GPIO_INPUT_PD_MODE); diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c index 9a17425536..ae32c231c0 100644 --- a/source/Core/BSP/Pinecilv2/ble.c +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -55,18 +55,23 @@ void bt_enable_cb(int err) } + void ble_stack_start(void) { - MSG("[OS] ble_controller_init...\r\n"); + MSG("[OS] ble_stack_start...\r\n"); GLB_Set_EM_Sel(GLB_EM_8KB); + MSG("[OS] ble_controller_init...\r\n"); ble_controller_init(configMAX_PRIORITIES - 1); + MSG("[OS] ble_controller_init...Done\r\n"); // // Initialize BLE Host stack - // MSG("[OS] hci_driver_init...\r\n"); - // hci_driver_init(); + MSG("[OS] hci_driver_init...\r\n"); + hci_driver_init(); + MSG("[OS] hci_driver_init...Done\r\n"); - // MSG("[OS] bt_enable...\r\n"); - // bt_enable(bt_enable_cb); + MSG("[OS] bt_enable...\r\n"); + bt_enable(bt_enable_cb); + MSG("[OS] bt_enable...Done\r\n"); } From 17f72ba9455916f67b3d5db684f0969048de2256 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 17:40:43 +1100 Subject: [PATCH 017/211] Update Makefile --- source/Makefile | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/source/Makefile b/source/Makefile index 5e4cb27406..ac990d4f2e 100644 --- a/source/Makefile +++ b/source/Makefile @@ -311,9 +311,49 @@ CPUFLAGS= -march=rv32imafc \ -mabi=ilp32f \ -mcmodel=medany -fsigned-char -fno-builtin -nostartfiles \ -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DBL702 -DBFLB_USE_ROM_DRIVER=1 -DEV_LDFLAGS=-nostartfiles -L $(PINECILV2_BLE_CRAPWARE_BLOB_DIR) -L $(PINECILV2_RF_CRAPWARE_BLOB_DIR) -l blecontroller_702_std -l bl702_rf +DEV_LDFLAGS=-nostartfiles -L $(PINECILV2_BLE_CRAPWARE_BLOB_DIR) -L $(PINECILV2_RF_CRAPWARE_BLOB_DIR) -l blecontroller_702_m0s1 -l bl702_rf DEV_AFLAGS= -DEV_GLOBAL_DEFS= -D CONFIG_DISABLE_BT_SMP=1 -DCONFIG_BT_BREDR=1 -DBL_MCU_SDK -DBFLB_BLE -DCFG_BLE_ENABLE -DCFG_BLE -DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER -DCONFIG_MAX_SCO=1 -DCFG_BLE_TX_BUFF_DATA=1 -DCFG_CON=1 -DDCFG_BLE_TX_BUFF_DATA=1 -DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL -DCONFIG_BT_GATT_CLIENT -DCONFIG_BT_CONN -DCONFIG_BT_GATT_DIS_PNP -DCONFIG_BT_GATT_DIS_SERIAL_NUMBER -DCONFIG_BT_GATT_DIS_FW_REV -DCONFIG_BT_GATT_DIS_HW_REV -DCONFIG_BT_GATT_DIS_SW_REV -DCONFIG_BT_ECC -DCONFIG_BT_GATT_DYNAMIC_DB -DCONFIG_BT_GATT_SERVICE_CHANGED -DCONFIG_BT_KEYS_OVERWRITE_OLDEST -DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING -DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS -DCONFIG_BT_BONDABLE -DCONFIG_BT_HCI_VS_EVT_USER +DEV_GLOBAL_DEFS= -DCFG_FREERTOS \ +-DARCH_RISCV \ +-DBL702 \ +-DCFG_BLE_ENABLE \ +-DBFLB_BLE \ +-DCFG_BLE \ +-DOPTIMIZE_DATA_EVT_FLOW_FROM_CONTROLLER \ +-DBL_MCU_SDK \ +-DCFG_CON=2 \ +-DCFG_BLE_TX_BUFF_DATA=2 \ +-DCONFIG_BT_PERIPHERAL \ +-DCONFIG_BT_L2CAP_DYNAMIC_CHANNEL \ +-DCONFIG_BT_GATT_CLIENT \ +-DCONFIG_BT_CONN \ +-DCONFIG_BT_GATT_DIS_PNP \ +-DCONFIG_BT_GATT_DIS_SERIAL_NUMBER \ +-DCONFIG_BT_GATT_DIS_FW_REV \ +-DCONFIG_BT_GATT_DIS_HW_REV \ +-DCONFIG_BT_GATT_DIS_SW_REV \ +-DCONFIG_BT_ECC \ +-DCONFIG_BT_GATT_DYNAMIC_DB \ +-DCONFIG_BT_GATT_SERVICE_CHANGED \ +-DCONFIG_BT_KEYS_OVERWRITE_OLDEST \ +-DCONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING \ +-DCONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS \ +-DCONFIG_BT_BONDABLE \ +-DCONFIG_BT_HCI_VS_EVT_USER \ +-DCONFIG_BT_ASSERT \ +-DCONFIG_BT_SIGNING \ +-DCONFIG_BT_SETTINGS_CCC_LAZY_LOADING \ +-DCONFIG_BT_SETTINGS_USE_PRINTK \ +-DCFG_BLE_STACK_DBG_PRINT \ +-DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler +# -DBFLB_USE_HAL_DRIVER +# -DCONFIG_BT_SMP \ +# -DCFG_SLEEP \ +# -DCONFIG_BT_ALLROLES \ +# -DCONFIG_BT_CENTRAL \ +# -DCONFIG_BT_OBSERVER \ +# -DCONFIG_BT_BROADCASTER \ +# -DBFLB_USE_ROM_DRIVER \ #Required to be turned off due to their drivers tripping warnings @@ -355,7 +395,7 @@ $(shell find $(DEVICE_BSP_DIR) -type d \( $(EXCLUDED_DIRS) \) -prune -false -o $(shell find $(SOURCE_MIDDLEWARES_DIR) -type f -name '*.cpp') # code optimisation ------------------------------------------------------------ -OPTIM=-Os -foptimize-strlen -faggressive-loop-optimizations -fdevirtualize-at-ltrans -fmerge-all-constants -fshort-wchar -flto -finline-small-functions -finline-functions -findirect-inlining -fdiagnostics-color -ffunction-sections -fdata-sections -fshort-enums -fsingle-precision-constant -fno-common -fno-math-errno -ffast-math -ffinite-math-only -fno-signed-zeros -fsingle-precision-constant +OPTIM=-Os -fno-jump-tables -foptimize-strlen -faggressive-loop-optimizations -fdevirtualize-at-ltrans -fmerge-all-constants -fshort-wchar -flto -finline-small-functions -finline-functions -findirect-inlining -fdiagnostics-color -ffunction-sections -fdata-sections -fshort-enums -fsingle-precision-constant -fno-common -fno-math-errno -ffast-math -ffinite-math-only -fno-signed-zeros -fsingle-precision-constant # global defines --------------------------------------------------------------- GLOBAL_DEFINES += $(DEV_GLOBAL_DEFS) -D USE_RTOS_SYSTICK -D MODEL_$(model) -D VECT_TAB_OFFSET=$(bootldr_size)U -fshort-wchar From dd8e02b2996f449a9181b077a2a4044bff0f0cca Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 22:37:44 +1100 Subject: [PATCH 018/211] c++ guard --- .../bl702_driver/std_drv/inc/bl702_sec_eng.h | 630 ++++++++---------- 1 file changed, 271 insertions(+), 359 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_sec_eng.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_sec_eng.h index 7bb7eaf61a..ed92aef2ca 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_sec_eng.h +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_sec_eng.h @@ -1,44 +1,47 @@ /** - ****************************************************************************** - * @file bl702_sec_eng.h - * @version V1.0 - * @date - * @brief This file is the standard driver header file - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2020 Bouffalo Lab

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Bouffalo Lab 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 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 bl702_sec_eng.h + * @version V1.0 + * @date + * @brief This file is the standard driver header file + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2020 Bouffalo Lab

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Bouffalo Lab 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 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. + * + ****************************************************************************** + */ #ifndef __BL702_SEC_ENG_H__ #define __BL702_SEC_ENG_H__ -#include "sec_eng_reg.h" #include "bl702_common.h" +#include "sec_eng_reg.h" +#ifdef __cplusplus +extern "C" { +#endif /** @addtogroup BL702_Peripheral_Driver * @{ */ @@ -55,260 +58,253 @@ * @brief AES port type definition */ typedef enum { - SEC_ENG_AES_ID0, /*!< AES0 port define */ + SEC_ENG_AES_ID0, /*!< AES0 port define */ } SEC_ENG_AES_ID_Type; /** * @brief SHA port type definition */ typedef enum { - SEC_ENG_SHA_ID0, /*!< SHA0 port define */ + SEC_ENG_SHA_ID0, /*!< SHA0 port define */ } SEC_ENG_SHA_ID_Type; /** * @brief SHA type definition */ typedef enum { - SEC_ENG_SHA256, /*!< SHA type:SHA256 */ - SEC_ENG_SHA224, /*!< SHA type:SHA224 */ - SEC_ENG_SHA1, /*!< SHA type:SHA1 */ - SEC_ENG_SHA1_RSVD, /*!< SHA type:SHA1 */ + SEC_ENG_SHA256, /*!< SHA type:SHA256 */ + SEC_ENG_SHA224, /*!< SHA type:SHA224 */ + SEC_ENG_SHA1, /*!< SHA type:SHA1 */ + SEC_ENG_SHA1_RSVD, /*!< SHA type:SHA1 */ } SEC_ENG_SHA_Type; /** * @brief AES type definition */ typedef enum { - SEC_ENG_AES_ECB, /*!< AES mode type:ECB */ - SEC_ENG_AES_CTR, /*!< AES mode type:CTR */ - SEC_ENG_AES_CBC, /*!< AES mode type:CBC */ + SEC_ENG_AES_ECB, /*!< AES mode type:ECB */ + SEC_ENG_AES_CTR, /*!< AES mode type:CTR */ + SEC_ENG_AES_CBC, /*!< AES mode type:CBC */ } SEC_ENG_AES_Type; /** * @brief AES KEY type definition */ typedef enum { - SEC_ENG_AES_KEY_128BITS, /*!< AES KEY type:128 bits */ - SEC_ENG_AES_KEY_256BITS, /*!< AES KEY type:256 bits */ - SEC_ENG_AES_KEY_192BITS, /*!< AES KEY type:192 bits */ - SEC_ENG_AES_DOUBLE_KEY_128BITS, /*!< AES double KEY type:128 bits */ + SEC_ENG_AES_KEY_128BITS, /*!< AES KEY type:128 bits */ + SEC_ENG_AES_KEY_256BITS, /*!< AES KEY type:256 bits */ + SEC_ENG_AES_KEY_192BITS, /*!< AES KEY type:192 bits */ + SEC_ENG_AES_DOUBLE_KEY_128BITS, /*!< AES double KEY type:128 bits */ } SEC_ENG_AES_Key_Type; /** * @brief AES CTR mode counter type definition */ typedef enum { - SEC_ENG_AES_COUNTER_BYTE_4, /*!< AES CTR mode counter type:4 bytes */ - SEC_ENG_AES_COUNTER_BYTE_1, /*!< AES CTR mode counter type:1 byte */ - SEC_ENG_AES_COUNTER_BYTE_2, /*!< AES CTR mode counter type:2 bytes */ - SEC_ENG_AES_COUNTER_BYTE_3, /*!< AES CTR mode counter type:3 bytes */ + SEC_ENG_AES_COUNTER_BYTE_4, /*!< AES CTR mode counter type:4 bytes */ + SEC_ENG_AES_COUNTER_BYTE_1, /*!< AES CTR mode counter type:1 byte */ + SEC_ENG_AES_COUNTER_BYTE_2, /*!< AES CTR mode counter type:2 bytes */ + SEC_ENG_AES_COUNTER_BYTE_3, /*!< AES CTR mode counter type:3 bytes */ } SEC_ENG_AES_Counter_Type; /** * @brief AES use new or old value type definition */ typedef enum { - SEC_ENG_AES_USE_NEW, /*!< Use new value */ - SEC_ENG_AES_USE_OLD, /*!< Use old value same as last one */ + SEC_ENG_AES_USE_NEW, /*!< Use new value */ + SEC_ENG_AES_USE_OLD, /*!< Use old value same as last one */ } SEC_ENG_AES_ValueUsed_Type; /** * @brief AES KEY source type definition */ typedef enum { - SEC_ENG_AES_KEY_SW, /*!< AES KEY from software */ - SEC_ENG_AES_KEY_HW, /*!< AES KEY from hardware */ + SEC_ENG_AES_KEY_SW, /*!< AES KEY from software */ + SEC_ENG_AES_KEY_HW, /*!< AES KEY from hardware */ } SEC_ENG_AES_Key_Src_Type; /** * @brief AES KEY source type definition */ typedef enum { - SEC_ENG_AES_ENCRYPTION, /*!< AES encryption */ - SEC_ENG_AES_DECRYPTION, /*!< AES decryption */ + SEC_ENG_AES_ENCRYPTION, /*!< AES encryption */ + SEC_ENG_AES_DECRYPTION, /*!< AES decryption */ } SEC_ENG_AES_EnDec_Type; /** * @brief AES PKA register size type definition */ typedef enum { - SEC_ENG_PKA_REG_SIZE_8 = 1, /*!< Register size is 8 Bytes */ - SEC_ENG_PKA_REG_SIZE_16, /*!< Register size is 16 Bytes */ - SEC_ENG_PKA_REG_SIZE_32, /*!< Register size is 32 Bytes */ - SEC_ENG_PKA_REG_SIZE_64, /*!< Register size is 64 Bytes */ - SEC_ENG_PKA_REG_SIZE_96, /*!< Register size is 96 Bytes */ - SEC_ENG_PKA_REG_SIZE_128, /*!< Register size is 128 Bytes */ - SEC_ENG_PKA_REG_SIZE_192, /*!< Register size is 192 Bytes */ - SEC_ENG_PKA_REG_SIZE_256, /*!< Register size is 256 Bytes */ - SEC_ENG_PKA_REG_SIZE_384, /*!< Register size is 384 Bytes */ - SEC_ENG_PKA_REG_SIZE_512, /*!< Register size is 512 Bytes */ + SEC_ENG_PKA_REG_SIZE_8 = 1, /*!< Register size is 8 Bytes */ + SEC_ENG_PKA_REG_SIZE_16, /*!< Register size is 16 Bytes */ + SEC_ENG_PKA_REG_SIZE_32, /*!< Register size is 32 Bytes */ + SEC_ENG_PKA_REG_SIZE_64, /*!< Register size is 64 Bytes */ + SEC_ENG_PKA_REG_SIZE_96, /*!< Register size is 96 Bytes */ + SEC_ENG_PKA_REG_SIZE_128, /*!< Register size is 128 Bytes */ + SEC_ENG_PKA_REG_SIZE_192, /*!< Register size is 192 Bytes */ + SEC_ENG_PKA_REG_SIZE_256, /*!< Register size is 256 Bytes */ + SEC_ENG_PKA_REG_SIZE_384, /*!< Register size is 384 Bytes */ + SEC_ENG_PKA_REG_SIZE_512, /*!< Register size is 512 Bytes */ } SEC_ENG_PKA_REG_SIZE_Type; /** * @brief AES PKA register size type definition */ typedef enum { - SEC_ENG_PKA_OP_PPSEL, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MOD2N = 0x11, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LDIV2N = 0x12, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LMUL2N = 0x13, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LDIV = 0x14, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LSQR = 0x15, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LMUL = 0x16, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LSUB = 0x17, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LADD = 0x18, /*!< PKA operation type */ - SEC_ENG_PKA_OP_LCMP = 0x19, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MDIV2 = 0x21, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MINV = 0x22, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MEXP = 0x23, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MSQR = 0x24, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MMUL = 0x25, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MREM = 0x26, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MSUB = 0x27, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MADD = 0x28, /*!< PKA operation type */ - SEC_ENG_PKA_OP_RESIZE = 0x31, /*!< PKA operation type */ - SEC_ENG_PKA_OP_MOVDAT = 0x32, /*!< PKA operation type */ - SEC_ENG_PKA_OP_NLIR = 0x33, /*!< PKA operation type */ - SEC_ENG_PKA_OP_SLIR = 0x34, /*!< PKA operation type */ - SEC_ENG_PKA_OP_CLIR = 0x35, /*!< PKA operation type */ - SEC_ENG_PKA_OP_CFLIRI_BUFFER = 0x36, /*!< PKA operation type */ - SEC_ENG_PKA_OP_CTLIRI_PLD = 0x37, /*!< PKA operation type */ - SEC_ENG_PKA_OP_CFLIR_BUFFER = 0x38, /*!< PKA operation type */ - SEC_ENG_PKA_OP_CTLIR_PLD = 0x39, /*!< PKA operation type */ + SEC_ENG_PKA_OP_PPSEL, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MOD2N = 0x11, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LDIV2N = 0x12, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LMUL2N = 0x13, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LDIV = 0x14, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LSQR = 0x15, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LMUL = 0x16, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LSUB = 0x17, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LADD = 0x18, /*!< PKA operation type */ + SEC_ENG_PKA_OP_LCMP = 0x19, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MDIV2 = 0x21, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MINV = 0x22, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MEXP = 0x23, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MSQR = 0x24, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MMUL = 0x25, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MREM = 0x26, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MSUB = 0x27, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MADD = 0x28, /*!< PKA operation type */ + SEC_ENG_PKA_OP_RESIZE = 0x31, /*!< PKA operation type */ + SEC_ENG_PKA_OP_MOVDAT = 0x32, /*!< PKA operation type */ + SEC_ENG_PKA_OP_NLIR = 0x33, /*!< PKA operation type */ + SEC_ENG_PKA_OP_SLIR = 0x34, /*!< PKA operation type */ + SEC_ENG_PKA_OP_CLIR = 0x35, /*!< PKA operation type */ + SEC_ENG_PKA_OP_CFLIRI_BUFFER = 0x36, /*!< PKA operation type */ + SEC_ENG_PKA_OP_CTLIRI_PLD = 0x37, /*!< PKA operation type */ + SEC_ENG_PKA_OP_CFLIR_BUFFER = 0x38, /*!< PKA operation type */ + SEC_ENG_PKA_OP_CTLIR_PLD = 0x39, /*!< PKA operation type */ } SEC_ENG_PKA_OP_Type; /** * @brief Sec Eng Interrupt Type Def */ typedef enum { - SEC_ENG_INT_TRNG, /*!< Sec Eng Trng Interrupt Type */ - SEC_ENG_INT_AES, /*!< Sec Eng Aes Interrupt Type */ - SEC_ENG_INT_SHA, /*!< Sec Eng Sha Interrupt Type */ - SEC_ENG_INT_PKA, /*!< Sec Eng Pka Interrupt Type */ - SEC_ENG_INT_CDET, /*!< Sec Eng Cdet Interrupt Type */ - SEC_ENG_INT_GMAC, /*!< Sec Eng Gmac Interrupt Type */ - SEC_ENG_INT_ALL, /*!< Sec Eng All Interrupt Types */ + SEC_ENG_INT_TRNG, /*!< Sec Eng Trng Interrupt Type */ + SEC_ENG_INT_AES, /*!< Sec Eng Aes Interrupt Type */ + SEC_ENG_INT_SHA, /*!< Sec Eng Sha Interrupt Type */ + SEC_ENG_INT_PKA, /*!< Sec Eng Pka Interrupt Type */ + SEC_ENG_INT_CDET, /*!< Sec Eng Cdet Interrupt Type */ + SEC_ENG_INT_GMAC, /*!< Sec Eng Gmac Interrupt Type */ + SEC_ENG_INT_ALL, /*!< Sec Eng All Interrupt Types */ } SEC_ENG_INT_Type; /** * @brief SEC_ENG SHA context */ -typedef struct -{ - uint32_t total[2]; /*!< Number of bytes processed */ - uint32_t *shaBuf; /*!< Data not processed but in this temp buffer */ - uint32_t *shaPadding; /*!< Padding data */ - uint8_t shaFeed; /*!< Sha has feed data */ +typedef struct { + uint32_t total[2]; /*!< Number of bytes processed */ + uint32_t *shaBuf; /*!< Data not processed but in this temp buffer */ + uint32_t *shaPadding; /*!< Padding data */ + uint8_t shaFeed; /*!< Sha has feed data */ } SEC_Eng_SHA256_Ctx; /** * @brief SEC_ENG SHA link mode context */ -typedef struct -{ - uint32_t total[2]; /*!< Number of bytes processed */ - uint32_t *shaBuf; /*!< Data not processed but in this temp buffer */ - uint32_t *shaPadding; /*!< Padding data */ - uint32_t linkAddr; /*!< Link configure address */ +typedef struct { + uint32_t total[2]; /*!< Number of bytes processed */ + uint32_t *shaBuf; /*!< Data not processed but in this temp buffer */ + uint32_t *shaPadding; /*!< Padding data */ + uint32_t linkAddr; /*!< Link configure address */ } SEC_Eng_SHA256_Link_Ctx; /** * @brief SEC_ENG AES context */ -typedef struct -{ - uint8_t aesFeed; /*!< AES has feed data */ - SEC_ENG_AES_Type mode; /*!< AES mode */ +typedef struct { + uint8_t aesFeed; /*!< AES has feed data */ + SEC_ENG_AES_Type mode; /*!< AES mode */ } SEC_Eng_AES_Ctx; /** * @brief SEC_ENG SHA link config structure type definition */ -typedef struct -{ - uint32_t : 2; /*!< [1:0]reserved */ - uint32_t shaMode : 3; /*!< [4:2]Sha-256/sha-224/sha-1/sha-1 */ - uint32_t : 1; /*!< [5]reserved */ - uint32_t shaHashSel : 1; /*!< [6]New hash or accumulate last hash */ - uint32_t : 2; /*!< [8:7]reserved */ - uint32_t shaIntClr : 1; /*!< [9]Clear interrupt */ - uint32_t shaIntSet : 1; /*!< [10]Set interrupt */ - uint32_t : 5; /*!< [15:11]reserved */ - uint32_t shaMsgLen : 16; /*!< [31:16]Number of 512-bit block */ - uint32_t shaSrcAddr; /*!< Message source address */ - uint32_t result[8]; /*!< Result of SHA */ +typedef struct { + uint32_t : 2; /*!< [1:0]reserved */ + uint32_t shaMode : 3; /*!< [4:2]Sha-256/sha-224/sha-1/sha-1 */ + uint32_t : 1; /*!< [5]reserved */ + uint32_t shaHashSel : 1; /*!< [6]New hash or accumulate last hash */ + uint32_t : 2; /*!< [8:7]reserved */ + uint32_t shaIntClr : 1; /*!< [9]Clear interrupt */ + uint32_t shaIntSet : 1; /*!< [10]Set interrupt */ + uint32_t : 5; /*!< [15:11]reserved */ + uint32_t shaMsgLen : 16; /*!< [31:16]Number of 512-bit block */ + uint32_t shaSrcAddr; /*!< Message source address */ + uint32_t result[8]; /*!< Result of SHA */ } __attribute__((aligned(4))) SEC_Eng_SHA_Link_Config_Type; /** * @brief SEC_ENG AES link config structure type definition */ -typedef struct -{ - uint32_t : 3; /*!< [2:0]Reserved */ - uint32_t aesMode : 2; /*!< [4:3]128-bit/256-bit/192-bit/128-bit-double key mode select */ - uint32_t aesDecEn : 1; /*!< [5]Encode or decode */ - uint32_t aesDecKeySel : 1; /*!< [6]Use new key or use same key as last one */ - uint32_t aesHwKeyEn : 1; /*!< [7]Enable or disable using hardware hey */ - uint32_t : 1; /*!< [8]Reserved */ - uint32_t aesIntClr : 1; /*!< [9]Clear interrupt */ - uint32_t aesIntSet : 1; /*!< [10]Set interrupt */ - uint32_t : 1; /*!< [11]Reserved */ - uint32_t aesBlockMode : 2; /*!< [13:12]ECB/CTR/CBC mode select */ - uint32_t aesIVSel : 1; /*!< [14]Use new iv or use same iv as last one */ - uint32_t : 1; /*!< [15]Reserved */ - uint32_t aesMsgLen : 16; /*!< [31:16]Number of 128-bit block */ - uint32_t aesSrcAddr; /*!< Message source address */ - uint32_t aesDstAddr; /*!< Message destination address */ - uint32_t aesIV0; /*!< Big endian initial vector(MSB) */ - uint32_t aesIV1; /*!< Big endian initial vector */ - uint32_t aesIV2; /*!< Big endian initial vector */ - uint32_t aesIV3; /*!< Big endian initial vector(LSB)(CTR mode:counter initial value) */ - uint32_t aesKey0; /*!< Big endian aes key(aes-128/256 key MSB) */ - uint32_t aesKey1; /*!< Big endian aes key */ - uint32_t aesKey2; /*!< Big endian aes key */ - uint32_t aesKey3; /*!< Big endian aes key(aes-128 key LSB) */ - uint32_t aesKey4; /*!< Big endian aes key */ - uint32_t aesKey5; /*!< Big endian aes key */ - uint32_t aesKey6; /*!< Big endian aes key */ - uint32_t aesKey7; /*!< Big endian aes key(aes-256 key LSB) */ +typedef struct { + uint32_t : 3; /*!< [2:0]Reserved */ + uint32_t aesMode : 2; /*!< [4:3]128-bit/256-bit/192-bit/128-bit-double key mode select */ + uint32_t aesDecEn : 1; /*!< [5]Encode or decode */ + uint32_t aesDecKeySel : 1; /*!< [6]Use new key or use same key as last one */ + uint32_t aesHwKeyEn : 1; /*!< [7]Enable or disable using hardware hey */ + uint32_t : 1; /*!< [8]Reserved */ + uint32_t aesIntClr : 1; /*!< [9]Clear interrupt */ + uint32_t aesIntSet : 1; /*!< [10]Set interrupt */ + uint32_t : 1; /*!< [11]Reserved */ + uint32_t aesBlockMode : 2; /*!< [13:12]ECB/CTR/CBC mode select */ + uint32_t aesIVSel : 1; /*!< [14]Use new iv or use same iv as last one */ + uint32_t : 1; /*!< [15]Reserved */ + uint32_t aesMsgLen : 16; /*!< [31:16]Number of 128-bit block */ + uint32_t aesSrcAddr; /*!< Message source address */ + uint32_t aesDstAddr; /*!< Message destination address */ + uint32_t aesIV0; /*!< Big endian initial vector(MSB) */ + uint32_t aesIV1; /*!< Big endian initial vector */ + uint32_t aesIV2; /*!< Big endian initial vector */ + uint32_t aesIV3; /*!< Big endian initial vector(LSB)(CTR mode:counter initial value) */ + uint32_t aesKey0; /*!< Big endian aes key(aes-128/256 key MSB) */ + uint32_t aesKey1; /*!< Big endian aes key */ + uint32_t aesKey2; /*!< Big endian aes key */ + uint32_t aesKey3; /*!< Big endian aes key(aes-128 key LSB) */ + uint32_t aesKey4; /*!< Big endian aes key */ + uint32_t aesKey5; /*!< Big endian aes key */ + uint32_t aesKey6; /*!< Big endian aes key */ + uint32_t aesKey7; /*!< Big endian aes key(aes-256 key LSB) */ } __attribute__((aligned(4))) SEC_Eng_AES_Link_Config_Type; /** * @brief SEC_ENG GMAC link config structure type definition */ -typedef struct -{ - uint32_t : 9; /*!< [8:0]reserved */ - uint32_t gmacIntClr : 1; /*!< [9]Clear interrupt */ - uint32_t gmacIntSet : 1; /*!< [10]Set interrupt */ - uint32_t : 5; /*!< [15:11]reserved */ - uint32_t gmacMsgLen : 16; /*!< [31:16]Number of 128-bit block */ - uint32_t gmacSrcAddr; /*!< Message source address */ - uint32_t gmacKey0; /*!< GMAC key */ - uint32_t gmacKey1; /*!< GMAC key */ - uint32_t gmacKey2; /*!< GMAC key */ - uint32_t gmacKey3; /*!< GMAC key */ - uint32_t result[4]; /*!< Result of GMAC */ - uint32_t dummy; /*!< Not use,trigger GMAC will clear this value */ +typedef struct { + uint32_t : 9; /*!< [8:0]reserved */ + uint32_t gmacIntClr : 1; /*!< [9]Clear interrupt */ + uint32_t gmacIntSet : 1; /*!< [10]Set interrupt */ + uint32_t : 5; /*!< [15:11]reserved */ + uint32_t gmacMsgLen : 16; /*!< [31:16]Number of 128-bit block */ + uint32_t gmacSrcAddr; /*!< Message source address */ + uint32_t gmacKey0; /*!< GMAC key */ + uint32_t gmacKey1; /*!< GMAC key */ + uint32_t gmacKey2; /*!< GMAC key */ + uint32_t gmacKey3; /*!< GMAC key */ + uint32_t result[4]; /*!< Result of GMAC */ + uint32_t dummy; /*!< Not use,trigger GMAC will clear this value */ } __attribute__((aligned(4))) SEC_Eng_GMAC_Link_Config_Type; /** * @brief SEC_ENG PKA status type definition */ -typedef struct -{ - uint16_t primeFail : 1; /*!< [0]Prime fail */ - uint16_t errUnknown : 1; /*!< [1]Err unknown opc */ - uint16_t errOverflow : 1; /*!< [2]Err opq overflow */ - uint16_t errSrc2 : 1; /*!< [3]Err invalid src2 */ - uint16_t errSrc1 : 1; /*!< [4]Err invalid src1 */ - uint16_t errSrc0 : 1; /*!< [5]Err invalid src0 */ - uint16_t errDiv0 : 1; /*!< [6]Err div by 0 */ - uint16_t errFull : 1; /*!< [7]Err cam full */ - uint16_t lastOpc : 1; /*!< [8]Last opc */ - uint16_t opqFull : 1; /*!< [9]Opq full */ - uint16_t cmdIndex : 5; /*!< [14:10]Cmd err index */ - uint16_t errCmd : 1; /*!< [15]Err cmd */ +typedef struct { + uint16_t primeFail : 1; /*!< [0]Prime fail */ + uint16_t errUnknown : 1; /*!< [1]Err unknown opc */ + uint16_t errOverflow : 1; /*!< [2]Err opq overflow */ + uint16_t errSrc2 : 1; /*!< [3]Err invalid src2 */ + uint16_t errSrc1 : 1; /*!< [4]Err invalid src1 */ + uint16_t errSrc0 : 1; /*!< [5]Err invalid src0 */ + uint16_t errDiv0 : 1; /*!< [6]Err div by 0 */ + uint16_t errFull : 1; /*!< [7]Err cam full */ + uint16_t lastOpc : 1; /*!< [8]Last opc */ + uint16_t opqFull : 1; /*!< [9]Opq full */ + uint16_t cmdIndex : 5; /*!< [14:10]Cmd err index */ + uint16_t errCmd : 1; /*!< [15]Err cmd */ } SEC_Eng_PKA_Status_Type; /*@} end of group SEC_ENG_Public_Types */ @@ -330,107 +326,64 @@ typedef struct /** @defgroup SEC_ENG_SHA_TYPE * @{ */ -#define IS_SEC_ENG_SHA_TYPE(type) (((type) == SEC_ENG_SHA256) || \ - ((type) == SEC_ENG_SHA224) || \ - ((type) == SEC_ENG_SHA1) || \ - ((type) == SEC_ENG_SHA1_RSVD)) +#define IS_SEC_ENG_SHA_TYPE(type) (((type) == SEC_ENG_SHA256) || ((type) == SEC_ENG_SHA224) || ((type) == SEC_ENG_SHA1) || ((type) == SEC_ENG_SHA1_RSVD)) /** @defgroup SEC_ENG_AES_TYPE * @{ */ -#define IS_SEC_ENG_AES_TYPE(type) (((type) == SEC_ENG_AES_ECB) || \ - ((type) == SEC_ENG_AES_CTR) || \ - ((type) == SEC_ENG_AES_CBC)) +#define IS_SEC_ENG_AES_TYPE(type) (((type) == SEC_ENG_AES_ECB) || ((type) == SEC_ENG_AES_CTR) || ((type) == SEC_ENG_AES_CBC)) /** @defgroup SEC_ENG_AES_KEY_TYPE * @{ */ -#define IS_SEC_ENG_AES_KEY_TYPE(type) (((type) == SEC_ENG_AES_KEY_128BITS) || \ - ((type) == SEC_ENG_AES_KEY_256BITS) || \ - ((type) == SEC_ENG_AES_KEY_192BITS) || \ - ((type) == SEC_ENG_AES_DOUBLE_KEY_128BITS)) +#define IS_SEC_ENG_AES_KEY_TYPE(type) (((type) == SEC_ENG_AES_KEY_128BITS) || ((type) == SEC_ENG_AES_KEY_256BITS) || ((type) == SEC_ENG_AES_KEY_192BITS) || ((type) == SEC_ENG_AES_DOUBLE_KEY_128BITS)) /** @defgroup SEC_ENG_AES_COUNTER_TYPE * @{ */ -#define IS_SEC_ENG_AES_COUNTER_TYPE(type) (((type) == SEC_ENG_AES_COUNTER_BYTE_4) || \ - ((type) == SEC_ENG_AES_COUNTER_BYTE_1) || \ - ((type) == SEC_ENG_AES_COUNTER_BYTE_2) || \ - ((type) == SEC_ENG_AES_COUNTER_BYTE_3)) +#define IS_SEC_ENG_AES_COUNTER_TYPE(type) \ + (((type) == SEC_ENG_AES_COUNTER_BYTE_4) || ((type) == SEC_ENG_AES_COUNTER_BYTE_1) || ((type) == SEC_ENG_AES_COUNTER_BYTE_2) || ((type) == SEC_ENG_AES_COUNTER_BYTE_3)) /** @defgroup SEC_ENG_AES_VALUEUSED_TYPE * @{ */ -#define IS_SEC_ENG_AES_VALUEUSED_TYPE(type) (((type) == SEC_ENG_AES_USE_NEW) || \ - ((type) == SEC_ENG_AES_USE_OLD)) +#define IS_SEC_ENG_AES_VALUEUSED_TYPE(type) (((type) == SEC_ENG_AES_USE_NEW) || ((type) == SEC_ENG_AES_USE_OLD)) /** @defgroup SEC_ENG_AES_KEY_SRC_TYPE * @{ */ -#define IS_SEC_ENG_AES_KEY_SRC_TYPE(type) (((type) == SEC_ENG_AES_KEY_SW) || \ - ((type) == SEC_ENG_AES_KEY_HW)) +#define IS_SEC_ENG_AES_KEY_SRC_TYPE(type) (((type) == SEC_ENG_AES_KEY_SW) || ((type) == SEC_ENG_AES_KEY_HW)) /** @defgroup SEC_ENG_AES_ENDEC_TYPE * @{ */ -#define IS_SEC_ENG_AES_ENDEC_TYPE(type) (((type) == SEC_ENG_AES_ENCRYPTION) || \ - ((type) == SEC_ENG_AES_DECRYPTION)) +#define IS_SEC_ENG_AES_ENDEC_TYPE(type) (((type) == SEC_ENG_AES_ENCRYPTION) || ((type) == SEC_ENG_AES_DECRYPTION)) /** @defgroup SEC_ENG_PKA_REG_SIZE_TYPE * @{ */ -#define IS_SEC_ENG_PKA_REG_SIZE_TYPE(type) (((type) == SEC_ENG_PKA_REG_SIZE_8) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_16) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_32) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_64) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_96) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_128) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_192) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_256) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_384) || \ - ((type) == SEC_ENG_PKA_REG_SIZE_512)) +#define IS_SEC_ENG_PKA_REG_SIZE_TYPE(type) \ + (((type) == SEC_ENG_PKA_REG_SIZE_8) || ((type) == SEC_ENG_PKA_REG_SIZE_16) || ((type) == SEC_ENG_PKA_REG_SIZE_32) || ((type) == SEC_ENG_PKA_REG_SIZE_64) || ((type) == SEC_ENG_PKA_REG_SIZE_96) \ + || ((type) == SEC_ENG_PKA_REG_SIZE_128) || ((type) == SEC_ENG_PKA_REG_SIZE_192) || ((type) == SEC_ENG_PKA_REG_SIZE_256) || ((type) == SEC_ENG_PKA_REG_SIZE_384) \ + || ((type) == SEC_ENG_PKA_REG_SIZE_512)) /** @defgroup SEC_ENG_PKA_OP_TYPE * @{ */ -#define IS_SEC_ENG_PKA_OP_TYPE(type) (((type) == SEC_ENG_PKA_OP_PPSEL) || \ - ((type) == SEC_ENG_PKA_OP_MOD2N) || \ - ((type) == SEC_ENG_PKA_OP_LDIV2N) || \ - ((type) == SEC_ENG_PKA_OP_LMUL2N) || \ - ((type) == SEC_ENG_PKA_OP_LDIV) || \ - ((type) == SEC_ENG_PKA_OP_LSQR) || \ - ((type) == SEC_ENG_PKA_OP_LMUL) || \ - ((type) == SEC_ENG_PKA_OP_LSUB) || \ - ((type) == SEC_ENG_PKA_OP_LADD) || \ - ((type) == SEC_ENG_PKA_OP_LCMP) || \ - ((type) == SEC_ENG_PKA_OP_MDIV2) || \ - ((type) == SEC_ENG_PKA_OP_MINV) || \ - ((type) == SEC_ENG_PKA_OP_MEXP) || \ - ((type) == SEC_ENG_PKA_OP_MSQR) || \ - ((type) == SEC_ENG_PKA_OP_MMUL) || \ - ((type) == SEC_ENG_PKA_OP_MREM) || \ - ((type) == SEC_ENG_PKA_OP_MSUB) || \ - ((type) == SEC_ENG_PKA_OP_MADD) || \ - ((type) == SEC_ENG_PKA_OP_RESIZE) || \ - ((type) == SEC_ENG_PKA_OP_MOVDAT) || \ - ((type) == SEC_ENG_PKA_OP_NLIR) || \ - ((type) == SEC_ENG_PKA_OP_SLIR) || \ - ((type) == SEC_ENG_PKA_OP_CLIR) || \ - ((type) == SEC_ENG_PKA_OP_CFLIRI_BUFFER) || \ - ((type) == SEC_ENG_PKA_OP_CTLIRI_PLD) || \ - ((type) == SEC_ENG_PKA_OP_CFLIR_BUFFER) || \ - ((type) == SEC_ENG_PKA_OP_CTLIR_PLD)) +#define IS_SEC_ENG_PKA_OP_TYPE(type) \ + (((type) == SEC_ENG_PKA_OP_PPSEL) || ((type) == SEC_ENG_PKA_OP_MOD2N) || ((type) == SEC_ENG_PKA_OP_LDIV2N) || ((type) == SEC_ENG_PKA_OP_LMUL2N) || ((type) == SEC_ENG_PKA_OP_LDIV) \ + || ((type) == SEC_ENG_PKA_OP_LSQR) || ((type) == SEC_ENG_PKA_OP_LMUL) || ((type) == SEC_ENG_PKA_OP_LSUB) || ((type) == SEC_ENG_PKA_OP_LADD) || ((type) == SEC_ENG_PKA_OP_LCMP) \ + || ((type) == SEC_ENG_PKA_OP_MDIV2) || ((type) == SEC_ENG_PKA_OP_MINV) || ((type) == SEC_ENG_PKA_OP_MEXP) || ((type) == SEC_ENG_PKA_OP_MSQR) || ((type) == SEC_ENG_PKA_OP_MMUL) \ + || ((type) == SEC_ENG_PKA_OP_MREM) || ((type) == SEC_ENG_PKA_OP_MSUB) || ((type) == SEC_ENG_PKA_OP_MADD) || ((type) == SEC_ENG_PKA_OP_RESIZE) || ((type) == SEC_ENG_PKA_OP_MOVDAT) \ + || ((type) == SEC_ENG_PKA_OP_NLIR) || ((type) == SEC_ENG_PKA_OP_SLIR) || ((type) == SEC_ENG_PKA_OP_CLIR) || ((type) == SEC_ENG_PKA_OP_CFLIRI_BUFFER) || ((type) == SEC_ENG_PKA_OP_CTLIRI_PLD) \ + || ((type) == SEC_ENG_PKA_OP_CFLIR_BUFFER) || ((type) == SEC_ENG_PKA_OP_CTLIR_PLD)) /** @defgroup SEC_ENG_INT_TYPE * @{ */ -#define IS_SEC_ENG_INT_TYPE(type) (((type) == SEC_ENG_INT_TRNG) || \ - ((type) == SEC_ENG_INT_AES) || \ - ((type) == SEC_ENG_INT_SHA) || \ - ((type) == SEC_ENG_INT_PKA) || \ - ((type) == SEC_ENG_INT_CDET) || \ - ((type) == SEC_ENG_INT_GMAC) || \ - ((type) == SEC_ENG_INT_ALL)) +#define IS_SEC_ENG_INT_TYPE(type) \ + (((type) == SEC_ENG_INT_TRNG) || ((type) == SEC_ENG_INT_AES) || ((type) == SEC_ENG_INT_SHA) || ((type) == SEC_ENG_INT_PKA) || ((type) == SEC_ENG_INT_CDET) || ((type) == SEC_ENG_INT_GMAC) \ + || ((type) == SEC_ENG_INT_ALL)) /*@} end of group SEC_ENG_Public_Constants */ @@ -453,118 +406,74 @@ void SEC_PKA_IRQHandler(void); void SEC_AES_IRQHandler(void); void SEC_SHA_IRQHandler(void); #endif -void Sec_Eng_SHA256_Init(SEC_Eng_SHA256_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, SEC_ENG_SHA_Type type, - uint32_t shaTmpBuf[16], - uint32_t padding[16]); -void Sec_Eng_SHA_Start(SEC_ENG_SHA_ID_Type shaNo); -BL_Err_Type Sec_Eng_SHA256_Update(SEC_Eng_SHA256_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, const uint8_t *input, - uint32_t len); +void Sec_Eng_SHA256_Init(SEC_Eng_SHA256_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, SEC_ENG_SHA_Type type, uint32_t shaTmpBuf[16], uint32_t padding[16]); +void Sec_Eng_SHA_Start(SEC_ENG_SHA_ID_Type shaNo); +BL_Err_Type Sec_Eng_SHA256_Update(SEC_Eng_SHA256_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, const uint8_t *input, uint32_t len); BL_Err_Type Sec_Eng_SHA256_Finish(SEC_Eng_SHA256_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, uint8_t *hash); -void Sec_Eng_SHA_Enable_Link(SEC_ENG_SHA_ID_Type shaNo); -void Sec_Eng_SHA_Disable_Link(SEC_ENG_SHA_ID_Type shaNo); -void Sec_Eng_SHA256_Link_Init(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, uint32_t linkAddr, - uint32_t shaTmpBuf[16], uint32_t padding[16]); -BL_Err_Type Sec_Eng_SHA256_Link_Update(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, - const uint8_t *input, uint32_t len); -BL_Err_Type Sec_Eng_SHA256_Link_Finish(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, - uint8_t *hash); -BL_Err_Type Sec_Eng_AES_Init(SEC_Eng_AES_Ctx *aesCtx, SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Type aesType, - SEC_ENG_AES_Key_Type keyType, SEC_ENG_AES_EnDec_Type enDecType); -void Sec_Eng_AES_Enable_LE(SEC_ENG_AES_ID_Type aesNo); -void Sec_Eng_AES_Enable_BE(SEC_ENG_AES_ID_Type aesNo); -void Sec_Eng_AES_Enable_Link(SEC_ENG_AES_ID_Type aesNo); -void Sec_Eng_AES_Disable_Link(SEC_ENG_AES_ID_Type aesNo); -BL_Err_Type Sec_Eng_AES_Link_Work(SEC_ENG_AES_ID_Type aesNo, uint32_t linkAddr, const uint8_t *in, uint32_t len, - uint8_t *out); -void Sec_Eng_AES_Set_Hw_Key_Src(SEC_ENG_AES_ID_Type aesNo, uint8_t src); -void Sec_Eng_AES_Set_Key_IV(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Key_Src_Type keySrc, const uint8_t *key, - const uint8_t *iv); -void Sec_Eng_AES_Set_Key_IV_BE(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Key_Src_Type keySrc, const uint8_t *key, - const uint8_t *iv); -void Sec_Eng_AES_Set_Counter_Byte(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Counter_Type counterType); -BL_Err_Type Sec_Eng_AES_Crypt(SEC_Eng_AES_Ctx *aesCtx, SEC_ENG_AES_ID_Type aesNo, const uint8_t *in, uint32_t len, - uint8_t *out); +void Sec_Eng_SHA_Enable_Link(SEC_ENG_SHA_ID_Type shaNo); +void Sec_Eng_SHA_Disable_Link(SEC_ENG_SHA_ID_Type shaNo); +void Sec_Eng_SHA256_Link_Init(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, uint32_t linkAddr, uint32_t shaTmpBuf[16], uint32_t padding[16]); +BL_Err_Type Sec_Eng_SHA256_Link_Update(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, const uint8_t *input, uint32_t len); +BL_Err_Type Sec_Eng_SHA256_Link_Finish(SEC_Eng_SHA256_Link_Ctx *shaCtx, SEC_ENG_SHA_ID_Type shaNo, uint8_t *hash); +BL_Err_Type Sec_Eng_AES_Init(SEC_Eng_AES_Ctx *aesCtx, SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Type aesType, SEC_ENG_AES_Key_Type keyType, SEC_ENG_AES_EnDec_Type enDecType); +void Sec_Eng_AES_Enable_LE(SEC_ENG_AES_ID_Type aesNo); +void Sec_Eng_AES_Enable_BE(SEC_ENG_AES_ID_Type aesNo); +void Sec_Eng_AES_Enable_Link(SEC_ENG_AES_ID_Type aesNo); +void Sec_Eng_AES_Disable_Link(SEC_ENG_AES_ID_Type aesNo); +BL_Err_Type Sec_Eng_AES_Link_Work(SEC_ENG_AES_ID_Type aesNo, uint32_t linkAddr, const uint8_t *in, uint32_t len, uint8_t *out); +void Sec_Eng_AES_Set_Hw_Key_Src(SEC_ENG_AES_ID_Type aesNo, uint8_t src); +void Sec_Eng_AES_Set_Key_IV(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Key_Src_Type keySrc, const uint8_t *key, const uint8_t *iv); +void Sec_Eng_AES_Set_Key_IV_BE(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Key_Src_Type keySrc, const uint8_t *key, const uint8_t *iv); +void Sec_Eng_AES_Set_Counter_Byte(SEC_ENG_AES_ID_Type aesNo, SEC_ENG_AES_Counter_Type counterType); +BL_Err_Type Sec_Eng_AES_Crypt(SEC_Eng_AES_Ctx *aesCtx, SEC_ENG_AES_ID_Type aesNo, const uint8_t *in, uint32_t len, uint8_t *out); BL_Err_Type Sec_Eng_AES_Finish(SEC_ENG_AES_ID_Type aesNo); BL_Err_Type Sec_Eng_Trng_Enable(void); -void Sec_Eng_Trng_Int_Enable(void); -void Sec_Eng_Trng_Int_Disable(void); +void Sec_Eng_Trng_Int_Enable(void); +void Sec_Eng_Trng_Int_Disable(void); BL_Err_Type Sec_Eng_Trng_Read(uint8_t data[32]); BL_Err_Type Sec_Eng_Trng_Get_Random(uint8_t *data, uint32_t len); -void Sec_Eng_Trng_Int_Read_Trigger(void); -void Sec_Eng_Trng_Int_Read(uint8_t data[32]); -void Sec_Eng_Trng_Disable(void); -void Sec_Eng_PKA_Reset(void); -void Sec_Eng_PKA_BigEndian_Enable(void); -void Sec_Eng_PKA_LittleEndian_Enable(void); -void Sec_Eng_PKA_GetStatus(SEC_Eng_PKA_Status_Type *status); -void Sec_Eng_PKA_Write_Data(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIndex, const uint32_t *data, uint16_t size, - uint8_t lastOp); -void Sec_Eng_PKA_Read_Data(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIdx, uint32_t *result, uint8_t retSize); -void Sec_Eng_PKA_CREG(SEC_ENG_PKA_REG_SIZE_Type dRegType, uint8_t dRegIdx, uint8_t size, uint8_t lastOp); -void Sec_Eng_PKA_Write_Immediate(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIndex, uint32_t data, uint8_t lastOp); -void Sec_Eng_PKA_NREG(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_Move_Data(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_RESIZE(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MADD(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MSUB(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MREM(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s2RegType, - uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MMUL(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MSQR(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s2RegType, - uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MEXP(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MINV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s2RegType, - uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_MINV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s2RegType, - uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LCMP(uint8_t *cout, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx); -void Sec_Eng_PKA_LADD(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LSUB(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LMUL(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s1RegType, - uint8_t s1RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LSQR(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LDIV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint8_t s2RegType, - uint8_t s2RegIdx, uint8_t lastOp); -void Sec_Eng_PKA_LMUL2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint16_t bit_shift, - uint8_t lastOp); -void Sec_Eng_PKA_LDIV2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint16_t bit_shift, - uint8_t lastOp); -void Sec_Eng_PKA_LMOD2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, - uint16_t bit_shift, - uint8_t lastOp); -void Sec_Eng_PKA_GF2Mont(uint8_t dRegType, uint8_t dRegIdx, uint8_t sRegType, uint8_t sRegIdx, uint32_t size, - uint8_t tRegType, uint8_t tRegIdx, uint8_t pRegType, uint8_t pRegIdx); -void Sec_Eng_PKA_Mont2GF(uint8_t dRegType, uint8_t dRegIdx, uint8_t aRegType, uint8_t aRegIdx, uint8_t invrRegType, - uint8_t invrRegIdx, - uint8_t tRegType, uint8_t tRegIdx, uint8_t pRegType, uint8_t pRegIdx); -void Sec_Eng_GMAC_Enable_LE(void); -void Sec_Eng_GMAC_Enable_BE(void); -void Sec_Eng_GMAC_Enable_Link(void); -void Sec_Eng_GMAC_Disable_Link(void); +void Sec_Eng_Trng_Int_Read_Trigger(void); +void Sec_Eng_Trng_Int_Read(uint8_t data[32]); +void Sec_Eng_Trng_Disable(void); +void Sec_Eng_PKA_Reset(void); +void Sec_Eng_PKA_BigEndian_Enable(void); +void Sec_Eng_PKA_LittleEndian_Enable(void); +void Sec_Eng_PKA_GetStatus(SEC_Eng_PKA_Status_Type *status); +void Sec_Eng_PKA_Write_Data(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIndex, const uint32_t *data, uint16_t size, uint8_t lastOp); +void Sec_Eng_PKA_Read_Data(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIdx, uint32_t *result, uint8_t retSize); +void Sec_Eng_PKA_CREG(SEC_ENG_PKA_REG_SIZE_Type dRegType, uint8_t dRegIdx, uint8_t size, uint8_t lastOp); +void Sec_Eng_PKA_Write_Immediate(SEC_ENG_PKA_REG_SIZE_Type regType, uint8_t regIndex, uint32_t data, uint8_t lastOp); +void Sec_Eng_PKA_NREG(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_Move_Data(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_RESIZE(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MADD(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MSUB(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MREM(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MMUL(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MSQR(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MEXP(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MINV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_MINV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LCMP(uint8_t *cout, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx); +void Sec_Eng_PKA_LADD(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LSUB(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LMUL(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s1RegType, uint8_t s1RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LSQR(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LDIV(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint8_t s2RegType, uint8_t s2RegIdx, uint8_t lastOp); +void Sec_Eng_PKA_LMUL2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint16_t bit_shift, uint8_t lastOp); +void Sec_Eng_PKA_LDIV2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint16_t bit_shift, uint8_t lastOp); +void Sec_Eng_PKA_LMOD2N(uint8_t dRegType, uint8_t dRegIdx, uint8_t s0RegType, uint8_t s0RegIdx, uint16_t bit_shift, uint8_t lastOp); +void Sec_Eng_PKA_GF2Mont(uint8_t dRegType, uint8_t dRegIdx, uint8_t sRegType, uint8_t sRegIdx, uint32_t size, uint8_t tRegType, uint8_t tRegIdx, uint8_t pRegType, uint8_t pRegIdx); +void Sec_Eng_PKA_Mont2GF(uint8_t dRegType, uint8_t dRegIdx, uint8_t aRegType, uint8_t aRegIdx, uint8_t invrRegType, uint8_t invrRegIdx, uint8_t tRegType, uint8_t tRegIdx, uint8_t pRegType, + uint8_t pRegIdx); +void Sec_Eng_GMAC_Enable_LE(void); +void Sec_Eng_GMAC_Enable_BE(void); +void Sec_Eng_GMAC_Enable_Link(void); +void Sec_Eng_GMAC_Disable_Link(void); BL_Err_Type Sec_Eng_GMAC_Link_Work(uint32_t linkAddr, const uint8_t *in, uint32_t len, uint8_t *out); -void SEC_Eng_IntMask(SEC_ENG_INT_Type intType, BL_Mask_Type intMask); -void SEC_Eng_ClrIntStatus(SEC_ENG_INT_Type intType); -void SEC_Eng_Int_Callback_Install(SEC_ENG_INT_Type intType, intCallback_Type *cbFun); +void SEC_Eng_IntMask(SEC_ENG_INT_Type intType, BL_Mask_Type intMask); +void SEC_Eng_ClrIntStatus(SEC_ENG_INT_Type intType); +void SEC_Eng_Int_Callback_Install(SEC_ENG_INT_Type intType, intCallback_Type *cbFun); BL_Sts_Type SEC_Eng_GetIntStatus(SEC_ENG_INT_Type intType); /*----------*/ void SEC_Eng_Turn_On_Sec_Ring(void); @@ -578,3 +487,6 @@ void SEC_Eng_Turn_Off_Sec_Ring(void); /*@} end of group BL702_Peripheral_Driver */ #endif /* __BL702_SEC_ENG_H__ */ +#ifdef __cplusplus +} +#endif \ No newline at end of file From dd0ac6d5efec0d0cf528e09c6b702694377c9f71 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 22:37:49 +1100 Subject: [PATCH 019/211] Update ble.c --- source/Core/BSP/Pinecilv2/ble.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/Core/BSP/Pinecilv2/ble.c b/source/Core/BSP/Pinecilv2/ble.c index ae32c231c0..833a66927d 100644 --- a/source/Core/BSP/Pinecilv2/ble.c +++ b/source/Core/BSP/Pinecilv2/ble.c @@ -49,9 +49,14 @@ int ble_start_adv(void) void bt_enable_cb(int err) { + MSG("[OS] bt_enable_cb...\r\n"); + MSG("[OS] ble_tp_init...\r\n"); ble_tp_init(); + MSG("[OS] ble_tp_init...Done\r\n"); + MSG("[OS] ble_start_adv...\r\n"); ble_start_adv(); + MSG("[OS] ble_start_adv...Done\r\n"); } From e594768e7f0cfbb21f4608e18db9615df8731d99 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 22:46:09 +1100 Subject: [PATCH 020/211] Fix linker --- .../drivers/bl702_driver/bl702_flash.ld | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld index ec1714b7a1..e14b21facb 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld @@ -29,6 +29,7 @@ MEMORY ram_memory (!rx) : ORIGIN = 0x42020000, LENGTH = 32K rsvd_memory (!rx) : ORIGIN = 0x42028000, LENGTH = 1K ram2_memory (!rx) : ORIGIN = 0x42028400, LENGTH = (31K - __EM_SIZE) + hbn_memory (rx) : ORIGIN = 0x40010000, LENGTH = 0xE00 /* hbn ram 4K used 3.5K*/ } SECTIONS @@ -92,6 +93,67 @@ SECTIONS . = ALIGN(4); __text_code_end__ = .; } > xip_memory + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >xip_memory AT>xip_memory + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >xip_memory AT>xip_memory + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >xip_memory AT>xip_memory + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + * the constructors, so we make sure it is + * first. Because this is a wildcard, it + * doesn't matter if the user does not + * actually link against crtbegin.o; the + * linker won't look for a file to match a + * wildcard. The wildcard also means that it + * doesn't matter which directory crtbegin.o + * is in. + */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + * the crtend.o file until after the sorted ctors. + * The .ctor section from the crtend file contains the + * end of ctors marker and it must be last + */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >xip_memory AT>xip_memory + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >xip_memory AT>xip_memory + + .lalign : + { + . = ALIGN(4); + PROVIDE( _data_lma = . ); + } >xip_memory AT>xip_memory . = ALIGN(4); __itcm_load_addr = .; @@ -122,7 +184,20 @@ SECTIONS __tcm_code_end__ = .; } > itcm_memory - __dtcm_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); + __hbn_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); + + .hbn_ram_region : AT (__hbn_load_addr) + { + . = ALIGN(4); + __hbn_ram_start__ = .; + *bl702_hbn_wakeup*.o*(.rodata*) + *(.hbn_ram_code*) + *(.hbn_ram_data) + . = ALIGN(4); + __hbn_ram_end__ = .; + } > hbn_memory + + __dtcm_load_addr = __hbn_load_addr + SIZEOF(.hbn_ram_region); .dtcm_region : AT (__dtcm_load_addr) { From ea7d270146054d9fb42d8a9a6288bfc44a19298e Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 23:15:30 +1100 Subject: [PATCH 021/211] Dump PC in assert failure --- source/Core/BSP/Pinecilv2/Pins.h | 1 + source/Core/BSP/Pinecilv2/Setup.cpp | 9 --------- .../risc-v/Core/Include/riscv_encoding.h | 1 + .../drivers/bl702_driver/startup/interrupt.c | 17 ++++++++++++++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/Pins.h b/source/Core/BSP/Pinecilv2/Pins.h index 3baf060c77..6037c1f92c 100644 --- a/source/Core/BSP/Pinecilv2/Pins.h +++ b/source/Core/BSP/Pinecilv2/Pins.h @@ -8,6 +8,7 @@ #ifndef BSP_PINE64_PINS_H_ #define BSP_PINE64_PINS_H_ #include "bl702_adc.h" +#include "bl702_pwm.h" #include "hal_gpio.h" #define KEY_B_Pin GPIO_PIN_28 diff --git a/source/Core/BSP/Pinecilv2/Setup.cpp b/source/Core/BSP/Pinecilv2/Setup.cpp index 26f7872c39..d3775aead7 100644 --- a/source/Core/BSP/Pinecilv2/Setup.cpp +++ b/source/Core/BSP/Pinecilv2/Setup.cpp @@ -159,12 +159,3 @@ void setupFUSBIRQ() { CPU_Interrupt_Enable(GPIO_INT0_IRQn); gpio_irq_enable(FUSB302_IRQ_Pin, ENABLE); } - -void vAssertCalled(void) { - MSG((char *)"vAssertCalled\r\n"); - PWM_Channel_Disable(PWM_Channel); - gpio_set_mode(PWM_Out_Pin, GPIO_INPUT_PD_MODE); - - while (1) - ; -} diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/risc-v/Core/Include/riscv_encoding.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/risc-v/Core/Include/riscv_encoding.h index 4987ac2653..973ec86c9c 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/risc-v/Core/Include/riscv_encoding.h +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/risc-v/Core/Include/riscv_encoding.h @@ -221,6 +221,7 @@ #define rdtime() read_csr(time) #define rdcycle() read_csr(cycle) #define rdinstret() read_csr(instret) +#define get_pc() read_csr(mepc) #endif diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/interrupt.c b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/interrupt.c index e23b18d2a4..65ef20a26d 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/interrupt.c +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/startup/interrupt.c @@ -173,6 +173,21 @@ const pFunc __Vectors[] __attribute__((section(".init"), aligned(64))) = { WIFI_IPC_PUBLIC_IRQHandler_Wrapper, /* 16 + 63 */ }; +#include "riscv_encoding.h" +void vAssertCalled(void) { + MSG((char *)"vAssertCalled\r\n"); + unsigned long epc; + epc = get_pc(); + MSG("mepc:0x%08x\r\n", (uint32_t)epc); + +// PWM_Channel_Disable(PWM_Channel); +// gpio_set_mode(PWM_Out_Pin, GPIO_INPUT_PD_MODE); + + while (1) + ; +} + + void Trap_Handler(void) { unsigned long cause; @@ -184,7 +199,7 @@ void Trap_Handler(void) cause = read_csr(mcause); MSG("mcause=%08x\r\n", (uint32_t)cause); - epc = read_csr(mepc); + epc = get_pc(); MSG("mepc:%08x\r\n", (uint32_t)epc); tval = read_csr(mtval); MSG("mtval:%08x\r\n", (uint32_t)tval); From 4ab33eaee8473775f53d757a3da9a01004a91830 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 23:15:40 +1100 Subject: [PATCH 022/211] Start sec --- source/Core/BSP/Pinecilv2/Setup.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/source/Core/BSP/Pinecilv2/Setup.cpp b/source/Core/BSP/Pinecilv2/Setup.cpp index d3775aead7..cb1b0cb7af 100644 --- a/source/Core/BSP/Pinecilv2/Setup.cpp +++ b/source/Core/BSP/Pinecilv2/Setup.cpp @@ -8,9 +8,9 @@ #include "BSP.h" #include "Debug.h" #include "FreeRTOSConfig.h" -#include "Pins.h" - #include "IRQ.h" +#include "Pins.h" +#include "bl702_sec_eng.h" #include "history.hpp" #include #define ADC_NORM_SAMPLES 16 @@ -35,6 +35,18 @@ void setup_adc(void); void hardware_init() { vPortDefineHeapRegions(xHeapRegions); + HBN_Set_XCLK_CLK_Sel(HBN_XCLK_CLK_XTAL); + + // Set capcode + { + uint32_t tmpVal = 0; + tmpVal = BL_RD_REG(AON_BASE, AON_XTAL_CFG); + tmpVal = BL_SET_REG_BITS_VAL(tmpVal, AON_XTAL_CAPCODE_IN_AON, 33); + tmpVal = BL_SET_REG_BITS_VAL(tmpVal, AON_XTAL_CAPCODE_OUT_AON, 33); + BL_WR_REG(AON_BASE, AON_XTAL_CFG, tmpVal); + } + + Sec_Eng_Trng_Enable(); gpio_set_mode(OLED_RESET_Pin, GPIO_OUTPUT_MODE); gpio_set_mode(KEY_A_Pin, GPIO_INPUT_PD_MODE); From 8b85febf8e8df175bbf1e6459f1a9eb7ce8c858c Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 23:15:43 +1100 Subject: [PATCH 023/211] Update ble_config.h --- .../components/ble/ble_stack/port/include/ble_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h index 073c9401f8..278219af08 100644 --- a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h +++ b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/ble_stack/port/include/ble_config.h @@ -607,7 +607,7 @@ 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(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_OBSERVER) -#define BFLB_BLE_NOTIFY_ADV_DISCARDED +// #define BFLB_BLE_NOTIFY_ADV_DISCARDED #endif #if defined(__cplusplus) } From 3f05088ffb15bb172b455aec718e1fa9ed75202e Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 22 Oct 2022 23:15:47 +1100 Subject: [PATCH 024/211] Update Makefile --- source/Makefile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/Makefile b/source/Makefile index ac990d4f2e..782f2f5a4d 100644 --- a/source/Makefile +++ b/source/Makefile @@ -310,8 +310,9 @@ LDSCRIPT=./Core/BSP/Pinecilv2/bl_mcu_sdk/drivers/bl702_driver/bl702_flash.ld CPUFLAGS= -march=rv32imafc \ -mabi=ilp32f \ -mcmodel=medany -fsigned-char -fno-builtin -nostartfiles \ - -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DBL702 -DBFLB_USE_ROM_DRIVER=1 -DEV_LDFLAGS=-nostartfiles -L $(PINECILV2_BLE_CRAPWARE_BLOB_DIR) -L $(PINECILV2_RF_CRAPWARE_BLOB_DIR) -l blecontroller_702_m0s1 -l bl702_rf + -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler -DARCH_RISCV -D__RISCV_FEATURE_MVE=0 -DBL702 +# -DBFLB_USE_ROM_DRIVER=1 +DEV_LDFLAGS=-nostartfiles -L $(PINECILV2_BLE_CRAPWARE_BLOB_DIR) -L $(PINECILV2_RF_CRAPWARE_BLOB_DIR) -l blecontroller_702_m0s1s -l bl702_rf DEV_AFLAGS= DEV_GLOBAL_DEFS= -DCFG_FREERTOS \ -DARCH_RISCV \ @@ -344,16 +345,16 @@ DEV_GLOBAL_DEFS= -DCFG_FREERTOS \ -DCONFIG_BT_SIGNING \ -DCONFIG_BT_SETTINGS_CCC_LAZY_LOADING \ -DCONFIG_BT_SETTINGS_USE_PRINTK \ +-DCFG_SLEEP \ +-DCONFIG_BT_ALLROLES \ +-DCONFIG_BT_CENTRAL \ +-DCONFIG_BT_OBSERVER \ +-DCONFIG_BT_BROADCASTER \ +-DBFLB_USE_ROM_DRIVER \ -DCFG_BLE_STACK_DBG_PRINT \ -DportasmHANDLE_INTERRUPT=FreeRTOS_Interrupt_Handler # -DBFLB_USE_HAL_DRIVER # -DCONFIG_BT_SMP \ -# -DCFG_SLEEP \ -# -DCONFIG_BT_ALLROLES \ -# -DCONFIG_BT_CENTRAL \ -# -DCONFIG_BT_OBSERVER \ -# -DCONFIG_BT_BROADCASTER \ -# -DBFLB_USE_ROM_DRIVER \ #Required to be turned off due to their drivers tripping warnings From 99df4b330769f076e0e547fa841c48552fb12534 Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sun, 23 Oct 2022 09:13:55 +1100 Subject: [PATCH 025/211] Binary blobs --- .../components/ble/bl702_rf/lib/libbl702_rf.a | Bin 0 -> 224290 bytes .../lib/libblecontroller_702_m0s1s.a | Bin 0 -> 3984406 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/bl702_rf/lib/libbl702_rf.a create mode 100644 source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/lib/libblecontroller_702_m0s1s.a diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/bl702_rf/lib/libbl702_rf.a b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/bl702_rf/lib/libbl702_rf.a new file mode 100644 index 0000000000000000000000000000000000000000..eb99532e441735cb0cf334de8ce8ed7869bb97db GIT binary patch literal 224290 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 literal 0 HcmV?d00001 diff --git a/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/lib/libblecontroller_702_m0s1s.a b/source/Core/BSP/Pinecilv2/bl_mcu_sdk/components/ble/blecontroller/lib/libblecontroller_702_m0s1s.a new file mode 100644 index 0000000000000000000000000000000000000000..8127fefd8b1cce4f8df9d9d5be0581bcfc362a2a GIT binary patch literal 3984406 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�FH)gUNyOXEVP^$i9At*7D$U~o@A+!F5VLhdhH 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

  • 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

    IbQC2o?K`BwBF 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


    (c&rpExejh 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�xF{ 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

    _ZGSuTJ6?I*4}Gb(|0kdy^4iCX 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

    S}{$c_|?3=y$Uo*pZwv=-*&Hup>FAK>sG| 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

    Fi{%(}SHbJDKccv6IbCPj+(H$z`V(J9+Hn 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

    *{CP# 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�TP+L-SDko1Z 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