diff --git a/.github/workflows/python_lint.yml b/.github/workflows/python_lint.yml index b62552fe87..5e36177855 100644 --- a/.github/workflows/python_lint.yml +++ b/.github/workflows/python_lint.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8] steps: - name: Checkout diff --git a/.gitignore b/.gitignore index 61e02dae70..38ebd38bef 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,6 @@ build # lock files for examples and components dependencies.lock + +# managed_components for examples +managed_components diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0607d2b2f9..d1bc5816e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,10 +48,11 @@ variables: # Docker images BOT_DOCKER_IMAGE_TAG: ":latest" - ESP_IDF_DOC_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env:v4.4-1-v3" + ESP_IDF_DOC_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env:v4.4-1-v4" ESP_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-env:v4.4-1" AFL_FUZZER_TEST_IMAGE: "$CI_DOCKER_REGISTRY/afl-fuzzer-test:v4.4-1-1" CLANG_STATIC_ANALYSIS_IMAGE: "${CI_DOCKER_REGISTRY}/clang-static-analysis:v4.4-1-2" + SONARQUBE_SCANNER_IMAGE: "${CI_DOCKER_REGISTRY}/sonarqube-scanner:3" # target test config file, used by assign test job CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/.gitlab/ci/target-test.yml" diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 07f880ebd4..65ba4cb9f7 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -161,6 +161,7 @@ /examples/ethernet/ @esp-idf-codeowners/network /examples/get-started/ @esp-idf-codeowners/system /examples/mesh/ @esp-idf-codeowners/wifi +/examples/network/ @esp-idf-codeowners/network @esp-idf-codeowners/wifi /examples/openthread/ @esp-idf-codeowners/ieee802154 /examples/peripherals/ @esp-idf-codeowners/peripherals /examples/protocols/ @esp-idf-codeowners/network @esp-idf-codeowners/app-utilities diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 93b9672d68..7a70c4aa5c 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -221,6 +221,14 @@ build_examples_cmake_esp32s2: variables: IDF_TARGET: esp32s2 +build_examples_cmake_esp32s3: + extends: + - .build_examples_cmake_template + - .rules:build:example_test-esp32s3 + parallel: 8 + variables: + IDF_TARGET: esp32s3 + build_examples_cmake_esp32c3: extends: - .build_examples_cmake_template diff --git a/.gitlab/ci/docs.yml b/.gitlab/ci/docs.yml index ffe2c0d813..80e627d605 100644 --- a/.gitlab/ci/docs.yml +++ b/.gitlab/ci/docs.yml @@ -66,7 +66,7 @@ check_docs_lang_sync: parallel: matrix: - DOCLANG: ["en", "zh_CN"] - DOCTGT: ["esp32", "esp32s2", "esp32c3"] + DOCTGT: ["esp32", "esp32s2", "esp32s3", "esp32c3"] check_docs_gh_links: image: $ESP_IDF_DOC_ENV_IMAGE @@ -128,13 +128,6 @@ build_docs_html_fast: variables: DOC_BUILDERS: "html" DOCS_FAST_BUILD: "yes" - # matrix is redefined to include esp32s3 here - # that we can build for S3 MRs during bringup phase without - # anything being built and published from master branch - parallel: - matrix: - - DOCLANG: ["en", "zh_CN"] - DOCTGT: ["esp32", "esp32s2", "esp32s3", "esp32c3"] build_docs_pdf: extends: diff --git a/.gitlab/ci/host-test.yml b/.gitlab/ci/host-test.yml index e442285ae5..82be5c54aa 100644 --- a/.gitlab/ci/host-test.yml +++ b/.gitlab/ci/host-test.yml @@ -220,11 +220,6 @@ test_efuse_table_on_host_esp32s2: variables: IDF_TARGET: esp32s2 -test_efuse_table_on_host_esp32s2: - extends: .test_efuse_table_on_host_template - variables: - IDF_TARGET: esp32s2 - test_efuse_table_on_host_esp32s3: extends: .test_efuse_table_on_host_template variables: @@ -250,10 +245,11 @@ test_espcoredump: expire_in: 1 week variables: IDF_COREDUMP_ELF_REPO: "https://gitlab-ci-token:${BOT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/idf/idf-coredump-elf.git" + IDF_COREDUMP_ELF_TAG: idf-20210910-00 # install CMake version specified in tools.json SETUP_TOOLS_LIST: "all" script: - - retry_failed git clone ${IDF_COREDUMP_ELF_REPO} -b master + - retry_failed git clone ${IDF_COREDUMP_ELF_REPO} -b $IDF_COREDUMP_ELF_TAG - cd ${IDF_PATH}/components/espcoredump/test/ - ./test_espcoredump.sh ${CI_PROJECT_DIR}/idf-coredump-elf @@ -367,3 +363,10 @@ test_cxx_gpio: - cd ${IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/host_test/gpio - idf.py build - build/test_gpio_cxx_host.elf + +test_linux_example: + extends: .host_test_template + script: + - cd ${IDF_PATH}/examples/build_system/cmake/linux_host_app + - idf.py build + - build/linux_host_app.elf diff --git a/.gitlab/ci/pre_check.yml b/.gitlab/ci/pre_check.yml index 3ddccfadca..676ef0a44b 100644 --- a/.gitlab/ci/pre_check.yml +++ b/.gitlab/ci/pre_check.yml @@ -113,6 +113,15 @@ check_public_headers: script: - python tools/ci/check_public_headers.py --jobs 4 --prefix xtensa-esp32-elf- +check_soc_struct_headers: + extends: + - .pre_check_base_template + - .rules:build + tags: + - build + script: + - find ${IDF_PATH}/components/soc/*/include/soc/ -name "*_struct.h" -print0 | xargs -0 -n1 ./tools/ci/check_soc_struct_headers.py + check_esp_err_to_name: extends: - .pre_check_base_template diff --git a/.gitlab/ci/static-code-analysis.yml b/.gitlab/ci/static-code-analysis.yml index 74a7f67bb8..cdcabfd758 100644 --- a/.gitlab/ci/static-code-analysis.yml +++ b/.gitlab/ci/static-code-analysis.yml @@ -30,7 +30,7 @@ check_pylint: - .pre_check_base_template - .rules:patterns:python-files - .before_script_minimal - image: $CI_DOCKER_REGISTRY/sonarqube-scanner:2 + image: $SONARQUBE_SCANNER_IMAGE artifacts: when: always paths: @@ -62,11 +62,19 @@ check_pylint: .sonar_scan_template: stage: build image: - name: $CI_DOCKER_REGISTRY/sonarqube-scanner:2 + name: $SONARQUBE_SCANNER_IMAGE before_script: - source tools/ci/utils.sh - export PYTHONPATH="$CI_PROJECT_DIR/tools:$CI_PROJECT_DIR/tools/ci/python_packages:$PYTHONPATH" - fetch_submodules + # Exclude the submodules, all paths ends with /** + - submodules=$(get_all_submodules) + # get all exclude paths specified in tools/ci/sonar_exclude_list.txt | ignore lines start with # | xargs | replace all to + - custom_excludes=$(cat $CI_PROJECT_DIR/tools/ci/sonar_exclude_list.txt | grep -v '^#' | xargs | sed -e 's/ /,/g') + # Exclude the report dir as well + - export EXCLUSIONS="$custom_excludes,$submodules" + - export SONAR_SCANNER_OPTS="-Xmx2048m" + variables: GIT_DEPTH: 0 REPORT_PATTERN: clang_tidy_reports/*.txt @@ -95,8 +103,10 @@ code_quality_check: -Dsonar.analysis.mode=preview -Dsonar.branch.name=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME -Dsonar.cxx.clangtidy.reportPath=$REPORT_PATTERN + -Dsonar.exclusions=$EXCLUSIONS -Dsonar.gitlab.ci_merge_request_iid=$CI_MERGE_REQUEST_IID -Dsonar.gitlab.commit_sha=$CI_MERGE_REQUEST_COMMITS + -Dsonar.gitlab.merge_request_discussion=true -Dsonar.gitlab.ref_name=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN @@ -112,6 +122,7 @@ code_quality_report: - sonar-scanner -Dsonar.branch.name=$CI_COMMIT_REF_NAME -Dsonar.cxx.clangtidy.reportPath=$REPORT_PATTERN + -Dsonar.exclusions=$EXCLUSIONS -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME -Dsonar.host.url=$SONAR_HOST_URL diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 6f420768d4..f9f3c10c90 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -46,7 +46,7 @@ extends: - .example_test_template - .rules:test:example_test-esp32 -variables: + variables: SUBMODULES_TO_FETCH: "all" test_weekend_mqtt: @@ -60,7 +60,7 @@ test_weekend_mqtt: - export MQTT_PUBLISH_TEST=1 - export TEST_PATH=$CI_PROJECT_DIR/tools/test_apps/protocols/mqtt/publish_connect_test - cd $IDF_PATH/tools/ci/python_packages/tiny_test_fw/bin - - run_cmd python Runner.py $TEST_PATH -c $TEST_PATH/publish_connect_mqtt_.yml -e $TEST_PATH/env.yml + - run_cmd python Runner.py $TEST_PATH -c $TEST_PATH/publish_connect_mqtt_.yml .example_test_esp32_template: extends: @@ -129,7 +129,7 @@ example_test_002: - ESP32 - Example_ShieldBox_Basic -example_test_enternet: +example_test_ethernet: extends: .example_test_esp32_template tags: - ESP32 @@ -270,20 +270,17 @@ example_test_ESP32_SDSPI: - ESP32 - UT_T1_SPIMODE -# uncomment when ESP32S2 & ESP32C3 runners with external SD connected over SPI are available -# ensure the runners have required tags created -# -#example_test_ESP32S2_SDSPI: -# extends: .example_test_esp32s2_template -# tags: -# - ESP32S2 -# - UT_T1_SPIMODE -# -#example_test_ESP32C3_SDSPI: -# extends: .example_test_esp32c3_template -# tags: -# - ESP32C3 -# - UT_T1_SPIMODE +example_test_ESP32S2_SDSPI: + extends: .example_test_esp32s2_template + tags: + - ESP32S2 + - UT_T1_SPIMODE + +example_test_ESP32C3_SDSPI: + extends: .example_test_esp32c3_template + tags: + - ESP32C3 + - UT_T1_SPIMODE .test_app_template: extends: .target_test_job_template @@ -482,7 +479,7 @@ UT_006: UT_007: extends: .unit_test_esp32_template - parallel: 3 + parallel: 4 tags: - ESP32_IDF - UT_T1_1 @@ -633,7 +630,7 @@ UT_046: UT_047: extends: .unit_test_esp32s2_template - parallel: 3 + parallel: 5 tags: - ESP32S2_IDF - UT_T1_1 @@ -644,6 +641,12 @@ UT_S2_SPI_DUAL: - ESP32S2_IDF - Example_SPI_Multi_device +UT_S2_SDSPI: + extends: .unit_test_esp32s2_template + tags: + - ESP32S2_IDF + - UT_T1_SPIMODE + UT_C3: extends: .unit_test_esp32c3_template parallel: 32 @@ -682,9 +685,15 @@ UT_C3_FLASH_SUSPEND: - ESP32C3_IDF - UT_T1_Flash_Suspend +UT_C3_SDSPI: + extends: .unit_test_esp32c3_template + tags: + - ESP32C3_IDF + - UT_T1_SPIMODE + UT_S3: extends: .unit_test_esp32s3_template - parallel: 27 + parallel: 29 tags: - ESP32S3_IDF - UT_T1_1 @@ -702,6 +711,18 @@ UT_S3_FLASH: - ESP32S3_IDF - UT_T1_ESP_FLASH +component_ut_test_ip101: + extends: .component_ut_esp32_template + tags: + - ESP32 + - COMPONENT_UT_IP101 + +component_ut_test_lan8720: + extends: .component_ut_esp32_template + tags: + - ESP32 + - COMPONENT_UT_LAN8720 + .integration_test_template: extends: - .target_test_job_template diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 043c533335..0fa2a43527 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - id: trailing-whitespace # note: whitespace exclusions use multiline regex, see https://pre-commit.com/#regular-expressions @@ -26,12 +26,12 @@ repos: args: ['-f=lf'] - id: double-quote-string-fixer - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.2 hooks: - id: flake8 args: ['--config=.flake8', '--tee', '--benchmark'] - repo: https://github.com/pycqa/isort - rev: 5.6.4 + rev: 5.9.3 hooks: - id: isort name: isort (python) @@ -92,11 +92,24 @@ repos: - id: mypy-check name: Check type annotations in python files entry: tools/ci/check_type_comments.py - additional_dependencies: ['mypy==0.800', 'mypy-extensions==0.4.3'] + additional_dependencies: + - 'mypy==0.800' + - 'mypy-extensions==0.4.3' language: python types: [python] + - id: check-copyright + name: Check copyright notices + entry: tools/ci/check_copyright.py --verbose --replace + additional_dependencies: + - 'comment_parser == 1.2.3' + - 'thefuzz == 0.19.0' + - 'thefuzz[speedup] == 0.19.0; sys_platform != "win32"' + # don't depend on python-Levenshtein on Windows, as it requires Microsoft C++ Build Tools to install + language: python + files: \.(py|c|h|cpp|hpp|ld)$ + require_serial: true - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - id: file-contents-sorter - files: '(tools\/ci\/executable-list\.txt|tools\/ci\/mypy_ignore_list\.txt)' + files: 'tools\/ci\/(executable-list\.txt|mypy_ignore_list\.txt|check_copyright_ignore\.txt)' diff --git a/CMakeLists.txt b/CMakeLists.txt index 34e043dd41..f580e8de85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,9 @@ if(NOT BOOTLOADER_BUILD) if(CONFIG_COMPILER_OPTIMIZATION_SIZE) list(APPEND compile_options "-Os") - list(APPEND compile_options "-freorder-blocks") + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND compile_options "-freorder-blocks") + endif() elseif(CONFIG_COMPILER_OPTIMIZATION_DEFAULT) list(APPEND compile_options "-Og") elseif(CONFIG_COMPILER_OPTIMIZATION_NONE) @@ -33,7 +35,9 @@ else() # BOOTLOADER_BUILD if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE) list(APPEND compile_options "-Os") - list(APPEND compile_options "-freorder-blocks") + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND compile_options "-freorder-blocks") + endif() elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG) list(APPEND compile_options "-Og") elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE) @@ -74,6 +78,48 @@ if(CONFIG_COMPILER_DISABLE_GCC8_WARNINGS) "-Wno-int-in-bool-context") endif() +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + list(APPEND c_compile_options "-Wno-old-style-declaration") +endif() + +# Clang finds some warnings in IDF code which GCC doesn't. +# All these warnings should be fixed before Clang is presented +# as a toolchain choice for users. +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + # Clang checks Doxygen comments for being in sync with function prototype. + # There are some inconsistencies, especially in ROM headers. + list(APPEND compile_options "-Wno-documentation") + # GCC allows repeated typedefs when the source and target types are the same. + # Clang doesn't allow this. This occurs in many components due to forward + # declarations. + list(APPEND compile_options "-Wno-typedef-redefinition") + # This issue is seemingly related to newlib's char type functions. + # Fix is not clear yet. + list(APPEND compile_options "-Wno-char-subscripts") + # Clang seems to notice format string issues which GCC doesn't. + list(APPEND compile_options "-Wno-format-security") + # Logic bug in essl component + list(APPEND compile_options "-Wno-tautological-overlap-compare") + # Some pointer checks in mDNS component check addresses which can't be NULL + list(APPEND compile_options "-Wno-tautological-pointer-compare") + # Similar to the above, in tcp_transport + list(APPEND compile_options "-Wno-pointer-bool-conversion") + # mbedTLS md5.c triggers this warning in md5_test_buf (false positive) + list(APPEND compile_options "-Wno-string-concatenation") + # multiple cases of implict convertions between unrelated enum types + list(APPEND compile_options "-Wno-enum-conversion") + # When IRAM_ATTR is specified both in function declaration and definition, + # it produces different section names, since section names include __COUNTER__. + # Occurs in multiple places. + list(APPEND compile_options "-Wno-section") + # Multiple cases of attributes unknown to clang, for example + # __attribute__((optimize("-O3"))) + list(APPEND compile_options "-Wno-unknown-attributes") + # Clang also produces many -Wunused-function warnings which GCC doesn't. + # However these aren't treated as errors. +endif() +# More warnings may exist in unit tests and example projects. + if(CONFIG_COMPILER_WARN_WRITE_STRINGS) list(APPEND compile_options "-Wwrite-strings") endif() @@ -118,7 +164,15 @@ list(APPEND link_options "-fno-lto") # Placing jump tables in flash would cause issues with code that required # to be placed in IRAM list(APPEND compile_options "-fno-jump-tables") -list(APPEND compile_options "-fno-tree-switch-conversion") +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + # This flag is GCC-specific. + # Not clear yet if some other flag should be used for Clang. + list(APPEND compile_options "-fno-tree-switch-conversion") +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "LLVM") + list(APPEND compile_options "-fno-use-cxa-atexit") +endif() idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND) diff --git a/Kconfig b/Kconfig index 942837a0ea..65d03788c9 100644 --- a/Kconfig +++ b/Kconfig @@ -11,7 +11,6 @@ mainmenu "Espressif IoT Development Framework Configuration" config IDF_ENV_FPGA # This option is for internal use only bool - default "y" if IDF_TARGET="esp32h2" # ESP32H2-TODO: IDF-3378 option env="IDF_ENV_FPGA" config IDF_TARGET_ARCH_RISCV diff --git a/README.md b/README.md index 10027f474b..73b855e398 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,24 @@ * [中文版](./README_CN.md) -ESP-IDF is the development framework for Espressif SoCs (released after 2016[1](#fn1)) provided for Windows, Linux and macOS. +ESP-IDF is the development framework for Espressif SoCs supported on Windows, Linux and macOS. + +# ESP-IDF Release and SoC Compatibility + +The following table shows ESP-IDF support of Espressif SoCs where ![alt text][preview] and ![alt text][supported] denote preview status and support, respectively. In preview status the build is not yet enabled and some crucial parts could be missing (like documentation, datasheet). Please use an ESP-IDF release where the desired SoC is already supported. + +|Chip | v3.3 | v4.0 | v4.1 | v4.2 | v4.3 | v4.4 | | +|:----------- |:---------------------: | :---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:---------------------------------------------------------- | +|ESP32 | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | | +|ESP32-S2 | | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | | +|ESP32-C3 | | | | | ![alt text][supported] | ![alt text][supported] | | +|ESP32-S3 | | | | | ![alt text][preview] | ![alt text][supported] | [Announcement](https://www.espressif.com/en/news/ESP32_S3) | +|ESP32-H2 | | | | | | ![alt text][preview] | [Announcement](https://www.espressif.com/en/news/ESP32_H2) | + +[supported]: https://img.shields.io/badge/-supported-green "supported" +[preview]: https://img.shields.io/badge/-preview-orange "preview" + +Espressif SoCs released before 2016 (ESP8266 and ESP8285) are supported by [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead. # Developing With ESP-IDF @@ -99,8 +116,3 @@ This can be combined with other targets, ie `idf.py -p PORT erase_flash flash` w * [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one. * If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html). - - -________ - -1: ESP8266 and ESP8285 are not supported in ESP-IDF. See [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead. \ No newline at end of file diff --git a/README_CN.md b/README_CN.md index 5408040576..123bea47ba 100644 --- a/README_CN.md +++ b/README_CN.md @@ -2,7 +2,24 @@ * [English Version](./README.md) -ESP-IDF 是乐鑫官方推出的开发框架,适用于 2016 年之后发布的系列芯片[1](#fn1),支持 Windows、Linux 和 macOS 操作系统。 +ESP-IDF 是乐鑫官方推出的物联网开发框架,支持 Windows、Linux 和 macOS 操作系统。 + +# ESP-IDF 与乐鑫芯片 + +下表总结了乐鑫芯片在 ESP-IDF 各版本中的支持状态,其中 ![alt text][supported] 代表已支持,![alt text][preview] 代表目前处于预览支持状态。在预览支持阶段,因为新芯片尚未完全添加到构建系统目录,所以一些重要的内容(如文档和技术规格书等)可能会缺失。请确保使用与芯片相匹配的 ESP-IDF 版本。 + +| 芯片 | v3.3 | v4.0 | v4.1 | v4.2 | v4.3 | v4.4 | | +|:----------- |:---------------------: | :---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:---------------------------------------------------------- | +|ESP32 | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | | +|ESP32-S2 | | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | | +|ESP32-C3 | | | | | ![alt text][supported] | ![alt text][supported] | | +|ESP32-S3 | | | | | ![alt text][preview] | ![alt text][supported] | [芯片发布公告](https://www.espressif.com/en/news/ESP32_S3) | +|ESP32-H2 | | | | | | ![alt text][preview] | [芯片发布公告](https://www.espressif.com/en/news/ESP32_H2) | + +[supported]: https://img.shields.io/badge/-%E6%94%AF%E6%8C%81-green "supported" +[preview]: https://img.shields.io/badge/-%E9%A2%84%E8%A7%88-orange "preview" + +对于 2016 年之前发布的乐鑫芯片(包括 ESP8266 和 ESP8285),请参考 [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK)。 # 使用 ESP-IDF 进行开发 @@ -99,8 +116,3 @@ ESP-IDF 中的子模块采用相对路径([详见 .gitmodules 文件](.gitmodu * 如果你在使用中发现了错误或者需要新的功能,请先[查看 GitHub Issues](https://github.com/espressif/esp-idf/issues),确保该问题没有重复提交。 * 如果你有兴趣为 ESP-IDF 作贡献,请先阅读[贡献指南](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html)。 - - -__________ - -1: ESP-IDF 不支持 ESP8266 和 ESP8285。如有需要,请参考 [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK)。 diff --git a/components/README.md b/components/README.md new file mode 100644 index 0000000000..c4c33d8674 --- /dev/null +++ b/components/README.md @@ -0,0 +1,160 @@ +# Core Components + +## Overview + +This document contains details about what the core components are, what they contain, and how they are organized. + +## Organization + +The core components are organized into two groups. + +The first group (referred to as `G0` from now on) contains `hal`, `xtensa` and `riscv` (referred to as `arch` components from now on), `esp_rom`, `esp_common`, and `soc`. This +group contain information about and low-level access to underlying hardware; or in the case of `esp_common`, hardware-agnostic code and utilities. +These components can depend on each other, but as much as possible have no dependencies outside the group. The reason for this is that, due to the +nature of what these components contain, the likelihood is high that a lot of other components will require these. Ideally, then, the dependency +relationship only goes one way. This makes it easier for these components, as a group, to be usable in another project. One can conceivably implement +a competing SDK to ESP-IDF on top of these components. + +The second group (referred to as `G1` from now on) sits at a higher level than the first group. This group contains the components `esp_hw_support`, `esp_system`, `newlib`, `spi_flash`, +`freertos`, `log`, and `heap`. Like the first group, circular dependencies within the group are allowed; and being at a higher level, dependency on the first group +is allowed. These components represent software mechanisms essential to building other components. + +## Descriptions + +The following is a short description of the components mentioned above. + +### `G0` Components + +#### `hal` + +Contains the hardware abstraction layer and low-level operation implementations for the various peripherals. The low-level functions assign meaningful names to register-level manipulations; the hardware abstraction provide operations one level above this, grouping these low-level functions +into routines that achieve a meaningful action or state of the peripheral. + +Example: + +- `spi_flash_ll_set_address` is a low-level function part of the hardware abstraction `spi_flash_hal_read_block` + +#### `arch` + +Contains low-level architecture operations and definitions, including those for customizations (can be thought of on the same level as the low-level functions of `hal`). +This can also contain files provided by the architecture vendor. + +Example: + +- `xt_set_exception_handler` +- `riscv_global_interrupts_enable` +- `ERI_PERFMON_MAX` + +#### `esp_common` + +Contains hardware-agnostic definitions, constants, macros, utilities, 'pure' and/or algorithmic functions that is useable by all other components (that is, barring there being a more appropriate component to put them in). + +Example: + +- `BIT(nr)` and other bit manipulation utilities in the future +- `IDF_DEPRECATED(REASON)` +- `ESP_IDF_VERSION_MAJOR` + +#### `soc` + +Contains description of the underlying hardware: register structure, addresses, pins, capabilities, etc. + +Example: + +- `DR_REG_DPORT_BASE` +- `SOC_MCPWM_SUPPORTED` +- `uart_dev_s` + +#### `esp_rom` + +Contains headers, linker scripts, abstraction layer, patches, and other related files to ROM functions. + +Example: + +- `esp32.rom.eco3.ld` +- `rom/aes.h` + +### `G1` Components + +#### `spi_flash` + +SPI flash device access implementation. + +#### `freertos` + +FreeRTOS port to targets supported by ESP-IDF. + +#### `log` + +Logging library. + +#### `heap` + +Heap implementation. + +#### `newlib` + +Some functions n the standard library are implemented here, especially those needing other `G1` components. + +Example: + +- `malloc` is implemented in terms of the component `heap`'s functions +- `gettimeofday` is implemented in terms of system time in `esp_system` + +#### `esp_system` + +Contains implementation of system services and controls system behavior. The implementations +here may take hardware resources and/or decide on a hardware state needed for support of a system service/feature/mechanism. +Currently, this encompasses the following, but not limited to: + +- Startup and initialization +- Panic and debug +- Reset and reset reason +- Task and interrupt watchdogs + +#### `esp_hw_support` + +Contains implementations that provide hardware operations, arbitration, or resource sharing, especially those that +is used in the system. Unlike `esp_system`, implementations here do not decide on a hardware state or takes hardware resource, acting +merely as facilitator to hardware access. Currently, this encompasses the following, but not limited to: + +- Interrupt allocation +- Sleep functions +- Memory functions (external SPIRAM, async memory, etc.) +- Clock and clock control +- Random generation +- CPU utilities +- MAC settings + +### `esp_hw_support` vs `esp_system` + +This section details list some implementations and the reason for placing it in either `esp_hw_support` or `esp_system`. + +#### `task_wdt.c` (`esp_system`) vs `intr_alloc.c` (`esp_hw_support`) + +The task watchdog fits the definition of taking and configuring hardware resources (wdt, interrupt) for implementation of a system service/mechanism. + +This is in contrast with interrupt allocation that merely facilitates access to the underlying hardware for other implementations - +drivers, user code, and even the task watchdog mentioned previously! + +#### `crosscore_int.c` (`esp_system`) + +The current implementation of crosscore interrupts is tightly coupled with a number of interrupt reasons +associated with system services/mechanisms: REASON_YIELD (scheduler), REASON_FREQ_SWITCH (power management) +REASON_PRINT_BACKTRACE (panic and debug). + +However, if an implementation exists that makes it possible to register an arbitrary interrupt reason - a +lower level inter-processor call if you will, then this implementation is a good candidate for `esp_hw_support`. +The current implementation in `esp_system` can then just register the interrupt reasons mentioned above. + +#### `esp_mac.h`, `esp_chip_info.h`, `esp_random.h` (`esp_hw_support`) + +The functions in these headers used to be in `esp_system.h`, but have been split-off. +However, to maintain backward compatibility, `esp_system.h` includes these headers. + +The remaining functions in `esp_system.h` are those that deal with system behavior, such +as `esp_register_shutdown_handler`, or are proxy for other system components's APIs such as +`esp_get_free_heap_size`. + +The functions split-off from `esp_system.h` are much more hardware manipulation oriented such as: +`esp_read_mac`, `esp_random` and `esp_chip_info`. \ No newline at end of file diff --git a/components/app_trace/sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.h b/components/app_trace/sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.h index 47624de258..aba3bd3ea3 100644 --- a/components/app_trace/sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.h +++ b/components/app_trace/sys_view/Sample/OS/SEGGER_SYSVIEW_FreeRTOS.h @@ -209,7 +209,7 @@ Notes: #define apiID_VEVENTGROUPDELETE (72u) #define apiID_UXEVENTGROUPGETNUMBER (73u) -#define traceTASK_NOTIFY_TAKE() SEGGER_SYSVIEW_RecordU32x2(apiFastID_OFFSET + apiID_ULTASKNOTIFYTAKE, xClearCountOnExit, xTicksToWait) +#define traceTASK_NOTIFY_TAKE( uxIndexToWait ) SEGGER_SYSVIEW_RecordU32x2(apiFastID_OFFSET + apiID_ULTASKNOTIFYTAKE, xClearCountOnExit, xTicksToWait) #define traceTASK_DELAY() SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VTASKDELAY, xTicksToDelay) #define traceTASK_DELAY_UNTIL() SEGGER_SYSVIEW_RecordVoid(apiFastID_OFFSET + apiID_VTASKDELAYUNTIL) #define traceTASK_DELETE( pxTCB ) if (pxTCB != NULL) { \ @@ -217,16 +217,16 @@ Notes: SEGGER_SYSVIEW_ShrinkId((U32)pxTCB)); \ SYSVIEW_DeleteTask((U32)pxTCB); \ } -#define traceTASK_NOTIFY_GIVE_FROM_ISR() SEGGER_SYSVIEW_RecordU32x2(apiFastID_OFFSET + apiID_VTASKNOTIFYGIVEFROMISR, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), (U32)pxHigherPriorityTaskWoken) +#define traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ) SEGGER_SYSVIEW_RecordU32x2(apiFastID_OFFSET + apiID_VTASKNOTIFYGIVEFROMISR, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), (U32)pxHigherPriorityTaskWoken) #define traceTASK_PRIORITY_INHERIT( pxTCB, uxPriority ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VTASKPRIORITYINHERIT, (U32)pxMutexHolder) #define traceTASK_RESUME( pxTCB ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VTASKRESUME, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB)) #define traceINCREASE_TICK_COUNT( xTicksToJump ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VTASKSTEPTICK, xTicksToJump) #define traceTASK_SUSPEND( pxTCB ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VTASKSUSPEND, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB)) #define traceTASK_PRIORITY_DISINHERIT( pxTCB, uxBasePriority ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_XTASKPRIORITYDISINHERIT, (U32)pxMutexHolder) #define traceTASK_RESUME_FROM_ISR( pxTCB ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_XTASKRESUMEFROMISR, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB)) -#define traceTASK_NOTIFY() SYSVIEW_RecordU32x4(apiFastID_OFFSET + apiID_XTASKGENERICNOTIFY, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), ulValue, eAction, (U32)pulPreviousNotificationValue) -#define traceTASK_NOTIFY_FROM_ISR() SYSVIEW_RecordU32x5(apiFastID_OFFSET + apiID_XTASKGENERICNOTIFYFROMISR, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), ulValue, eAction, (U32)pulPreviousNotificationValue, (U32)pxHigherPriorityTaskWoken) -#define traceTASK_NOTIFY_WAIT() SYSVIEW_RecordU32x4(apiFastID_OFFSET + apiID_XTASKNOTIFYWAIT, ulBitsToClearOnEntry, ulBitsToClearOnExit, (U32)pulNotificationValue, xTicksToWait) +#define traceTASK_NOTIFY( uxIndexToNotify ) SYSVIEW_RecordU32x4(apiFastID_OFFSET + apiID_XTASKGENERICNOTIFY, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), ulValue, eAction, (U32)pulPreviousNotificationValue) +#define traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ) SYSVIEW_RecordU32x5(apiFastID_OFFSET + apiID_XTASKGENERICNOTIFYFROMISR, SEGGER_SYSVIEW_ShrinkId((U32)pxTCB), ulValue, eAction, (U32)pulPreviousNotificationValue, (U32)pxHigherPriorityTaskWoken) +#define traceTASK_NOTIFY_WAIT( uxIndexToWait ) SYSVIEW_RecordU32x4(apiFastID_OFFSET + apiID_XTASKNOTIFYWAIT, ulBitsToClearOnEntry, ulBitsToClearOnExit, (U32)pulNotificationValue, xTicksToWait) #define traceQUEUE_CREATE( pxNewQueue ) SEGGER_SYSVIEW_RecordU32x3(apiFastID_OFFSET + apiID_XQUEUEGENERICCREATE, uxQueueLength, uxItemSize, ucQueueType) #define traceQUEUE_DELETE( pxQueue ) SEGGER_SYSVIEW_RecordU32(apiFastID_OFFSET + apiID_VQUEUEDELETE, SEGGER_SYSVIEW_ShrinkId((U32)pxQueue)) diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index e6329224e6..8dd328215c 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -46,6 +46,7 @@ if(NOT BOOTLOADER_BUILD) add_custom_target(blank_ota_data ALL DEPENDS ${blank_otadata_file}) add_dependencies(flash blank_ota_data) + add_dependencies(encrypted-flash blank_ota_data) set(otatool_py ${python} ${COMPONENT_DIR}/otatool.py) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 1ad38ea99a..306e4dcd3c 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -502,6 +502,7 @@ menu "Security features" bool "Enable hardware Secure Boot in bootloader (READ DOCS FIRST)" default n depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || ESP32C3_REV_MIN_3 || IDF_TARGET_ESP32S3 + select ESPTOOLPY_NO_STUB if !IDF_TARGET_ESP32 && !IDF_TARGET_ESP32S2 help Build a bootloader which enables Secure Boot on first boot. @@ -892,6 +893,8 @@ menu "Security features" It is also possible to enable secure download mode at runtime by calling esp_efuse_enable_rom_secure_download_mode() + Note: Secure Download mode is not available for ESP32 (includes revisions till ECO3). + config SECURE_INSECURE_ALLOW_DL_MODE bool "UART ROM download mode (Enabled (not recommended))" help diff --git a/components/bootloader/subproject/main/ld/esp32/bootloader.ld b/components/bootloader/subproject/main/ld/esp32/bootloader.ld index 0c065d2c0c..30e5a79c45 100644 --- a/components/bootloader/subproject/main/ld/esp32/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32/bootloader.ld @@ -49,6 +49,7 @@ SECTIONS *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_soc.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encryption_secure_features.*(.literal .text .literal.* .text.*) diff --git a/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld b/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld index 4da19b44de..c9b2da7612 100644 --- a/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld @@ -36,6 +36,7 @@ SECTIONS *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_soc.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encryption_secure_features.*(.literal .text .literal.* .text.*) diff --git a/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld b/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld index dc49e09123..c5648b815e 100644 --- a/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32h2/bootloader.ld @@ -36,6 +36,7 @@ SECTIONS *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_soc.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_partitions.*(.literal .text .literal.* .text.*) diff --git a/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld b/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld index 6ba052d6aa..92e0126cf0 100644 --- a/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32s2/bootloader.ld @@ -36,6 +36,7 @@ SECTIONS *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_soc.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encryption_secure_features.*(.literal .text .literal.* .text.*) diff --git a/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld b/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld index 85daecd512..244482f823 100644 --- a/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld +++ b/components/bootloader/subproject/main/ld/esp32s3/bootloader.ld @@ -37,6 +37,7 @@ SECTIONS *libbootloader_support.a:bootloader_sha.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_console_loader.*(.literal .text .literal.* .text.*) *libbootloader_support.a:bootloader_panic.*(.literal .text .literal.* .text.*) + *libbootloader_support.a:bootloader_soc.*(.literal .text .literal.* .text.*) *libbootloader_support.a:esp_image_format.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encrypt.*(.literal .text .literal.* .text.*) *libbootloader_support.a:flash_encryption_secure_features.*(.literal .text .literal.* .text.*) diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index f2da1dbb2e..664a59817d 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -26,6 +26,7 @@ if(BOOTLOADER_BUILD) "src/bootloader_console_loader.c" "src/bootloader_panic.c" "src/${IDF_TARGET}/bootloader_sha.c" + "src/${IDF_TARGET}/bootloader_soc.c" "src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c" ) list(APPEND priv_requires hal) diff --git a/components/bootloader_support/include/bootloader_common.h b/components/bootloader_support/include/bootloader_common.h index 2ff0e595a2..2f4e906220 100644 --- a/components/bootloader_support/include/bootloader_common.h +++ b/components/bootloader_support/include/bootloader_common.h @@ -119,6 +119,15 @@ bool bootloader_common_label_search(const char *list, char *label); */ void bootloader_configure_spi_pins(int drv); +/** + * @brief Get flash CS IO + * + * Can be determined by eFuse values, or the default value + * + * @return Flash CS IO + */ +uint8_t bootloader_flash_get_cs_io(void); + /** * @brief Calculates a sha-256 for a given partition or returns a appended digest. * diff --git a/components/bootloader_support/include_bootloader/bootloader_soc.h b/components/bootloader_support/include_bootloader/bootloader_soc.h new file mode 100644 index 0000000000..a7414fd3f3 --- /dev/null +++ b/components/bootloader_support/include_bootloader/bootloader_soc.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +/** + * @brief Configure analog super WDT reset + * + * @param enable Boolean to enable or disable super WDT reset + */ +void bootloader_ana_super_wdt_reset_config(bool enable); + +/** + * @brief Configure analog brownout reset + * + * @param enable Boolean to enable or disable brownout reset + */ +void bootloader_ana_bod_reset_config(bool enable); + +/** + * @brief Configure analog clock glitch reset + * + * @param enable Boolean to enable or disable clock glitch reset + */ +void bootloader_ana_clock_glitch_reset_config(bool enable); diff --git a/components/bootloader_support/src/bootloader_common.c b/components/bootloader_support/src/bootloader_common.c index 2369436d7e..2acbd74fa9 100644 --- a/components/bootloader_support/src/bootloader_common.c +++ b/components/bootloader_support/src/bootloader_common.c @@ -23,6 +23,7 @@ #include "esp_rom_crc.h" #include "esp_rom_gpio.h" #include "esp_rom_sys.h" +#include "esp_rom_efuse.h" #include "esp_flash_partitions.h" #include "bootloader_flash_priv.h" #include "bootloader_common.h" @@ -191,8 +192,19 @@ void bootloader_common_vddsdio_configure(void) #endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST } - RESET_REASON bootloader_common_get_reset_reason(int cpu_no) { return (RESET_REASON)esp_rom_get_reset_reason(cpu_no); } + +uint8_t bootloader_flash_get_cs_io(void) +{ + uint8_t cs_io; + const uint32_t spiconfig = esp_rom_efuse_get_flash_gpio_info(); + if (spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI) { + cs_io = SPI_CS0_GPIO_NUM; + } else { + cs_io = (spiconfig >> 18) & 0x3f; + } + return cs_io; +} diff --git a/components/bootloader_support/src/bootloader_efuse_esp32.c b/components/bootloader_support/src/bootloader_efuse_esp32.c index 3704e16c3a..2a89e63ce4 100644 --- a/components/bootloader_support/src/bootloader_efuse_esp32.c +++ b/components/bootloader_support/src/bootloader_efuse_esp32.c @@ -7,14 +7,14 @@ #include "bootloader_common.h" #include "bootloader_clock.h" #include "soc/efuse_reg.h" -#include "soc/apb_ctrl_reg.h" +#include "soc/syscon_reg.h" uint8_t bootloader_common_get_chip_revision(void) { uint8_t eco_bit0, eco_bit1, eco_bit2; eco_bit0 = (REG_READ(EFUSE_BLK0_RDATA3_REG) & 0xF000) >> 15; eco_bit1 = (REG_READ(EFUSE_BLK0_RDATA5_REG) & 0x100000) >> 20; - eco_bit2 = (REG_READ(APB_CTRL_DATE_REG) & 0x80000000) >> 31; + eco_bit2 = (REG_READ(SYSCON_DATE_REG) & 0x80000000) >> 31; uint32_t combine_value = (eco_bit2 << 2) | (eco_bit1 << 1) | eco_bit0; uint8_t chip_ver = 0; switch (combine_value) { @@ -28,7 +28,7 @@ uint8_t bootloader_common_get_chip_revision(void) chip_ver = 2; break; #if CONFIG_IDF_ENV_FPGA - case 4: /* Empty efuses, but APB_CTRL_DATE_REG bit is set */ + case 4: /* Empty efuses, but SYSCON_DATE_REG bit is set */ chip_ver = 3; break; #endif diff --git a/components/bootloader_support/src/bootloader_flash_config_esp32s3.c b/components/bootloader_support/src/bootloader_flash_config_esp32s3.c index 6266a74e8c..357de775a5 100644 --- a/components/bootloader_support/src/bootloader_flash_config_esp32s3.c +++ b/components/bootloader_support/src/bootloader_flash_config_esp32s3.c @@ -33,18 +33,18 @@ void bootloader_flash_update_id() void IRAM_ATTR bootloader_flash_cs_timing_config() { - // SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time, cs_hold_delay registers for FLASH/PSRAM, so we only need to set SPI0 related registers here + //SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time, cs_hold_delay registers for FLASH, so we only need to set SPI0 related registers here +#if CONFIG_ESPTOOLPY_OCT_FLASH SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M); SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, FLASH_CS_HOLD_TIME, SPI_MEM_CS_HOLD_TIME_S); SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, FLASH_CS_SETUP_TIME, SPI_MEM_CS_SETUP_TIME_S); - - SET_PERI_REG_MASK(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_M | SPI_MEM_SPI_SMEM_CS_SETUP_M); - SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_TIME_V, FLASH_CS_HOLD_TIME, SPI_MEM_SPI_SMEM_CS_HOLD_TIME_S); - SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_SETUP_TIME_V, FLASH_CS_SETUP_TIME, SPI_MEM_SPI_SMEM_CS_SETUP_TIME_S); - - // cs high time + //CS high time SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_DELAY_V, FLASH_CS_HOLD_DELAY, SPI_MEM_CS_HOLD_DELAY_S); - SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_V, FLASH_CS_HOLD_DELAY, SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_S); +#else + SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S); + SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S); + SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M); +#endif } void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t *pfhdr) diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index dddf93f3a8..8533f2b3ce 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -76,6 +76,7 @@ #include "bootloader_utility.h" #include "bootloader_sha.h" #include "bootloader_console.h" +#include "bootloader_soc.h" #include "esp_efuse.h" static const char *TAG = "boot"; @@ -636,6 +637,12 @@ static void load_image(const esp_image_metadata_t *image_data) ESP_LOGI(TAG, "Disabling RNG early entropy source..."); bootloader_random_disable(); + /* Disable glitch reset after all the security checks are completed. + * Glitch detection can be falsely triggered by EMI interference (high RF TX power, etc) + * and to avoid such false alarms, disable it. + */ + bootloader_ana_clock_glitch_reset_config(false); + // copy loaded segments to RAM, set up caches for mapped segments, and start application unpack_load_app(image_data); } diff --git a/components/bootloader_support/src/esp32/bootloader_soc.c b/components/bootloader_support/src/esp32/bootloader_soc.c new file mode 100644 index 0000000000..242a575508 --- /dev/null +++ b/components/bootloader_support/src/esp32/bootloader_soc.c @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +void bootloader_ana_super_wdt_reset_config(bool enable) +{ + (void)enable; +} + +void bootloader_ana_bod_reset_config(bool enable) +{ + (void)enable; +} + +void bootloader_ana_clock_glitch_reset_config(bool enable) +{ + (void)enable; +} diff --git a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c index 7f022f6560..608654e091 100644 --- a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c +++ b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c @@ -36,6 +36,7 @@ #include "regi2c_ctrl.h" #include "bootloader_console.h" #include "bootloader_flash_priv.h" +#include "bootloader_soc.h" #include "esp_efuse.h" static const char *TAG = "boot.esp32c3"; @@ -263,7 +264,7 @@ static inline void bootloader_hardware_init(void) } } -static inline void bootloader_glitch_reset_disable(void) +static inline void bootloader_ana_reset_config(void) { /* For origin chip & ECO1: only support swt reset; @@ -271,10 +272,27 @@ static inline void bootloader_glitch_reset_disable(void) For ECO3: fix clock glitch reset bug, support all reset, include: swt & brownout & clock glitch reset. */ uint8_t chip_version = bootloader_common_get_chip_revision(); - if (chip_version < 2) { - REG_SET_FIELD(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SEL, RTC_CNTL_FIB_SUPER_WDT_RST); - } else if (chip_version == 2) { - REG_SET_FIELD(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SEL, RTC_CNTL_FIB_SUPER_WDT_RST | RTC_CNTL_FIB_BOR_RST); + switch (chip_version) { + case 0: + case 1: + //Enable WDT reset. Disable BOR and GLITCH reset + bootloader_ana_super_wdt_reset_config(true); + bootloader_ana_bod_reset_config(false); + bootloader_ana_clock_glitch_reset_config(false); + break; + case 2: + //Enable WDT and BOR reset. Disable GLITCH reset + bootloader_ana_super_wdt_reset_config(true); + bootloader_ana_bod_reset_config(true); + bootloader_ana_clock_glitch_reset_config(false); + break; + case 3: + default: + //Enable WDT, BOR, and GLITCH reset + bootloader_ana_super_wdt_reset_config(true); + bootloader_ana_bod_reset_config(true); + bootloader_ana_clock_glitch_reset_config(true); + break; } } @@ -283,7 +301,7 @@ esp_err_t bootloader_init(void) esp_err_t ret = ESP_OK; bootloader_hardware_init(); - bootloader_glitch_reset_disable(); + bootloader_ana_reset_config(); bootloader_super_wdt_auto_feed(); // protect memory region bootloader_init_mem(); diff --git a/components/bootloader_support/src/esp32c3/bootloader_soc.c b/components/bootloader_support/src/esp32c3/bootloader_soc.c new file mode 100644 index 0000000000..7104528a58 --- /dev/null +++ b/components/bootloader_support/src/esp32c3/bootloader_soc.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + +void bootloader_ana_super_wdt_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SUPER_WDT_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } else { + REG_CLR_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } +} + +void bootloader_ana_bod_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_BOR_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } +} + +void bootloader_ana_clock_glitch_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_GLITCH_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } +} diff --git a/components/bootloader_support/src/esp32h2/bootloader_esp32h2.c b/components/bootloader_support/src/esp32h2/bootloader_esp32h2.c index 075bd6a8c4..7ff27dd693 100644 --- a/components/bootloader_support/src/esp32h2/bootloader_esp32h2.c +++ b/components/bootloader_support/src/esp32h2/bootloader_esp32h2.c @@ -36,6 +36,7 @@ #include "regi2c_ctrl.h" #include "bootloader_console.h" #include "bootloader_flash_priv.h" +#include "bootloader_soc.h" static const char *TAG = "boot.esp32h2"; @@ -254,27 +255,15 @@ static void bootloader_super_wdt_auto_feed(void) static inline void bootloader_hardware_init(void) { - // This check is always included in the bootloader so it can - // print the minimum revision error message later in the boot - if (bootloader_common_get_chip_revision() < 3) { - REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_FORCE_XPD_IPH, 1); - REGI2C_WRITE_MASK(I2C_BIAS, I2C_BIAS_DREG_1P1_PVT, 12); - } + } -static inline void bootloader_glitch_reset_disable(void) +static inline void bootloader_ana_reset_config(void) { - /* - For origin chip & ECO1: only support swt reset; - For ECO2: fix brownout reset bug, support swt & brownout reset; - For ECO3: fix clock glitch reset bug, support all reset, include: swt & brownout & clock glitch reset. - */ - uint8_t chip_version = bootloader_common_get_chip_revision(); - if (chip_version < 2) { - REG_SET_FIELD(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SEL, RTC_CNTL_FIB_SUPER_WDT_RST); - } else if (chip_version == 2) { - REG_SET_FIELD(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SEL, RTC_CNTL_FIB_SUPER_WDT_RST | RTC_CNTL_FIB_BOR_RST); - } + //Enable WDT, BOR, and GLITCH reset + bootloader_ana_super_wdt_reset_config(true); + bootloader_ana_bod_reset_config(true); + bootloader_ana_clock_glitch_reset_config(true); } esp_err_t bootloader_init(void) @@ -282,7 +271,7 @@ esp_err_t bootloader_init(void) esp_err_t ret = ESP_OK; bootloader_hardware_init(); - bootloader_glitch_reset_disable(); + bootloader_ana_reset_config(); bootloader_super_wdt_auto_feed(); // protect memory region bootloader_init_mem(); diff --git a/components/bootloader_support/src/esp32h2/bootloader_soc.c b/components/bootloader_support/src/esp32h2/bootloader_soc.c new file mode 100644 index 0000000000..7104528a58 --- /dev/null +++ b/components/bootloader_support/src/esp32h2/bootloader_soc.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + +void bootloader_ana_super_wdt_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SUPER_WDT_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } else { + REG_CLR_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } +} + +void bootloader_ana_bod_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_BOR_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } +} + +void bootloader_ana_clock_glitch_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_GLITCH_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } +} diff --git a/components/bootloader_support/src/esp32s2/bootloader_soc.c b/components/bootloader_support/src/esp32s2/bootloader_soc.c new file mode 100644 index 0000000000..242a575508 --- /dev/null +++ b/components/bootloader_support/src/esp32s2/bootloader_soc.c @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +void bootloader_ana_super_wdt_reset_config(bool enable) +{ + (void)enable; +} + +void bootloader_ana_bod_reset_config(bool enable) +{ + (void)enable; +} + +void bootloader_ana_clock_glitch_reset_config(bool enable) +{ + (void)enable; +} diff --git a/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c b/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c index f4aae147b2..8346b00d02 100644 --- a/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c +++ b/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c @@ -34,6 +34,7 @@ #include "bootloader_mem.h" #include "bootloader_console.h" #include "bootloader_flash_priv.h" +#include "bootloader_soc.h" #include "esp_efuse.h" @@ -296,9 +297,18 @@ static void bootloader_super_wdt_auto_feed(void) REG_WRITE(RTC_CNTL_SWD_WPROTECT_REG, 0); } +static inline void bootloader_ana_reset_config(void) +{ + //Enable WDT, BOR, and GLITCH reset + bootloader_ana_super_wdt_reset_config(true); + bootloader_ana_bod_reset_config(true); + bootloader_ana_clock_glitch_reset_config(true); +} + esp_err_t bootloader_init(void) { esp_err_t ret = ESP_OK; + bootloader_ana_reset_config(); bootloader_super_wdt_auto_feed(); // protect memory region bootloader_init_mem(); diff --git a/components/bootloader_support/src/esp32s3/bootloader_soc.c b/components/bootloader_support/src/esp32s3/bootloader_soc.c new file mode 100644 index 0000000000..7104528a58 --- /dev/null +++ b/components/bootloader_support/src/esp32s3/bootloader_soc.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" + +void bootloader_ana_super_wdt_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_SUPER_WDT_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } else { + REG_CLR_BIT(RTC_CNTL_SWD_CONF_REG, RTC_CNTL_SWD_BYPASS_RST); + } +} + +void bootloader_ana_bod_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_BOR_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_ANA_RST_EN); + } +} + +void bootloader_ana_clock_glitch_reset_config(bool enable) +{ + REG_CLR_BIT(RTC_CNTL_FIB_SEL_REG, RTC_CNTL_FIB_GLITCH_RST); + + if (enable) { + REG_SET_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } else { + REG_CLR_BIT(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_GLITCH_RST_EN); + } +} diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 3ddc13758a..efacc806a1 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -1,6 +1,8 @@ if(CONFIG_BT_ENABLED) if(CONFIG_IDF_TARGET_ESP32) - set(srcs "controller/esp32/bt.c") + set(srcs "controller/esp32/bt.c" + "controller/esp32/hli_api.c" + "controller/esp32/hli_vectors.S") elseif(CONFIG_IDF_TARGET_ESP32C3) set(srcs "controller/esp32c3/bt.c") elseif(CONFIG_IDF_TARGET_ESP32S3) @@ -55,6 +57,7 @@ if(CONFIG_BT_ENABLED) host/bluedroid/bta/gatt/include host/bluedroid/bta/hf_ag/include host/bluedroid/bta/hf_client/include + host/bluedroid/bta/hd/include host/bluedroid/bta/hh/include host/bluedroid/bta/jv/include host/bluedroid/bta/sdp/include @@ -66,12 +69,12 @@ if(CONFIG_BT_ENABLED) host/bluedroid/external/sbc/plc/include host/bluedroid/btc/profile/esp/include host/bluedroid/btc/profile/std/a2dp/include - host/bluedroid/btc/profile/std/hid/include host/bluedroid/btc/profile/std/include host/bluedroid/btc/include host/bluedroid/stack/btm/include host/bluedroid/stack/gap/include host/bluedroid/stack/gatt/include + host/bluedroid/stack/hid/include host/bluedroid/stack/l2cap/include host/bluedroid/stack/sdp/include host/bluedroid/stack/smp/include @@ -94,6 +97,8 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/api/esp_gatt_common_api.c" "host/bluedroid/api/esp_gattc_api.c" "host/bluedroid/api/esp_gatts_api.c" + "host/bluedroid/api/esp_hidd_api.c" + "host/bluedroid/api/esp_hidh_api.c" "host/bluedroid/api/esp_hf_ag_api.c" "host/bluedroid/api/esp_hf_client_api.c" "host/bluedroid/api/esp_spp_api.c" @@ -128,6 +133,9 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/bta/gatt/bta_gatts_co.c" "host/bluedroid/bta/gatt/bta_gatts_main.c" "host/bluedroid/bta/gatt/bta_gatts_utils.c" + "host/bluedroid/bta/hd/bta_hd_api.c" + "host/bluedroid/bta/hd/bta_hd_act.c" + "host/bluedroid/bta/hd/bta_hd_main.c" "host/bluedroid/bta/hh/bta_hh_act.c" "host/bluedroid/bta/hh/bta_hh_api.c" "host/bluedroid/bta/hh/bta_hh_cfg.c" @@ -184,8 +192,9 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c" "host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c" "host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c" - "host/bluedroid/btc/profile/std/hid/hidh_api.c" - "host/bluedroid/btc/profile/std/hid/hidh_conn.c" + "host/bluedroid/btc/profile/std/hid/btc_hd.c" + "host/bluedroid/btc/profile/std/hid/btc_hh.c" + "host/bluedroid/btc/profile/std/hid/bta_hh_co.c" "host/bluedroid/btc/profile/std/gap/btc_gap_ble.c" "host/bluedroid/btc/profile/std/gap/btc_gap_bt.c" "host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c" @@ -251,6 +260,10 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/stack/avrc/avrc_pars_tg.c" "host/bluedroid/stack/avrc/avrc_sdp.c" "host/bluedroid/stack/avrc/avrc_utils.c" + "host/bluedroid/stack/hid/hidd_api.c" + "host/bluedroid/stack/hid/hidd_conn.c" + "host/bluedroid/stack/hid/hidh_api.c" + "host/bluedroid/stack/hid/hidh_conn.c" "host/bluedroid/stack/btm/btm_acl.c" "host/bluedroid/stack/btm/btm_ble.c" "host/bluedroid/stack/btm/btm_ble_addr.c" @@ -597,6 +610,8 @@ if(CONFIG_BT_ENABLED) if(CONFIG_IDF_TARGET_ESP32) target_link_libraries(${COMPONENT_LIB} INTERFACE "-L${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32/esp32") target_link_libraries(${COMPONENT_LIB} PUBLIC btdm_app) + + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_hli_vectors_bt") elseif(CONFIG_IDF_TARGET_ESP32C3) target_link_libraries(${COMPONENT_LIB} INTERFACE "-L${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32c3_family/esp32c3") diff --git a/components/bt/Kconfig b/components/bt/Kconfig index ca04f51b4b..8a15de6b86 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -7,42 +7,16 @@ menu "Bluetooth" help Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices. - config BT_CTRL_ESP32 - bool - depends on BT_ENABLED && IDF_TARGET_ESP32 - default y - - config BT_CTRL_ESP32C3 - bool - depends on BT_ENABLED && IDF_TARGET_ESP32C3 - default y - config BT_CTRL_ESP32S3 - bool - depends on BT_ENABLED && IDF_TARGET_ESP32S3 - default y - config BT_SOC_SUPPORT_5_0 bool depends on BT_ENABLED && (IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3) default y if BT_ENABLED && (IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3) default n - menu "Bluetooth controller(ESP32 Dual Mode Bluetooth)" - visible if BT_CTRL_ESP32 + menu "Bluetooth controller" + depends on BT_ENABLED - source "$IDF_PATH/components/bt/controller/esp32/Kconfig.in" - endmenu - - menu "Bluetooth controller(ESP32C3 Bluetooth Low Energy)" - visible if BT_CTRL_ESP32C3 - - source "$IDF_PATH/components/bt/controller/esp32c3/Kconfig.in" - endmenu - - menu "Bluetooth controller(ESP32S3 Bluetooth Low Energy)" - visible if BT_CTRL_ESP32S3 - - source "$IDF_PATH/components/bt/controller/esp32s3/Kconfig.in" + source "$IDF_PATH/components/bt/controller/$IDF_TARGET/Kconfig.in" endmenu choice BT_HOST @@ -73,12 +47,12 @@ menu "Bluetooth" endchoice menu "Bluedroid Options" - visible if BT_BLUEDROID_ENABLED + depends on BT_BLUEDROID_ENABLED source "$IDF_PATH/components/bt/host/bluedroid/Kconfig.in" endmenu menu "NimBLE Options" - visible if BT_NIMBLE_ENABLED + depends on BT_NIMBLE_ENABLED source "$IDF_PATH/components/bt/host/nimble/Kconfig.in" endmenu diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 9579446817..9cc311c52c 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -53,6 +53,12 @@ #if BTC_HF_CLIENT_INCLUDED #include "btc_hf_client.h" #endif /* #if BTC_HF_CLIENT_INCLUDED */ +#if BTC_HD_INCLUDED == TRUE +#include "btc_hd.h" +#endif /* BTC_HD_INCLUDED */ +#if BTC_HH_INCLUDED == TRUE +#include "btc_hh.h" +#endif /* BTC_HH_INCLUDED */ #endif /* #if CLASSIC_BT_INCLUDED */ #endif @@ -120,6 +126,12 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if BTC_HF_CLIENT_INCLUDED [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler}, #endif /* #if BTC_HF_CLIENT_INCLUDED */ +#if BTC_HD_INCLUDED + [BTC_PID_HD] = {btc_hd_call_handler, btc_hd_cb_handler }, +#endif +#if BTC_HH_INCLUDED + [BTC_PID_HH] = {btc_hh_call_handler, btc_hh_cb_handler }, +#endif #endif /* #if CLASSIC_BT_INCLUDED */ #endif #if CONFIG_BLE_MESH diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index cc9382590a..f8da6ffd42 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -65,6 +65,8 @@ typedef enum { BTC_PID_AVRC_CT, BTC_PID_AVRC_TG, BTC_PID_SPP, + BTC_PID_HD, + BTC_PID_HH, #if (BTC_HF_INCLUDED == TRUE) BTC_PID_HF, #endif /* BTC_HF_INCLUDED */ @@ -99,6 +101,10 @@ typedef struct { typedef void (* btc_arg_deep_copy_t)(btc_msg_t *msg, void *dst, void *src); +#ifdef __cplusplus +extern "C" { +#endif + /** * transfer an message to another module in the different task. * @param msg message @@ -124,4 +130,8 @@ void btc_deinit(void); bool btc_check_queue_is_congest(void); int get_btc_work_queue_size(void); +#ifdef __cplusplus +} +#endif + #endif /* __BTC_TASK_H__ */ diff --git a/components/bt/component.mk b/components/bt/component.mk index 8ec9205adf..bd8d52371a 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -10,6 +10,7 @@ COMPONENT_ADD_INCLUDEDIRS := include LIBS := btdm_app COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/controller/lib_esp32/esp32 \ + -u ld_include_hli_vectors_bt \ $(addprefix -l,$(LIBS)) # re-link program if BT binary libs change @@ -46,6 +47,7 @@ COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \ host/bluedroid/bta/hf_client/include \ host/bluedroid/bta/dm/include \ host/bluedroid/bta/gatt/include \ + host/bluedroid/bta/hd/include \ host/bluedroid/bta/hh/include \ host/bluedroid/bta/jv/include \ host/bluedroid/bta/sdp/include \ @@ -70,6 +72,7 @@ COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \ host/bluedroid/stack/gap/include \ host/bluedroid/stack/gatt/include \ host/bluedroid/stack/hcic/include \ + host/bluedroid/stack/hid/include \ host/bluedroid/stack/l2cap/include \ host/bluedroid/stack/sdp/include \ host/bluedroid/stack/smp/include \ @@ -86,6 +89,7 @@ COMPONENT_ADD_INCLUDEDIRS += host/bluedroid/api/include/api \ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/bta/gatt \ + host/bluedroid/bta/hd \ host/bluedroid/bta/hh \ host/bluedroid/bta/sdp \ host/bluedroid/bta/av \ @@ -118,6 +122,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/stack/gap \ host/bluedroid/stack/gatt \ host/bluedroid/stack/hcic \ + host/bluedroid/stack/hid \ host/bluedroid/stack/include \ host/bluedroid/stack/l2cap \ host/bluedroid/stack/sdp \ diff --git a/components/bt/controller/esp32/Kconfig.in b/components/bt/controller/esp32/Kconfig.in index 70a710c9b7..cc71f6563b 100644 --- a/components/bt/controller/esp32/Kconfig.in +++ b/components/bt/controller/esp32/Kconfig.in @@ -1,6 +1,5 @@ choice BTDM_CTRL_MODE prompt "Bluetooth controller mode (BR/EDR/BLE/DUALMODE)" - depends on BT_CTRL_ESP32 help Specify the bluetooth controller mode (BR/EDR, BLE or dual mode). @@ -152,7 +151,7 @@ config BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF choice BTDM_CTRL_PINNED_TO_CORE_CHOICE prompt "The cpu core which bluetooth controller run" - depends on BT_CTRL_ESP32 && !FREERTOS_UNICORE + depends on !FREERTOS_UNICORE help Specify the cpu core to run bluetooth controller. Can not specify no-affinity. @@ -172,7 +171,6 @@ config BTDM_CTRL_PINNED_TO_CORE choice BTDM_CTRL_HCI_MODE_CHOICE prompt "HCI mode" - depends on BT_CTRL_ESP32 help Speicify HCI mode as VHCI or UART(H4) @@ -210,11 +208,8 @@ menu "HCI UART(H4) Options" endmenu menu "MODEM SLEEP Options" - visible if BT_CTRL_ESP32 - config BTDM_CTRL_MODEM_SLEEP bool "Bluetooth modem sleep" - depends on BT_CTRL_ESP32 default y help Enable/disable bluetooth controller low power mode. @@ -415,3 +410,16 @@ config BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD of ADV packets lost in the controller reaches this threshold. It is better to set a larger value. If you set `BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD` to a small value or printf every adv lost event, it may cause adv packets lost more. + + +config BTDM_RESERVE_DRAM + hex + default 0xdb5c if BT_ENABLED + default 0 + +config BTDM_CTRL_HLI + bool "High level interrupt" + depends on BT_ENABLED + default y + help + Using Level 4 interrupt for Bluetooth. diff --git a/components/bt/controller/esp32/bt.c b/components/bt/controller/esp32/bt.c index 8808618c81..5bf5d8a318 100644 --- a/components/bt/controller/esp32/bt.c +++ b/components/bt/controller/esp32/bt.c @@ -40,6 +40,7 @@ #include "driver/periph_ctrl.h" #include "soc/rtc.h" #include "soc/soc_memory_layout.h" +#include "soc/dport_reg.h" #include "esp32/clk.h" #include "esp_coexist_internal.h" #if !CONFIG_FREERTOS_UNICORE @@ -47,6 +48,7 @@ #endif #include "esp_rom_sys.h" +#include "hli_api.h" #if CONFIG_BT_ENABLED @@ -54,6 +56,7 @@ ************************************************************************ */ +#define UNUSED(x) (void)(x) #define BTDM_LOG_TAG "BTDM_INIT" #define BTDM_INIT_PERIOD (5000) /* ms */ @@ -92,12 +95,12 @@ do{\ } while(0) #define OSI_FUNCS_TIME_BLOCKING 0xffffffff -#define OSI_VERSION 0x00010002 +#define OSI_VERSION 0x00010003 #define OSI_MAGIC_VALUE 0xFADEBEAD /* SPIRAM Configuration */ #if CONFIG_SPIRAM_USE_MALLOC -#define BTDM_MAX_QUEUE_NUM (5) +#define BTDM_MAX_QUEUE_NUM (6) #endif /* Types definition @@ -184,6 +187,10 @@ struct osi_funcs_t { void *(* _coex_schm_curr_phase_get)(void); int (* _coex_wifi_channel_get)(uint8_t *primary, uint8_t *secondary); int (* _coex_register_wifi_channel_change_callback)(void *cb); + xt_handler (*_set_isr_l3)(int n, xt_handler f, void *arg); + void (*_interrupt_l3_disable)(void); + void (*_interrupt_l3_restore)(void); + void *(* _customer_queue_create)(uint32_t queue_len, uint32_t item_size); uint32_t _magic; }; @@ -268,8 +275,13 @@ extern uint32_t _btdm_data_end; static bool btdm_queue_generic_register(const btdm_queue_item_t *queue); static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue); #endif /* CONFIG_SPIRAM_USE_MALLOC */ -static void IRAM_ATTR interrupt_disable(void); -static void IRAM_ATTR interrupt_restore(void); + +#if CONFIG_BTDM_CTRL_HLI +static xt_handler set_isr_hlevel_wrapper(int n, xt_handler f, void *arg); +static void IRAM_ATTR interrupt_hlevel_disable(void); +static void IRAM_ATTR interrupt_hlevel_restore(void); +#endif /* CONFIG_BTDM_CTRL_HLI */ +static void IRAM_ATTR task_yield(void); static void IRAM_ATTR task_yield_from_isr(void); static void *semphr_create_wrapper(uint32_t max, uint32_t init); static void semphr_delete_wrapper(void *semphr); @@ -281,12 +293,21 @@ static void *mutex_create_wrapper(void); static void mutex_delete_wrapper(void *mutex); static int32_t mutex_lock_wrapper(void *mutex); static int32_t mutex_unlock_wrapper(void *mutex); +#if CONFIG_BTDM_CTRL_HLI +static void *queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size); +static void queue_delete_hlevel_wrapper(void *queue); +static int32_t IRAM_ATTR queue_send_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_send_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw); +static int32_t IRAM_ATTR queue_recv_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_recv_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw); +#else static void *queue_create_wrapper(uint32_t queue_len, uint32_t item_size); static void queue_delete_wrapper(void *queue); -static int32_t queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms); static int32_t IRAM_ATTR queue_send_from_isr_wrapper(void *queue, void *item, void *hptw); -static int32_t queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms); +static int32_t IRAM_ATTR queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms); static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, void *hptw); +#endif /* CONFIG_BTDM_CTRL_HLI */ static int32_t task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id); static void task_delete_wrapper(void *task_handle); static bool IRAM_ATTR is_in_isr_wrapper(void); @@ -317,17 +338,30 @@ static uint8_t coex_schm_curr_period_get_wrapper(void); static void * coex_schm_curr_phase_get_wrapper(void); static int coex_wifi_channel_get_wrapper(uint8_t *primary, uint8_t *secondary); static int coex_register_wifi_channel_change_callback_wrapper(void *cb); +#if CONFIG_BTDM_CTRL_HLI +static void *customer_queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size); +#endif /* CONFIG_BTDM_CTRL_HLI */ +static void IRAM_ATTR interrupt_l3_disable(void); +static void IRAM_ATTR interrupt_l3_restore(void); + /* Local variable definition *************************************************************************** */ /* OSI funcs */ static const struct osi_funcs_t osi_funcs_ro = { ._version = OSI_VERSION, +#if CONFIG_BTDM_CTRL_HLI + ._set_isr = set_isr_hlevel_wrapper, + ._ints_on = xt_ints_on, + ._interrupt_disable = interrupt_hlevel_disable, + ._interrupt_restore = interrupt_hlevel_restore, +#else ._set_isr = xt_set_interrupt_handler, ._ints_on = xt_ints_on, - ._interrupt_disable = interrupt_disable, - ._interrupt_restore = interrupt_restore, - ._task_yield = vPortYield, + ._interrupt_disable = interrupt_l3_disable, + ._interrupt_restore = interrupt_l3_restore, +#endif /* CONFIG_BTDM_CTRL_HLI */ + ._task_yield = task_yield, ._task_yield_from_isr = task_yield_from_isr, ._semphr_create = semphr_create_wrapper, ._semphr_delete = semphr_delete_wrapper, @@ -339,12 +373,21 @@ static const struct osi_funcs_t osi_funcs_ro = { ._mutex_delete = mutex_delete_wrapper, ._mutex_lock = mutex_lock_wrapper, ._mutex_unlock = mutex_unlock_wrapper, +#if CONFIG_BTDM_CTRL_HLI + ._queue_create = queue_create_hlevel_wrapper, + ._queue_delete = queue_delete_hlevel_wrapper, + ._queue_send = queue_send_hlevel_wrapper, + ._queue_send_from_isr = queue_send_from_isr_hlevel_wrapper, + ._queue_recv = queue_recv_hlevel_wrapper, + ._queue_recv_from_isr = queue_recv_from_isr_hlevel_wrapper, +#else ._queue_create = queue_create_wrapper, ._queue_delete = queue_delete_wrapper, ._queue_send = queue_send_wrapper, ._queue_send_from_isr = queue_send_from_isr_wrapper, ._queue_recv = queue_recv_wrapper, ._queue_recv_from_isr = queue_recv_from_isr_wrapper, +#endif /* CONFIG_BTDM_CTRL_HLI */ ._task_create = task_create_wrapper, ._task_delete = task_delete_wrapper, ._is_in_isr = is_in_isr_wrapper, @@ -378,6 +421,14 @@ static const struct osi_funcs_t osi_funcs_ro = { ._coex_schm_curr_phase_get = coex_schm_curr_phase_get_wrapper, ._coex_wifi_channel_get = coex_wifi_channel_get_wrapper, ._coex_register_wifi_channel_change_callback = coex_register_wifi_channel_change_callback_wrapper, + ._set_isr_l3 = xt_set_interrupt_handler, + ._interrupt_l3_disable = interrupt_l3_disable, + ._interrupt_l3_restore = interrupt_l3_restore, +#if CONFIG_BTDM_CTRL_HLI + ._customer_queue_create = customer_queue_create_hlevel_wrapper, +#else + ._customer_queue_create = NULL, +#endif /* CONFIG_BTDM_CTRL_HLI */ ._magic = OSI_MAGIC_VALUE, }; @@ -494,7 +545,48 @@ static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue) #endif /* CONFIG_SPIRAM_USE_MALLOC */ -static void IRAM_ATTR interrupt_disable(void) +#if CONFIG_BTDM_CTRL_HLI +struct interrupt_hlevel_cb{ + uint32_t status; + uint8_t nested; +}; + +static DRAM_ATTR struct interrupt_hlevel_cb hli_cb = { + .status = 0, + .nested = 0, +}; + +static xt_handler set_isr_hlevel_wrapper(int mask, xt_handler f, void *arg) +{ + esp_err_t err = hli_intr_register((intr_handler_t) f, arg, DPORT_PRO_INTR_STATUS_0_REG, mask); + if (err == ESP_OK) { + return f; + } else { + return 0; + } + } + +static void IRAM_ATTR interrupt_hlevel_disable(void) +{ + assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE); + assert(hli_cb.nested != ~0); + uint32_t status = hli_intr_disable(); + if (hli_cb.nested++ == 0) { + hli_cb.status = status; + } +} + +static void IRAM_ATTR interrupt_hlevel_restore(void) +{ + assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE); + assert(hli_cb.nested > 0); + if (--hli_cb.nested == 0) { + hli_intr_restore(hli_cb.status); + } +} +#endif /* CONFIG_BTDM_CTRL_HLI */ + +static void IRAM_ATTR interrupt_l3_disable(void) { if (xPortInIsrContext()) { portENTER_CRITICAL_ISR(&global_int_mux); @@ -503,7 +595,7 @@ static void IRAM_ATTR interrupt_disable(void) } } -static void IRAM_ATTR interrupt_restore(void) +static void IRAM_ATTR interrupt_l3_restore(void) { if (xPortInIsrContext()) { portEXIT_CRITICAL_ISR(&global_int_mux); @@ -512,6 +604,12 @@ static void IRAM_ATTR interrupt_restore(void) } } +static void IRAM_ATTR task_yield(void) +{ + vPortYield(); +} + + static void IRAM_ATTR task_yield_from_isr(void) { portYIELD_FROM_ISR(); @@ -519,18 +617,19 @@ static void IRAM_ATTR task_yield_from_isr(void) static void *semphr_create_wrapper(uint32_t max, uint32_t init) { + void *handle = NULL; + #if !CONFIG_SPIRAM_USE_MALLOC - return (void *)xSemaphoreCreateCounting(max, init); + handle = (void *)xSemaphoreCreateCounting(max, init); #else StaticQueue_t *queue_buffer = NULL; - QueueHandle_t handle = NULL; queue_buffer = heap_caps_malloc(sizeof(StaticQueue_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); if (!queue_buffer) { goto error; } - handle = xSemaphoreCreateCountingStatic(max, init, queue_buffer); + handle = (void *)xSemaphoreCreateCountingStatic(max, init, queue_buffer); if (!handle) { goto error; } @@ -544,8 +643,19 @@ static void *semphr_create_wrapper(uint32_t max, uint32_t init) if (!btdm_queue_generic_register(&item)) { goto error; } - return handle; +#endif +#if CONFIG_BTDM_CTRL_HLI + SemaphoreHandle_t downstream_semaphore = handle; + assert(downstream_semaphore); + hli_queue_handle_t s_semaphore = hli_semaphore_create(max, downstream_semaphore); + assert(downstream_semaphore); + return s_semaphore; +#else + return handle; +#endif /* CONFIG_BTDM_CTRL_HLI */ + +#if CONFIG_SPIRAM_USE_MALLOC error: if (handle) { vSemaphoreDelete(handle); @@ -560,11 +670,22 @@ static void *semphr_create_wrapper(uint32_t max, uint32_t init) static void semphr_delete_wrapper(void *semphr) { + void *handle = NULL; +#if CONFIG_BTDM_CTRL_HLI + if (((hli_queue_handle_t)semphr)->downstream != NULL) { + handle = ((hli_queue_handle_t)semphr)->downstream; + } + + hli_queue_delete(semphr); +#else + handle = semphr; +#endif /* CONFIG_BTDM_CTRL_HLI */ + #if !CONFIG_SPIRAM_USE_MALLOC - vSemaphoreDelete(semphr); + vSemaphoreDelete(handle); #else btdm_queue_item_t item = { - .handle = semphr, + .handle = handle, .storage = NULL, .buffer = NULL, }; @@ -573,33 +694,55 @@ static void semphr_delete_wrapper(void *semphr) vSemaphoreDelete(item.handle); free(item.buffer); } - - return; #endif } static int32_t IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw) { +#if CONFIG_BTDM_CTRL_HLI + return (int32_t)xSemaphoreTakeFromISR(((hli_queue_handle_t)semphr)->downstream, hptw); +#else return (int32_t)xSemaphoreTakeFromISR(semphr, hptw); +#endif /* CONFIG_BTDM_CTRL_HLI */ } static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw) { +#if CONFIG_BTDM_CTRL_HLI + UNUSED(hptw); + assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE); + return hli_semaphore_give(semphr); +#else return (int32_t)xSemaphoreGiveFromISR(semphr, hptw); +#endif /* CONFIG_BTDM_CTRL_HLI */ } static int32_t semphr_take_wrapper(void *semphr, uint32_t block_time_ms) { + bool ret; +#if CONFIG_BTDM_CTRL_HLI if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { - return (int32_t)xSemaphoreTake(semphr, portMAX_DELAY); + ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, portMAX_DELAY); } else { - return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS); + ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, block_time_ms / portTICK_PERIOD_MS); } +#else + if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { + ret = xSemaphoreTake(semphr, portMAX_DELAY); + } else { + ret = xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS); + } +#endif /* CONFIG_BTDM_CTRL_HLI */ + return (int32_t)ret; } static int32_t semphr_give_wrapper(void *semphr) { +#if CONFIG_BTDM_CTRL_HLI + return (int32_t)xSemaphoreGive(((hli_queue_handle_t)semphr)->downstream); +#else return (int32_t)xSemaphoreGive(semphr); +#endif /* CONFIG_BTDM_CTRL_HLI */ } static void *mutex_create_wrapper(void) @@ -745,6 +888,79 @@ static void queue_delete_wrapper(void *queue) #endif } +#if CONFIG_BTDM_CTRL_HLI +static void *queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size) +{ + QueueHandle_t downstream_queue = queue_create_wrapper(queue_len, item_size); + assert(downstream_queue); + hli_queue_handle_t queue = hli_queue_create(queue_len, item_size, downstream_queue); + assert(queue); + return queue; +} + +static void *customer_queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size) +{ + QueueHandle_t downstream_queue = queue_create_wrapper(queue_len, item_size); + assert(downstream_queue); + hli_queue_handle_t queue = hli_customer_queue_create(queue_len, item_size, downstream_queue); + assert(queue); + return queue; +} + +static void queue_delete_hlevel_wrapper(void *queue) +{ + if (((hli_queue_handle_t)queue)->downstream != NULL) { + queue_delete_wrapper(((hli_queue_handle_t)queue)->downstream); + } + hli_queue_delete(queue); +} + +static int32_t queue_send_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms) +{ + if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { + return (int32_t)xQueueSend(((hli_queue_handle_t)queue)->downstream, item, portMAX_DELAY); + } else { + return (int32_t)xQueueSend(((hli_queue_handle_t)queue)->downstream, item, block_time_ms / portTICK_PERIOD_MS); + } +} + +/** + * Queue send from isr + * @param queue The queue which will send to + * @param item The message which will be send + * @param hptw need do task yield or not + * @return send success or not + * There is an issue here: When the queue is full, it may reture true but it send fail to the queue, sometimes. + * But in Bluetooth controller's isr, We don't care about the return value. + * It only required tp send success when the queue is empty all the time. + * So, this function meets the requirement. + */ +static int32_t IRAM_ATTR queue_send_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw) +{ + UNUSED(hptw); + assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE); + return hli_queue_put(queue, item); +} + +static int32_t queue_recv_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms) +{ + bool ret; + if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { + ret = xQueueReceive(((hli_queue_handle_t)queue)->downstream, item, portMAX_DELAY); + } else { + ret = xQueueReceive(((hli_queue_handle_t)queue)->downstream, item, block_time_ms / portTICK_PERIOD_MS); + } + + return (int32_t)ret; +} + +static int32_t IRAM_ATTR queue_recv_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw) +{ + return (int32_t)xQueueReceiveFromISR(((hli_queue_handle_t)queue)->downstream, item, hptw); +} + +#else + static int32_t queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms) { if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { @@ -760,18 +976,23 @@ static int32_t IRAM_ATTR queue_send_from_isr_wrapper(void *queue, void *item, vo } static int32_t queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms) -{ + { + bool ret; if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) { - return (int32_t)xQueueReceive(queue, item, portMAX_DELAY); + ret = xQueueReceive(queue, item, portMAX_DELAY); } else { - return (int32_t)xQueueReceive(queue, item, block_time_ms / portTICK_PERIOD_MS); + ret = xQueueReceive(queue, item, block_time_ms / portTICK_PERIOD_MS); } -} + + return (int32_t)ret; + } static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, void *hptw) { return (int32_t)xQueueReceiveFromISR(queue, item, hptw); } +#endif /* CONFIG_BTDM_CTRL_HLI */ + static int32_t task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id) { @@ -1317,11 +1538,35 @@ esp_err_t esp_bt_mem_release(esp_bt_mode_t mode) return ESP_OK; } +#if CONFIG_BTDM_CTRL_HLI +static void hli_queue_setup_cb(void* arg) +{ + hli_queue_setup(); +} + +static void hli_queue_setup_pinned_to_core(int core_id) +{ +#if CONFIG_FREERTOS_UNICORE + hli_queue_setup_cb(NULL); +#else /* CONFIG_FREERTOS_UNICORE */ + if (xPortGetCoreID() == core_id) { + hli_queue_setup_cb(NULL); + } else { + esp_ipc_call(core_id, hli_queue_setup_cb, NULL); + } +#endif /* !CONFIG_FREERTOS_UNICORE */ +} +#endif /* CONFIG_BTDM_CTRL_HLI */ + esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) { esp_err_t err; uint32_t btdm_cfg_mask = 0; +#if CONFIG_BTDM_CTRL_HLI + hli_queue_setup_pinned_to_core(CONFIG_BTDM_CTRL_PINNED_TO_CORE); +#endif /* CONFIG_BTDM_CTRL_HLI */ + //if all the bt available memory was already released, cannot initialize bluetooth controller if (btdm_dram_available_region[0].mode == ESP_BT_MODE_IDLE) { return ESP_ERR_INVALID_STATE; @@ -1736,4 +1981,15 @@ esp_err_t esp_ble_scan_dupilcate_list_flush(void) return ESP_OK; } +/** + * This function re-write controller's function, + * As coredump can not show paramerters in function which is in a .a file. + * + * After coredump fixing this issue, just delete this function. + */ +void IRAM_ATTR r_assert(const char *condition, int param0, int param1, const char *file, int line) +{ + __asm__ __volatile__("ill\n"); +} + #endif /* CONFIG_BT_ENABLED */ diff --git a/components/bt/controller/esp32/hli_api.c b/components/bt/controller/esp32/hli_api.c new file mode 100644 index 0000000000..e1444c2bb0 --- /dev/null +++ b/components/bt/controller/esp32/hli_api.c @@ -0,0 +1,306 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD +// +// 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 "esp_log.h" +#include "esp_heap_caps.h" +#include "xtensa/core-macros.h" +#include "soc/dport_reg.h" +#include "hli_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#if CONFIG_BTDM_CTRL_HLI +#define HLI_MAX_HANDLERS 4 + +typedef struct { + intr_handler_t handler; + void* arg; + uint32_t intr_reg; + uint32_t intr_mask; +} hli_handler_info_t; + +typedef struct { +#define CUSTOMER_TYPE_REQUEST (0) +#define CUSTOMER_TYPE_RELEASE (1) + struct { + uint32_t cb_type; + union { + int (* request)(uint32_t, uint32_t, uint32_t); + int (* release)(uint32_t); + } cb; + } customer_cb; + uint32_t arg0, arg1, arg2; +} customer_swisr_t; + +static void IRAM_ATTR customer_swisr_handle(customer_swisr_t *cus_swisr) +{ + if (cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_REQUEST) { + if (cus_swisr->customer_cb.cb.request != NULL) { + cus_swisr->customer_cb.cb.request(cus_swisr->arg0, cus_swisr->arg1, cus_swisr->arg2); + } + } else if(cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_RELEASE) { + if (cus_swisr->customer_cb.cb.release != NULL) { + cus_swisr->customer_cb.cb.release(cus_swisr->arg0); + } + } +} + +static DRAM_ATTR hli_handler_info_t s_hli_handlers[HLI_MAX_HANDLERS]; + +esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask) +{ + for (hli_handler_info_t* hip = s_hli_handlers; + hip < s_hli_handlers + HLI_MAX_HANDLERS; + ++hip) { + if (hip->handler == NULL) { + hip->arg = arg; + hip->intr_reg = intr_reg; + hip->intr_mask = intr_mask; + hip->handler = handler; /* set last, indicates the entry as valid */ + return ESP_OK; + } + } + return ESP_ERR_NO_MEM; +} + +void IRAM_ATTR hli_c_handler(void) +{ + bool handled = false; + /* Iterate over registered interrupt handlers, + * and check if the expected mask is present in the interrupt status register. + */ + for (hli_handler_info_t* hip = s_hli_handlers; + hip < s_hli_handlers + HLI_MAX_HANDLERS; + ++hip) { + if (hip->handler == NULL) { + continue; + } + uint32_t reg = hip->intr_reg; + uint32_t val; + if (reg == 0) { /* special case for CPU internal interrupts */ + val = XTHAL_GET_INTERRUPT(); + } else { + /* "reg" might not be in DPORT, but this will work in any case */ + val = DPORT_REG_READ(reg); + } + if ((val & hip->intr_mask) != 0) { + handled = true; + (*hip->handler)(hip->arg); + } + } + if (!handled) { + /* no handler found, it is OK in this case. */ + } +} + +uint32_t IRAM_ATTR hli_intr_disable(void) +{ + /* disable level 4 and below */ + return XTOS_SET_INTLEVEL(XCHAL_DEBUGLEVEL - 2); +} + +void IRAM_ATTR hli_intr_restore(uint32_t state) +{ + XTOS_RESTORE_JUST_INTLEVEL(state); +} + +#define HLI_META_QUEUE_SIZE 16 +#define HLI_QUEUE_MAX_ELEM_SIZE 32 +#define HLI_QUEUE_SW_INT_NUM 29 + +#define HLI_QUEUE_FLAG_SEMAPHORE BIT(0) +#define HLI_QUEUE_FLAG_CUSTOMER BIT(1) + +static DRAM_ATTR struct hli_queue_t *s_meta_queue_ptr = NULL; +static intr_handle_t ret_handle; + +static inline char* IRAM_ATTR wrap_ptr(hli_queue_handle_t queue, char *ptr) +{ + return (ptr == queue->bufend) ? queue->buf : ptr; +} + +static inline bool IRAM_ATTR queue_empty(hli_queue_handle_t queue) +{ + return queue->begin == queue->end; +} + +static inline bool IRAM_ATTR queue_full(hli_queue_handle_t queue) +{ + return wrap_ptr(queue, queue->end + queue->elem_size) == queue->begin; +} + +static void IRAM_ATTR queue_isr_handler(void* arg) +{ + int do_yield = pdFALSE; + XTHAL_SET_INTCLEAR(BIT(HLI_QUEUE_SW_INT_NUM)); + hli_queue_handle_t queue; + + while (hli_queue_get(s_meta_queue_ptr, &queue)) { + static DRAM_ATTR char scratch[HLI_QUEUE_MAX_ELEM_SIZE]; + while (hli_queue_get(queue, scratch)) { + int res = pdPASS; + if ((queue->flags & HLI_QUEUE_FLAG_CUSTOMER) != 0) { + customer_swisr_handle((customer_swisr_t *)scratch); + } else if ((queue->flags & HLI_QUEUE_FLAG_SEMAPHORE) != 0) { + res = xSemaphoreGiveFromISR((SemaphoreHandle_t) queue->downstream, &do_yield); + } else { + res = xQueueSendFromISR(queue->downstream, scratch, &do_yield); + } + if (res == pdFAIL) { + /* Failed to send to downstream queue, it is OK in this case. */ + } + } + } + if (do_yield) { + portYIELD_FROM_ISR(); + } +} + +/* Notify the level 3 handler that an element is added to the given hli queue. + * Do this by placing the queue handle onto s_meta_queue, and raising a SW interrupt. + * + * This function must be called with HL interrupts disabled! + */ +static void IRAM_ATTR queue_signal(hli_queue_handle_t queue) +{ + /* See if the queue is already in s_meta_queue, before adding */ + bool found = false; + const hli_queue_handle_t *end = (hli_queue_handle_t*) s_meta_queue_ptr->end; + hli_queue_handle_t *item = (hli_queue_handle_t*) s_meta_queue_ptr->begin; + for (;item != end; item = (hli_queue_handle_t*) wrap_ptr(s_meta_queue_ptr, (char*) (item + 1))) { + if (*item == queue) { + found = true; + break; + } + } + if (!found) { + bool res = hli_queue_put(s_meta_queue_ptr, &queue); + if (!res) { + esp_rom_printf(DRAM_STR("Fatal error in queue_signal: s_meta_queue full\n")); + abort(); + } + XTHAL_SET_INTSET(BIT(HLI_QUEUE_SW_INT_NUM)); + } +} + +static void queue_init(hli_queue_handle_t queue, size_t buf_size, size_t elem_size, QueueHandle_t downstream) +{ + queue->elem_size = elem_size; + queue->begin = queue->buf; + queue->end = queue->buf; + queue->bufend = queue->buf + buf_size; + queue->downstream = downstream; + queue->flags = 0; +} + +void hli_queue_setup(void) +{ + if (s_meta_queue_ptr == NULL) { + s_meta_queue_ptr = hli_queue_create(HLI_META_QUEUE_SIZE, sizeof(void*), NULL); + ESP_ERROR_CHECK(esp_intr_alloc(ETS_INTERNAL_SW1_INTR_SOURCE, ESP_INTR_FLAG_IRAM, queue_isr_handler, NULL, &ret_handle)); + xt_ints_on(BIT(HLI_QUEUE_SW_INT_NUM)); + } +} + +void hli_queue_shutdown(void) +{ + if (s_meta_queue_ptr != NULL) { + hli_queue_delete(s_meta_queue_ptr); + s_meta_queue_ptr = NULL; + esp_intr_free(ret_handle); + xt_ints_off(BIT(HLI_QUEUE_SW_INT_NUM)); + } +} + +hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream) +{ + const size_t buf_elem = nelem + 1; + if (elem_size > HLI_QUEUE_MAX_ELEM_SIZE) { + return NULL; + } + size_t buf_size = buf_elem * elem_size; + hli_queue_handle_t res = (hli_queue_handle_t) heap_caps_malloc(sizeof(struct hli_queue_t) + buf_size, + MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); + if (res == NULL) { + return NULL; + } + queue_init(res, buf_size, elem_size, downstream); + return res; +} + +hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream) +{ + hli_queue_handle_t res = hli_queue_create(nelem, elem_size, (QueueHandle_t) downstream); + if (res == NULL) { + return NULL; + } + res->flags |= HLI_QUEUE_FLAG_CUSTOMER; + return res; +} + +hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream) +{ + const size_t elem_size = 1; + hli_queue_handle_t res = hli_queue_create(max_count, elem_size, (QueueHandle_t) downstream); + if (res == NULL) { + return NULL; + } + res->flags |= HLI_QUEUE_FLAG_SEMAPHORE; + return res; +} + +void hli_queue_delete(hli_queue_handle_t queue) +{ + free(queue); +} + +bool IRAM_ATTR hli_queue_get(hli_queue_handle_t queue, void* out) +{ + uint32_t int_state = hli_intr_disable(); + bool res = false; + if (!queue_empty(queue)) { + memcpy(out, queue->begin, queue->elem_size); + queue->begin = wrap_ptr(queue, queue->begin + queue->elem_size); + res = true; + } + hli_intr_restore(int_state); + return res; +} + +bool IRAM_ATTR hli_queue_put(hli_queue_handle_t queue, const void* data) +{ + uint32_t int_state = hli_intr_disable(); + bool res = false; + bool was_empty = queue_empty(queue); + if (!queue_full(queue)) { + memcpy(queue->end, data, queue->elem_size); + queue->end = wrap_ptr(queue, queue->end + queue->elem_size); + if (was_empty && queue != s_meta_queue_ptr) { + queue_signal(queue); + } + res = true; + } + hli_intr_restore(int_state); + return res; +} + +bool IRAM_ATTR hli_semaphore_give(hli_queue_handle_t queue) +{ + uint8_t data = 0; + return hli_queue_put(queue, &data); +} + +#endif /* CONFIG_BTDM_CTRL_HLI */ diff --git a/components/bt/controller/esp32/hli_api.h b/components/bt/controller/esp32/hli_api.h new file mode 100644 index 0000000000..e0049aab32 --- /dev/null +++ b/components/bt/controller/esp32/hli_api.h @@ -0,0 +1,176 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD +// +// 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. + + +#pragma once + +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_BTDM_CTRL_HLI + +/*** Queues ***/ + +struct hli_queue_t +{ + size_t elem_size; + char* begin; + char* end; + const char* bufend; + QueueHandle_t downstream; + int flags; + char buf[0]; +}; + +/** + * @brief Register a high level interrupt function + * + * @param handler interrupt handler function + * @param arg argument to pass to the interrupt handler + * @param intr_reg address of the peripheral register containing the interrupt status, + * or value 0 to get the status from CPU INTERRUPT register + * @param intr_mask mask of the interrupt, in the interrupt status register + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if too many handlers are registered + */ +esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask); + +/** + * @brief Mask all interrupts (including high level ones) on the current CPU + * + * @return uint32_t interrupt status, pass it to hli_intr_restore + */ +uint32_t hli_intr_disable(void); + +/** + * @brief Re-enable interrupts + * + * @param state value returned by hli_intr_disable + */ +void hli_intr_restore(uint32_t state); + +/** + * @brief Type of a hli queue + */ +typedef struct hli_queue_t* hli_queue_handle_t; + +/** + * @brief Initialize hli_queue module. Must be called once before using hli queue APIs. + */ +void hli_queue_setup(void); + +/** + * @brief Shutdown hli_queue module. + */ +void hli_queue_shutdown(void); + +/** + * @brief Create a hli queue, wrapping a FreeRTOS queue + * + * This queue can be used from high level interrupts, + * but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this + * queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3 + * software interrupt. + * + * @param nelem number of elements in the queue + * @param elem_size size of one element; must match element size of a downstream queue + * @param downstream FreeRTOS queue to send the values to + * @return hli_queue_handle_t handle of the created queue, or NULL on failure + */ +hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream); + +/** + * @brief Create a customer hli queue, wrapping a FreeRTOS queue + * + * This queue can be used from high level interrupts, + * but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this + * queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3 + * software interrupt. + * + * @param nelem number of elements in the queue + * @param elem_size size of one element; must match element size of a downstream queue + * @param downstream FreeRTOS queue to send the values to + * @return hli_queue_handle_t handle of the created queue, or NULL on failure + */ +hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream); + +/** + * @brief Create a hli queue, wrapping a FreeRTOS semaphore + * + * See notes on hli_queue_create. + * + * @param max_count maximum semaphore count + * @param downstream FreeRTOS semaphore to forward the calls to + * @return hli_queue_handle_t handle of the created queue, or NULL on failure + */ +hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream); + +/** + * @brief Delete a hli queue + * + * Make sure noone is using the queue before deleting it. + * + * @param queue handle returned by hli_queue_create or hli_semaphore_create + */ +void hli_queue_delete(hli_queue_handle_t queue); + +/** + * @brief Get one element from a hli queue + * + * Usually not used, values get sent to a downstream FreeRTOS queue automatically. + * However if downstream queue is NULL, this API can be used to get values from a hli queue. + * + * @param queue handle of a queue + * @param out pointer where to store the element + * @return true if the element was successfully read from the queue + */ +bool hli_queue_get(hli_queue_handle_t queue, void* out); + +/** + * @brief Put one element into a hli queue + * + * This puts copies an element into the queue and raises a software interrupt (level 3). + * In the interrupt, the value is copied to a FreeRTOS "downstream" queue. + * + * Note that if the value does not fit into a downstream queue, no error is returned, + * and the value is lost. + * + * @param queue handle of a queue + * @param data pointer to the element to be sent + * @return true if data was placed into the hli queue successfully + */ +bool hli_queue_put(hli_queue_handle_t queue, const void* data); + +/** + * @brief "Give" a semaphore wrapped by a hli queue + * + * @param queue handle returned by hli_semaphore_create + * @return true if the event was sent to a hli queue successfully + */ +bool hli_semaphore_give(hli_queue_handle_t queue); + +#endif /* CONFIG_BTDM_CTRL_HLI */ + +#ifdef __cplusplus +} +#endif diff --git a/components/bt/controller/esp32/hli_vectors.S b/components/bt/controller/esp32/hli_vectors.S new file mode 100644 index 0000000000..9b8a0dd9e1 --- /dev/null +++ b/components/bt/controller/esp32/hli_vectors.S @@ -0,0 +1,275 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD +// +// 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 +#include +#include "freertos/xtensa_context.h" +#include "sdkconfig.h" +#include "soc/soc.h" + +#if CONFIG_BTDM_CTRL_HLI + +/* Interrupt stack size, for C code. + * TODO: reduce and make configurable. + */ +#define L4_INTR_STACK_SIZE 4096 + +/* Save area for the CPU state: + * - 64 words for the general purpose registers + * - 7 words for some of the special registers: + * - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed + * - SAR, LBEG, LEND, LCOUNT — since the C code might use these + * - EPC1 — since the C code might cause window overflow exceptions + * This is not laid out as standard exception frame structure + * for simplicity of the save/restore code. + */ +#define REG_FILE_SIZE (64 * 4) +#define SPECREG_OFFSET REG_FILE_SIZE +#define SPECREG_SIZE (7 * 4) +#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE) + + .data +_l4_intr_stack: + .space L4_INTR_STACK_SIZE +_l4_save_ctx: + .space REG_SAVE_AREA_SIZE + + .section .iram1,"ax" + .global xt_highint4 + .type xt_highint4,@function + .align 4 + +xt_highint4: + +#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX + /* + Here, Timer2 is used to count a little time(50us). + The subsequent dram0 write operation is blocked due to live lock, which will + cause timer2 to timeout and trigger a level 5 interrupt. + */ + rsr.ccount a0 + addmi a0, a0, (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ*50) + wsr a0, CCOMPARE2 + + /* Enable Timer 2 interrupt */ + rsr a0, INTENABLE + extui a0, a0, 16, 1 + bnez a0, 1f + movi a0, 0 + xsr a0, INTENABLE /* disable all interrupts */ + /* And a0 with (1 << 16) for Timer 2 interrupt mask */ + addmi a0, a0, (1<<14) + addmi a0, a0, (1<<14) + addmi a0, a0, (1<<14) + addmi a0, a0, (1<<14) + wsr a0, INTENABLE /* Enable Timer 2 */ +1: +#endif + + movi a0, _l4_save_ctx + /* save 4 lower registers */ + s32i a1, a0, 4 + s32i a2, a0, 8 + s32i a3, a0, 12 + rsr a2, EXCSAVE_4 /* holds the value of a0 */ + s32i a2, a0, 0 + + /* Save special registers */ + addi a0, a0, SPECREG_OFFSET + rsr a2, WINDOWBASE + s32i a2, a0, 0 + rsr a2, WINDOWSTART + s32i a2, a0, 4 + rsr a2, SAR + s32i a2, a0, 8 + rsr a2, LBEG + s32i a2, a0, 12 + rsr a2, LEND + s32i a2, a0, 16 + rsr a2, LCOUNT + s32i a2, a0, 20 + rsr a2, EPC1 + s32i a2, a0, 24 + +#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX + movi a0, 0 + xsr a0, INTENABLE /* disable all interrupts */ + movi a2, ~(1<<16) + and a0, a2, a0 + wsr a0, INTENABLE +#endif + + /* disable exception mode, window overflow */ + movi a0, PS_INTLEVEL(5) | PS_EXCM + wsr a0, PS + rsync + + /* Save the remaining physical registers. + * 4 registers are already saved, which leaves 60 registers to save. + * (FIXME: consider the case when the CPU is configured with physical 32 registers) + * These 60 registers are saved in 5 iterations, 12 registers at a time. + */ + movi a1, 5 + movi a3, _l4_save_ctx + 4 * 4 + + /* This is repeated 5 times, each time the window is shifted by 12 registers. + * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused. + */ +1: + s32i a4, a3, 0 + s32i a5, a3, 4 + s32i a6, a3, 8 + s32i a7, a3, 12 + s32i a8, a3, 16 + s32i a9, a3, 20 + s32i a10, a3, 24 + s32i a11, a3, 28 + s32i a12, a3, 32 + s32i a13, a3, 36 + s32i a14, a3, 40 + s32i a15, a3, 44 + + /* We are about to rotate the window, so that a12-a15 will become the new a0-a3. + * Copy a0-a3 to a12-15 to still have access to these values. + * At the same time we can decrement the counter and adjust the save area pointer + */ + + /* a0 is constant (_l4_save_ctx), no need to copy */ + addi a13, a1, -1 /* copy and decrement the downcounter */ + /* a2 is scratch so no need to copy */ + addi a15, a3, 48 /* copy and adjust the save area pointer */ + beqz a13, 2f /* have saved all registers ? */ + rotw 3 /* rotate the window and go back */ + j 1b + + /* the loop is complete */ +2: + rotw 4 /* this brings us back to the original window */ + /* a0 still points to _l4_save_ctx */ + + /* Can clear WINDOWSTART now, all registers are saved */ + rsr a2, WINDOWBASE + /* WINDOWSTART = (1 << WINDOWBASE) */ + movi a3, 1 + ssl a2 + sll a3, a3 + wsr a3, WINDOWSTART + +_highint4_stack_switch: + movi a0, 0 + movi sp, _l4_intr_stack + L4_INTR_STACK_SIZE - 16 + s32e a0, sp, -12 /* For GDB: set null SP */ + s32e a0, sp, -16 /* For GDB: set null PC */ + movi a0, _highint4_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */ + + /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */ + movi a6, PS_INTLEVEL(4) | PS_UM | PS_WOE + wsr a6, PS + rsync + + /* Call C handler */ + mov a6, sp + call4 hli_c_handler + + l32e sp, sp, -12 /* switch back to the original stack */ + + /* Done with C handler; re-enable exception mode, disabling window overflow */ + movi a2, PS_INTLEVEL(5) | PS_EXCM /* TOCHECK */ + wsr a2, PS + rsync + + /* Restore the special registers. + * WINDOWSTART will be restored near the end. + */ + movi a0, _l4_save_ctx + SPECREG_OFFSET + l32i a2, a0, 8 + wsr a2, SAR + l32i a2, a0, 12 + wsr a2, LBEG + l32i a2, a0, 16 + wsr a2, LEND + l32i a2, a0, 20 + wsr a2, LCOUNT + l32i a2, a0, 24 + wsr a2, EPC1 + + /* Restoring the physical registers. + * This is the reverse to the saving process above. + */ + + /* Rotate back to the final window, then start loading 12 registers at a time, + * in 5 iterations. + * Again, a1 is the downcounter and a3 is the save area pointer. + * After each rotation, a1 and a3 are copied from a13 and a15. + * To simplify the loop, we put the initial values into a13 and a15. + */ + rotw -4 + movi a15, _l4_save_ctx + 64 * 4 /* point to the end of the save area */ + movi a13, 5 + +1: + /* Copy a1 and a3 from their previous location, + * at the same time decrementing and adjusting the save area pointer. + */ + addi a1, a13, -1 + addi a3, a15, -48 + + /* Load 12 registers */ + l32i a4, a3, 0 + l32i a5, a3, 4 + l32i a6, a3, 8 + l32i a7, a3, 12 + l32i a8, a3, 16 + l32i a9, a3, 20 + l32i a10, a3, 24 + l32i a11, a3, 28 /* ensure PS and EPC written */ + l32i a12, a3, 32 + l32i a13, a3, 36 + l32i a14, a3, 40 + l32i a15, a3, 44 + + /* Done with the loop? */ + beqz a1, 2f + /* If no, rotate the window and repeat */ + rotw -3 + j 1b + +2: + /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain + * to be restored. Also need to restore WINDOWSTART, since all the general + * registers are now in place. + */ + movi a0, _l4_save_ctx + + l32i a2, a0, SPECREG_OFFSET + 4 + wsr a2, WINDOWSTART + + l32i a1, a0, 4 + l32i a2, a0, 8 + l32i a3, a0, 12 + rsr a0, EXCSAVE_4 /* holds the value of a0 before the interrupt handler */ + + /* Return from the interrupt, restoring PS from EPS_4 */ + rfi 4 + +#endif /* CONFIG_BTDM_CTRL_HLI */ + +/* The linker has no reason to link in this file; all symbols it exports are already defined + (weakly!) in the default int handler. Define a symbol here so we can use it to have the + linker inspect this anyway. */ + + .global ld_include_hli_vectors_bt +ld_include_hli_vectors_bt: diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/env.yml b/components/bt/controller/esp32h2/Kconfig.in similarity index 100% rename from tools/test_apps/protocols/mqtt/publish_connect_test/env.yml rename to components/bt/controller/esp32h2/Kconfig.in diff --git a/components/bt/controller/esp32s2/Kconfig.in b/components/bt/controller/esp32s2/Kconfig.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/bt/controller/lib_esp32 b/components/bt/controller/lib_esp32 index fb49791b7c..fe0a3d00f1 160000 --- a/components/bt/controller/lib_esp32 +++ b/components/bt/controller/lib_esp32 @@ -1 +1 @@ -Subproject commit fb49791b7c1a8a35f06e68124c90022667b4cff1 +Subproject commit fe0a3d00f11dbf9d219f2a291e3cab7419e5cac1 diff --git a/components/bt/controller/lib_esp32c3_family b/components/bt/controller/lib_esp32c3_family index 9ca8afd50a..32f15e826a 160000 --- a/components/bt/controller/lib_esp32c3_family +++ b/components/bt/controller/lib_esp32c3_family @@ -1 +1 @@ -Subproject commit 9ca8afd50afde57958a67fca65847edc52f7d91c +Subproject commit 32f15e826a102d2d64e612620468122ea2234a2e diff --git a/components/bt/esp_ble_mesh/Kconfig.in b/components/bt/esp_ble_mesh/Kconfig.in index 964f52b13f..38e9caa01d 100644 --- a/components/bt/esp_ble_mesh/Kconfig.in +++ b/components/bt/esp_ble_mesh/Kconfig.in @@ -9,10 +9,10 @@ if BLE_MESH config BLE_MESH_USE_DUPLICATE_SCAN bool "Support Duplicate Scan in BLE Mesh" depends on BT_BLUEDROID_ENABLED - select BTDM_BLE_SCAN_DUPL if BT_CTRL_ESP32 - select BTDM_BLE_MESH_SCAN_DUPL_EN if BT_CTRL_ESP32 - select BT_CTRL_BLE_SCAN_DUPL if BT_CTRL_ESP32C3 - select BT_CTRL_BLE_MESH_SCAN_DUPL_EN if BT_CTRL_ESP32C3 + select BTDM_BLE_SCAN_DUPL if IDF_TARGET_ESP32 + select BTDM_BLE_MESH_SCAN_DUPL_EN if IDF_TARGET_ESP32 + select BT_CTRL_BLE_SCAN_DUPL if IDF_TARGET_ESP32C3 + select BT_CTRL_BLE_MESH_SCAN_DUPL_EN if IDF_TARGET_ESP32C3 default y help Enable this option to allow using specific duplicate scan filter diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index 47980eeb28..0b41479221 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -42,7 +42,7 @@ config BT_BLUEDROID_MEM_DEBUG config BT_CLASSIC_ENABLED bool "Classic Bluetooth" - depends on BT_BLUEDROID_ENABLED && BT_CTRL_ESP32 + depends on BT_BLUEDROID_ENABLED && IDF_TARGET_ESP32 default n help For now this option needs "SMP_ENABLE" to be set to yes @@ -99,13 +99,27 @@ config BT_HFP_WBS_ENABLE This enables Wide Band Speech. Should disable it when SCO data path is PCM. Otherwise there will be no data transmited via GPIOs. -config BT_HID_HOST_ENABLED - bool "Classic BT HID Host" +config BT_HID_ENABLED + bool "Classic BT HID" depends on BT_CLASSIC_ENABLED default n help This enables the BT HID Host +choice BT_HID_ROLE + prompt "Profile Role configuration" + depends on BT_HID_ENABLED + config BT_HID_HOST_ENABLED + bool "Classic BT HID Host" + help + This enables the BT HID Host + + config BT_HID_DEVICE_ENABLED + bool "Classic BT HID Device" + help + This enables the BT HID Device +endchoice + config BT_SSP_ENABLED bool "Secure Simple Pairing" depends on BT_CLASSIC_ENABLED @@ -1056,8 +1070,3 @@ config BT_BLE_42_FEATURES_SUPPORTED default n help This enables BLE 4.2 features. - -config BT_RESERVE_DRAM - hex - default 0xdb5c if BT_ENABLED - default 0 diff --git a/components/bt/host/bluedroid/api/esp_hidd_api.c b/components/bt/host/bluedroid/api/esp_hidd_api.c new file mode 100644 index 0000000000..fafe8f8e6e --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_hidd_api.c @@ -0,0 +1,176 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 "esp_err.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" +#include "esp_hidd_api.h" + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t *callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HD, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_device_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t* app_param, esp_hidd_qos_param_t* in_qos, esp_hidd_qos_param_t* out_qos) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.register_app.app_param = app_param; + args.register_app.in_qos = in_qos; + args.register_app.out_qos = out_qos; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_unregister_app(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNREGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + memcpy(args.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_CONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_disconnect(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DISCONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t* data) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_SEND_REPORT_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.send_report.type = type; + args.send_report.id = id; + args.send_report.len = len; + args.send_report.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), btc_hd_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REPORT_ERROR_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.error = error; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNPLUG_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/api/esp_hidh_api.c b/components/bt/host/bluedroid/api/esp_hidh_api.c new file mode 100644 index 0000000000..b62a1a7006 --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_hidh_api.c @@ -0,0 +1,254 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 "btc/btc_manage.h" +#include "btc_hh.h" +#include "esp_bt_main.h" +#include "esp_err.h" +#include "esp_hidh_api.h" +#include + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t *callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HH, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_host_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_INIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_CONNECT_EVT; + + memcpy(arg.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DISCONNECT_EVT; + + memcpy(arg.disconnect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_UNPLUG_EVT; + + memcpy(arg.unplug.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_INFO_EVT; + + memcpy(arg.set_info.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_info.hid_info = hid_info; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_PROTO_EVT; + + memcpy(arg.get_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_PROTO_EVT; + + memcpy(arg.set_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_protocol.protocol_mode = protocol_mode; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_IDLE_EVT; + + memcpy(arg.get_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_IDLE_EVT; + + memcpy(arg.set_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_idle.idle_time = idle_time; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_REPORT_EVT; + + memcpy(arg.get_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.get_report.report_type = report_type; + arg.get_report.report_id = report_id; + arg.get_report.buffer_size = buffer_size; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_REPORT_EVT; + + memcpy(arg.set_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_report.report_type = report_type; + arg.set_report.len = len; + arg.set_report.report = report; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SEND_DATA_EVT; + + memcpy(arg.send_data.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.send_data.len = len; + arg.send_data.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index ce30993585..dd48ab982d 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -467,7 +467,7 @@ typedef struct { uint16_t rx_len; /*!< pkt rx data length value */ uint16_t tx_len; /*!< pkt tx data length value */ -}esp_ble_pkt_data_length_params_t; +} esp_ble_pkt_data_length_params_t; /** * @brief BLE encryption keys @@ -648,7 +648,7 @@ typedef enum { typedef enum{ ESP_BLE_WHITELIST_REMOVE = 0X00, /*!< remove mac from whitelist */ ESP_BLE_WHITELIST_ADD = 0X01, /*!< add address to whitelist */ -}esp_ble_wl_opration_t; +} esp_ble_wl_opration_t; #if (BLE_42_FEATURE_SUPPORT == TRUE) typedef enum { ESP_BLE_DUPLICATE_EXCEPTIONAL_LIST_ADD = 0, /*!< Add device info into duplicate scan exceptional list */ @@ -998,7 +998,7 @@ typedef union { uint16_t conn_int; /*!< Current connection interval */ uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80. Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec */ - }update_conn_params; /*!< Event parameter of ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT */ + } update_conn_params; /*!< Event parameter of ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT */ /** * @brief ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT */ @@ -1018,13 +1018,13 @@ typedef union { struct ble_remove_bond_dev_cmpl_evt_param { esp_bt_status_t status; /*!< Indicate the remove bond device operation success status */ esp_bd_addr_t bd_addr; /*!< The device address which has been remove from the bond list */ - }remove_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT */ + } remove_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT */ struct ble_clear_bond_dev_cmpl_evt_param { esp_bt_status_t status; /*!< Indicate the clear bond device operation success status */ - }clear_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT */ + } clear_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT */ @@ -1032,7 +1032,7 @@ typedef union { esp_bt_status_t status; /*!< Indicate the get bond device operation success status */ uint8_t dev_num; /*!< Indicate the get number device in the bond list */ esp_ble_bond_dev_t *bond_dev; /*!< the pointer to the bond device Structure */ - }get_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT */ + } get_bond_dev_cmpl; /*!< Event parameter of ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT */ /** * @brief ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h index 077cca3850..4071909634 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h @@ -380,7 +380,7 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, /** * @brief Find all the service with the given service uuid in the gattc cache, if the svc_uuid is NULL, find all the service. * Note: It just get service from local cache, won't get from remote devices. If want to get it from remote device, need - * to used the esp_ble_gattc_search_service. + * to used the esp_ble_gattc_cache_refresh, then call esp_ble_gattc_get_service again. * * @param[in] gattc_if: Gatt client access interface. * @param[in] conn_id: connection ID which identify the server. diff --git a/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h new file mode 100644 index 0000000000..de90ff8efc --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h @@ -0,0 +1,379 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 __ESP_HIDD_API_H__ +#define __ESP_HIDD_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* sub_class of hid device */ +#define ESP_HID_CLASS_UNKNOWN (0x00<<2) +#define ESP_HID_CLASS_JOS (0x01<<2) /* joy stick */ +#define ESP_HID_CLASS_GPD (0x02<<2) /* game pad */ +#define ESP_HID_CLASS_RMC (0x03<<2) /* remote control */ +#define ESP_HID_CLASS_SED (0x04<<2) /* sensing device */ +#define ESP_HID_CLASS_DGT (0x05<<2) /* Digitizer tablet */ +#define ESP_HID_CLASS_CDR (0x06<<2) /* card reader */ +#define ESP_HID_CLASS_KBD (0x10<<2) /* keyboard */ +#define ESP_HID_CLASS_MIC (0x20<<2) /* pointing device */ +#define ESP_HID_CLASS_COM (0x30<<2) /* Combo keyboard/pointing */ + +/** + * @brief HIDD handshake error + */ +typedef enum { + ESP_HID_PAR_HANDSHAKE_RSP_SUCCESS = 0, + ESP_HID_PAR_HANDSHAKE_RSP_NOT_READY = 1, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID = 2, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ = 3, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM = 4, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN = 14, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_FATAL = 15 +} esp_hidd_handshake_error_t; + +/** + * @brief HIDD report types + */ +typedef enum { + ESP_HIDD_REPORT_TYPE_OTHER = 0, + ESP_HIDD_REPORT_TYPE_INPUT, + ESP_HIDD_REPORT_TYPE_OUTPUT, + ESP_HIDD_REPORT_TYPE_FEATURE, + // special value for reports to be sent on INTR(INPUT is assumed) + ESP_HIDD_REPORT_TYPE_INTRDATA +} esp_hidd_report_type_t; + +/** + * @brief HIDD connection state + */ +typedef enum { + ESP_HIDD_CONN_STATE_CONNECTED, + ESP_HIDD_CONN_STATE_CONNECTING, + ESP_HIDD_CONN_STATE_DISCONNECTED, + ESP_HIDD_CONN_STATE_DISCONNECTING, + ESP_HIDD_CONN_STATE_UNKNOWN +} esp_hidd_connection_state_t; + +/** + * @brief HID device protocol modes + */ +typedef enum { + ESP_HIDD_REPORT_MODE = 0x00, + ESP_HIDD_BOOT_MODE = 0x01, + ESP_HIDD_UNSUPPORTED_MODE = 0xff +} esp_hidd_protocol_mode_t; + + +/** + * @brief HIDD characteristics for SDP report + */ +typedef struct { + const char *name; + const char *description; + const char *provider; + uint8_t subclass; + uint8_t *desc_list; + int desc_list_len; +} esp_hidd_app_param_t; + +/** + * @brief HIDD Quality of Service parameters + */ +typedef struct { + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t access_latency; + uint32_t delay_variation; +} esp_hidd_qos_param_t; + +/** + * @brief HID device callback function events + */ +typedef enum { + ESP_HIDD_INIT_EVT = 0, /*!< When HID device is inited, the event comes */ + ESP_HIDD_DEINIT_EVT, /*!< When HID device is deinited, the event comes */ + ESP_HIDD_REGISTER_APP_EVT, /*!< When HID device application registered, the event comes */ + ESP_HIDD_UNREGISTER_APP_EVT, /*!< When HID device application unregistered, the event comes */ + ESP_HIDD_OPEN_EVT, /*!< When HID device connection to host opened, the event comes */ + ESP_HIDD_CLOSE_EVT, /*!< When HID device connection to host closed, the event comes */ + ESP_HIDD_SEND_REPORT_EVT, /*!< When HID device send report to lower layer, the event comes */ + ESP_HIDD_REPORT_ERR_EVT, /*!< When HID device report handshanke error to lower layer, the event comes */ + ESP_HIDD_GET_REPORT_EVT, /*!< When HID device receives GET_REPORT request from host, the event comes */ + ESP_HIDD_SET_REPORT_EVT, /*!< When HID device receives SET_REPORT request from host, the event comes */ + ESP_HIDD_SET_PROTOCOL_EVT, /*!< When HID device receives SET_PROTOCOL request from host, the event comes */ + ESP_HIDD_INTR_DATA_EVT, /*!< When HID device receives DATA from host on intr, the event comes */ + ESP_HIDD_VC_UNPLUG_EVT, /*!< When HID device initiates Virtual Cable Unplug, the event comes */ + ESP_HIDD_API_ERR_EVT /*!< When HID device has API error, the event comes */ +} esp_hidd_cb_event_t; + +typedef enum { + ESP_HIDD_SUCCESS, + ESP_HIDD_ERROR, /*!< general ESP HD error */ + ESP_HIDD_NO_RES, /*!< out of system resources */ + ESP_HIDD_BUSY, /*!< Temporarily can not handle this request. */ + ESP_HIDD_NO_DATA, /*!< No data. */ + ESP_HIDD_NEED_INIT, /*!< HIDD module shall init first */ + ESP_HIDD_NEED_DEINIT, /*!< HIDD module shall deinit first */ + ESP_HIDD_NEED_REG, /*!< HIDD module shall register first */ + ESP_HIDD_NEED_DEREG, /*!< HIDD module shall deregister first */ + ESP_HIDD_NO_CONNECTION, /*!< connection may have been closed */ +} esp_hidd_status_t; + +/** + * @brief HID device callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDD_INIT_EVT + */ + struct hidd_init_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } init; /*!< HIDD callback param of ESP_HIDD_INIT_EVT */ + + /** + * @brief ESP_HIDD_DEINIT_EVT + */ + struct hidd_deinit_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } deinit; /*!< HIDD callback param of ESP_HIDD_DEINIT_EVT */ + + /** + * @brief ESP_HIDD_REGISTER_APP_EVT + */ + struct hidd_register_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + bool in_use; /*!< indicate whether use virtual cable plug host address */ + esp_bd_addr_t bd_addr; /*!< host address */ + } register_app; /*!< HIDD callback param of ESP_HIDD_REGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_UNREGISTER_APP_EVT + */ + struct hidd_unregister_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } unregister_app; /*!< HIDD callback param of ESP_HIDD_UNREGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_OPEN_EVT + */ + struct hidd_open_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + esp_bd_addr_t bd_addr; /*!< host address */ + } open; /*!< HIDD callback param of ESP_HIDD_OPEN_EVT */ + + /** + * @brief ESP_HIDD_CLOSE_EVT + */ + struct hidd_close_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } close; /*!< HIDD callback param of ESP_HIDD_CLOSE_EVT */ + + /** + * @brief ESP_HIDD_SEND_REPORT_EVT + */ + struct hidd_send_report_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + } send_report; /*!< HIDD callback param of ESP_HIDD_SEND_REPORT_EVT */ + + /** + * @brief ESP_HIDD_REPORT_ERR_EVT + */ + struct hidd_report_err_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } report_err; /*!< HIDD callback param of ESP_HIDD_REPORT_ERR_EVT */ + + /** + * @brief ESP_HIDD_GET_REPORT_EVT + */ + struct hidd_get_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t buffer_size; /*!< buffer size */ + } get_report; /*!< HIDD callback param of ESP_HIDD_GET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_REPORT_EVT + */ + struct hidd_set_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t len; /*!< set_report data length */ + uint8_t *data; /*!< set_report data pointer */ + } set_report; /*!< HIDD callback param of ESP_HIDD_SET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_PROTOCOL_EVT + */ + struct hidd_set_protocol_evt_param { + esp_hidd_protocol_mode_t protocol_mode; /*!< protocol mode */ + } set_protocol; /*!< HIDD callback param of ESP_HIDD_SET_PROTOCOL_EVT */ + + /** + * @brief ESP_HIDD_INTR_DATA_EVT + */ + struct hidd_intr_data_evt_param { + uint8_t report_id; /*!< interrupt channel report id */ + uint16_t len; /*!< interrupt channel report data length */ + uint8_t *data; /*!< interrupt channel report data pointer */ + } intr_data; /*!< HIDD callback param of ESP_HIDD_INTR_DATA_EVT */ + + /** + * @brief ESP_HIDD_VC_UNPLUG_EVT + */ + struct hidd_vc_unplug_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } vc_unplug; /*!< HIDD callback param of ESP_HIDD_VC_UNPLUG_EVT */ +} esp_hidd_cb_param_t; + +/** + * @brief HID device callback function type. + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (esp_hd_cb_t)(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID device module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t callback); + +/** + * @brief This function initializes HIDD. This function should be called after esp_bluedroid_enable and + * esp_blueroid_init success, and should be called after esp_bt_hid_device_register_callback. + * When the operation is complete the callback function will be called with ESP_HIDD_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_init(void); + +/** + * @brief This function de-initializes HIDD interface. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_device_init(). When the operation is complete the callback + * function will be called with ESP_HIDD_DEINIT_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_deinit(void); + +/** + * @brief Registers HIDD parameters with SDP and sets l2cap Quality of Service. This function should be called after + * esp_bluedroid_enable and esp_blueroid_init success, and must be done after esp_bt_hid_device_init. When the operation is complete the callback + * function will be called with ESP_HIDD_REGISTER_APP_EVT. + * + * @param[in] app_param: HIDD parameters + * @param[in] in_qos: incoming QoS parameters + * @param[in] out_qos: outgoing QoS parameters + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t *app_param, esp_hidd_qos_param_t *in_qos, + esp_hidd_qos_param_t *out_qos); + +/** + * @brief Removes HIDD parameters from SDP and resets l2cap Quality of Service. This function should be called after esp_bluedroid_enable and + * esp_blueroid_init success, and should be called after esp_bt_hid_device_init. When the operation is complete the callback + * function will be called with ESP_HIDD_UNREGISTER_APP_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_unregister_app(void); + +/** + * @brief This function connects HIDD interface to connected bluetooth device, if not done already. When the operation is complete the callback + * function will be called with ESP_HIDD_OPEN_EVT. + * + * @param[in] bd_addr: Remote host bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr); + +/** + * @brief This function disconnects HIDD interface. When the operation is complete the callback + * function will be called with ESP_HIDD_CLOSE_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_disconnect(void); + +/** + * @brief Send HIDD report. When the operation is complete the callback + * function will be called with ESP_HIDD_SEND_REPORT_EVT. + * + * @param[in] type: type of report + * @param[in] id: report id as defined by descriptor + * @param[in] len: length of report + * @param[in] data: report data + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *data); + +/** + * @brief Sends HIDD handshake with error info for invalid set_report. When the operation is complete the callback + * function will be called with ESP_HIDD_REPORT_ERR_EVT. + * + * @param[in] error: type of error + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error); + +/** + * @brief Unplug virtual cable of HIDD. When the operation is complete the callback + * function will be called with ESP_HIDD_VC_UNPLUG_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h b/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h new file mode 100644 index 0000000000..07abdece2d --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h @@ -0,0 +1,465 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 __ESP_HIDH_API_H__ +#define __ESP_HIDH_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BTHH_MAX_DSC_LEN 884 + +/** + * @brief HID host connection state + */ +typedef enum { + ESP_HIDH_CONN_STATE_CONNECTED = 0, /*!< connected state */ + ESP_HIDH_CONN_STATE_CONNECTING, /*!< connecting state */ + ESP_HIDH_CONN_STATE_DISCONNECTED, /*!< disconnected state */ + ESP_HIDH_CONN_STATE_DISCONNECTING, /*!< disconnecting state */ + ESP_HIDH_CONN_STATE_UNKNOWN /*!< unknown state(initial state) */ +} esp_hidh_connection_state_t; + +typedef enum { + ESP_HIDH_OK, + ESP_HIDH_HS_HID_NOT_READY, /*!< handshake error : device not ready */ + ESP_HIDH_HS_INVALID_RPT_ID, /*!< handshake error : invalid report ID */ + ESP_HIDH_HS_TRANS_NOT_SPT, /*!< handshake error : transaction not spt */ + ESP_HIDH_HS_INVALID_PARAM, /*!< handshake error : invalid paremter */ + ESP_HIDH_HS_ERROR, /*!< handshake error : unspecified HS error */ + ESP_HIDH_ERR, /*!< general ESP HH error */ + ESP_HIDH_ERR_SDP, /*!< SDP error */ + ESP_HIDH_ERR_PROTO, /*!< SET_Protocol error, + only used in ESP_HIDH_OPEN_EVT callback */ + + ESP_HIDH_ERR_DB_FULL, /*!< device database full error, used in + ESP_HIDH_OPEN_EVT/ESP_HIDH_ADD_DEV_EVT */ + ESP_HIDH_ERR_TOD_UNSPT, /*!< type of device not supported */ + ESP_HIDH_ERR_NO_RES, /*!< out of system resources */ + ESP_HIDH_ERR_AUTH_FAILED, /*!< authentication fail */ + ESP_HIDH_ERR_HDL, /*!< connection handle error */ + ESP_HIDH_ERR_SEC, /*!< encryption error */ + // self_defined + ESP_HIDH_BUSY, /*!< Temporarily can not handle this request. */ + ESP_HIDH_NO_DATA, /*!< No data. */ + ESP_HIDH_NEED_INIT, /*!< HIDH module shall init first */ + ESP_HIDH_NEED_DEINIT, /*!< HIDH module shall deinit first */ + ESP_HIDH_NO_CONNECTION, /*!< connection may have been closed */ +} esp_hidh_status_t; + +/** + * @brief HID host protocol modes + */ +typedef enum { + ESP_HIDH_BOOT_MODE = 0x00, /*!< boot protocol mode */ + ESP_HIDH_REPORT_MODE = 0x01, /*!< report protocol mode */ + ESP_HIDH_UNSUPPORTED_MODE = 0xff /*!< unsupported protocol mode */ +} esp_hidh_protocol_mode_t; + +/** + * @brief HID host report types + */ +typedef enum { + ESP_HIDH_REPORT_TYPE_OTHER = 0, /*!< unsupported report type */ + ESP_HIDH_REPORT_TYPE_INPUT, /*!< input report type */ + ESP_HIDH_REPORT_TYPE_OUTPUT, /*!< output report type */ + ESP_HIDH_REPORT_TYPE_FEATURE, /*!< feature report type */ +} esp_hidh_report_type_t; + +/** + * @brief HID host callback function events + */ +typedef enum { + ESP_HIDH_INIT_EVT = 0, /*!< When HID host is inited, the event comes */ + ESP_HIDH_DEINIT_EVT, /*!< When HID host is deinited, the event comes */ + ESP_HIDH_OPEN_EVT, /*!< When HID host connection opened, the event comes */ + ESP_HIDH_CLOSE_EVT, /*!< When HID host connection closed, the event comes */ + ESP_HIDH_GET_RPT_EVT, /*!< When Get_Report command is called, the event comes */ + ESP_HIDH_SET_RPT_EVT, /*!< When Set_Report command is called, the event comes */ + ESP_HIDH_GET_PROTO_EVT, /*!< When Get_Protocol command is called, the event comes */ + ESP_HIDH_SET_PROTO_EVT, /*!< When Set_Protocol command is called, the event comes */ + ESP_HIDH_GET_IDLE_EVT, /*!< When Get_Idle command is called, the event comes */ + ESP_HIDH_SET_IDLE_EVT, /*!< When Set_Idle command is called, the event comes */ + ESP_HIDH_GET_DSCP_EVT, /*!< When HIDH is inited, the event comes */ + ESP_HIDH_ADD_DEV_EVT, /*!< When a device is added, the event comes */ + ESP_HIDH_RMV_DEV_EVT, /*!< When a device is removed, the event comes */ + ESP_HIDH_VC_UNPLUG_EVT, /*!< When virtually unplugged, the event comes */ + ESP_HIDH_DATA_EVT, /*!< When send data on interrupt channel, the event comes */ + ESP_HIDH_DATA_IND_EVT, /*!< When receive data on interrupt channel, the event comes */ + ESP_HIDH_SET_INFO_EVT /*!< When set the HID device descriptor, the event comes */ +} esp_hidh_cb_event_t; + +typedef struct { + int attr_mask; + uint8_t sub_class; + uint8_t app_id; + int vendor_id; + int product_id; + int version; + uint8_t ctry_code; + int dl_len; + uint8_t dsc_list[BTHH_MAX_DSC_LEN]; +} esp_hidh_hid_info_t; + +/** + * @brief HID host callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDH_INIT_EVT + */ + struct hidh_init_evt_param { + esp_hidh_status_t status; /*!< status */ + } init; /*!< HIDH callback param of ESP_HIDH_INIT_EVT */ + + /** + * @brief ESP_HIDH_DEINIT_EVT + */ + struct hidh_uninit_evt_param { + esp_hidh_status_t status; /*!< status */ + } deinit; /*!< HIDH callback param of ESP_HIDH_DEINIT_EVT */ + + /** + * @brief ESP_HIDH_OPEN_EVT + */ + struct hidh_open_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + bool is_orig; /*!< indicate if host intiate the connection */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } open; /*!< HIDH callback param of ESP_HIDH_OPEN_EVT */ + + /** + * @brief ESP_HIDH_CLOSE_EVT + */ + struct hidh_close_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } close; /*!< HIDH callback param of ESP_HIDH_CLOSE_EVT */ + + /** + * @brief ESP_HIDH_VC_UNPLUG_EVT + */ + struct hidh_unplug_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } unplug; /*!< HIDH callback param of ESP_HIDH_VC_UNPLUG_EVT */ + + /** + * @brief ESP_HIDH_GET_PROTO_EVT + */ + struct hidh_get_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + } get_proto; /*!< HIDH callback param of ESP_HIDH_GET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_SET_PROTO_EVT + */ + struct hidh_set_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_proto; /*!< HIDH callback param of ESP_HIDH_SET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_GET_RPT_EVT + */ + struct hidh_get_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } get_rpt; /*!< HIDH callback param of ESP_HIDH_GET_RPT_EVT */ + + /** + * @brief ESP_HIDH_SET_RPT_EVT + */ + struct hidh_set_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_rpt; /*!< HIDH callback param of ESP_HIDH_SET_RPT_EVT */ + + /** + * @brief ESP_HIDH_DATA_EVT + */ + struct hidh_send_data_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } send_data; /*!< HIDH callback param of ESP_HIDH_DATA_EVT */ + + /** + * @brief ESP_HIDH_GET_IDLE_EVT + */ + struct hidh_get_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t idle_rate; /*!< idle rate */ + } get_idle; /*!< HIDH callback param of ESP_HIDH_GET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_SET_IDLE_EVT + */ + struct hidh_set_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_idle; /*!< HIDH callback param of ESP_HIDH_SET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_DATA_IND_EVT + */ + struct hidh_data_ind_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } data_ind; /*!< HIDH callback param of ESP_HIDH_DATA_IND_EVT */ + + /** + * @brief ESP_HIDH_ADD_DEV_EVT + */ + struct hidh_add_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } add_dev; /*!< HIDH callback param of ESP_HIDH_ADD_DEV_EVT */ + + /** + * @brief ESP_HIDH_RMV_DEV_EVT + */ + struct hidh_rmv_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } rmv_dev; /*!< HIDH callback param of ESP_HIDH_RMV_DEV_EVT */ + + /** + * @brief ESP_HIDH_GET_DSCP_EVT + */ + struct hidh_get_dscp_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + bool added; /*!< Indicate if added */ + uint16_t vendor_id; /*!< Vendor ID */ + uint16_t product_id; /*!< Product ID */ + uint16_t version; /*!< Version */ + uint16_t ssr_max_latency; /*!< SSR max latency */ + uint16_t ssr_min_tout; /*!< SSR min timeout */ + uint8_t ctry_code; /*!< Country Code */ + uint16_t dl_len; /*!< Device descriptor length */ + uint8_t *dsc_list; /*!< Device descriptor pointer */ + } dscp; /*!< HIDH callback param of ESP_HIDH_GET_DSCP_EVT */ + + /** + * @brief ESP_HIDH_SET_INFO_EVT + */ + struct hidh_set_info_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } set_info; /*!< HIDH callback param of ESP_HIDH_SET_INFO_EVT */ +} esp_hidh_cb_param_t; + +/** + * @brief HID host callback function type + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (esp_hh_cb_t)(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID host module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t callback); + +/** + * @brief This function initializes HID host. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_host_register_callback(). + * When the operation is complete the callback function will be called with ESP_HIDH_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_init(void); + +/** + * @brief Closes the interface. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_host_init(). + * When the operation is complete the callback function will be called with ESP_HIDH_DEINIT_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_deinit(void); + +/** + * @brief Connect to hid device. When the operation is complete the callback + * function will be called with ESP_HIDH_OPEN_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr); + +/** + * @brief Disconnect from hid device. When the operation is complete the callback + * function will be called with ESP_HIDH_CLOSE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr); + +/** + * @brief Virtual UnPlug (VUP) the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_VC_UNPLUG_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID device descriptor for the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_INFO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] hid_info: HID device descriptor structure. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info); + +/** + * @brief Get the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] protocol_mode: Protocol mode type. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode); + +/** + * @brief Get the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] idle_time: Idle time rate + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time); + +/** + * @brief Send a GET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report_id: Report id + * @param[in] buffer_size: Buffer size + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size); + +/** + * @brief Send a SET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report: Report data pointer + * @param[in] len: Report data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len); + +/** + * @brief Send data to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_DATA_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] data: Data pointer + * @param[in] len: Data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/bt/host/bluedroid/bta/dm/bta_dm_act.c b/components/bt/host/bluedroid/bta/dm/bta_dm_act.c index 75bc12c60a..7b95f520b4 100644 --- a/components/bt/host/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/host/bluedroid/bta/dm/bta_dm_act.c @@ -126,7 +126,7 @@ static void bta_dm_ctrl_features_rd_cmpl_cback(tBTM_STATUS result); #endif #endif #if (SMP_INCLUDED == TRUE) -static void bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr); +static BOOLEAN bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr); #endif ///SMP_INCLUDED == TRUE #if (BLE_INCLUDED == TRUE) static void bta_dm_observe_results_cb(tBTM_INQ_RESULTS *p_inq, UINT8 *p_eir); @@ -3069,7 +3069,9 @@ static UINT8 bta_dm_authentication_complete_cback(BD_ADDR bd_addr, DEV_CLASS dev bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event); } - bta_dm_remove_sec_dev_entry(bd_addr); + if (bta_dm_remove_sec_dev_entry(bd_addr)) { + return BTM_SEC_DEV_REC_REMOVED; + } } return BTM_SUCCESS; @@ -3740,12 +3742,13 @@ static void bta_dm_delay_role_switch_cback(TIMER_LIST_ENT *p_tle) ** remtoe device does not exist, else schedule for dev entry removal upon ACL close ** -** Returns void +** Returns TRUE if device entry is removed from Security device DB, FALSE otherwise ** *******************************************************************************/ #if (SMP_INCLUDED == TRUE) -static void bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr) +static BOOLEAN bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr) { + BOOLEAN is_device_deleted = FALSE; UINT16 index = 0; if ( BTM_IsAclConnectionUp(remote_bd_addr, BT_TRANSPORT_LE) || BTM_IsAclConnectionUp(remote_bd_addr, BT_TRANSPORT_BR_EDR)) { @@ -3763,7 +3766,7 @@ static void bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr) APPL_TRACE_ERROR(" %s Device does not exist in DB", __FUNCTION__); } } else { - BTM_SecDeleteDevice (remote_bd_addr, bta_dm_cb.device_list.peer_device[index].transport); + is_device_deleted = BTM_SecDeleteDevice (remote_bd_addr, bta_dm_cb.device_list.peer_device[index].transport); #if (BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE) /* need to remove all pending background connection */ BTA_GATTC_CancelOpen(0, remote_bd_addr, FALSE); @@ -3771,6 +3774,7 @@ static void bta_dm_remove_sec_dev_entry(BD_ADDR remote_bd_addr) BTA_GATTC_Refresh(remote_bd_addr, false); #endif } + return is_device_deleted; } #endif ///SMP_INCLUDED == TRUE @@ -4498,6 +4502,22 @@ void bta_dm_set_encryption (tBTA_DM_MSG *p_data) } #endif ///SMP_INCLUDED == TRUE +#if (BTA_HD_INCLUDED == TRUE) +BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr) +{ + APPL_TRACE_DEBUG("%s: count(%d)", __func__, bta_dm_conn_srvcs.count); + for (uint8_t j = 0; j < bta_dm_conn_srvcs.count; j++) { + // Check if profiles other than hid are connected + if ((bta_dm_conn_srvcs.conn_srvc[j].id != BTA_ID_HD) && + !bdcmp(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr)) { + APPL_TRACE_DEBUG("%s: Another profile (id=%d) is connected", __func__, bta_dm_conn_srvcs.conn_srvc[j].id); + return FALSE; + } + } + return TRUE; +} +#endif /* BTA_HD_INCLUDED == TRUE */ + #if (BLE_INCLUDED == TRUE) /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c b/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c index 5735941de1..31047b1ae3 100644 --- a/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c +++ b/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c @@ -117,11 +117,11 @@ tBTA_DM_CFG *const p_bta_dm_cfg = (tBTA_DM_CFG *) &bta_dm_cfg; tBTA_DM_RM *const p_bta_dm_rm_cfg = (tBTA_DM_RM *) &bta_dm_rm_cfg; #if BLE_INCLUDED == TRUE +# define BTA_DM_NUM_PM_ENTRY 10 /* number of entries in bta_dm_pm_cfg except the first */ +# define BTA_DM_NUM_PM_SPEC 10 /* number of entries in bta_dm_pm_spec */ +#else # define BTA_DM_NUM_PM_ENTRY 8 /* number of entries in bta_dm_pm_cfg except the first */ # define BTA_DM_NUM_PM_SPEC 8 /* number of entries in bta_dm_pm_spec */ -#else -# define BTA_DM_NUM_PM_ENTRY 6 /* number of entries in bta_dm_pm_cfg except the first */ -# define BTA_DM_NUM_PM_SPEC 6 /* number of entries in bta_dm_pm_spec */ #endif #if (BTA_DM_PM_INCLUDED == TRUE) @@ -133,10 +133,12 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_CFG bta_dm_pm_cfg[BTA_DM_NUM_PM_ENTRY + 1] {BTA_ID_JV, BTA_APP_ID_1, 2}, /* app BTA_JV_PM_ID_1, reuse ftc spec table */ {BTA_ID_JV, BTA_ALL_APP_ID, 3}, /* reuse fts spec table */ {BTA_ID_HS, BTA_ALL_APP_ID, 4}, /* HS spec table */ - {BTA_ID_AVK, BTA_ALL_APP_ID, 5} /* avk spec table */ + {BTA_ID_AVK, BTA_ALL_APP_ID, 5}, /* avk spec table */ + {BTA_ID_HD, BTA_ALL_APP_ID, 6}, /* hd spec table */ + {BTA_ID_HH, BTA_ALL_APP_ID, 7} /* hh spec table */ #if BLE_INCLUDED == TRUE - , {BTA_ID_GATTC, BTA_ALL_APP_ID, 6} /* gattc spec table */ - , {BTA_ID_GATTS, BTA_ALL_APP_ID, 7} /* gatts spec table */ + , {BTA_ID_GATTC, BTA_ALL_APP_ID, 8} /* gattc spec table */ + , {BTA_ID_GATTS, BTA_ALL_APP_ID, 9} /* gatts spec table */ #endif }; @@ -254,10 +256,48 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = { {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ } + }, + + /* HD : 6 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR3), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_HD_IDLE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* HH : 7 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR1), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HH_OPEN_IDX, BTA_DM_PM_HH_OPEN_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close, used for HH suspend */ + {{BTA_DM_PM_SNIFF_HH_IDLE_IDX, BTA_DM_PM_HH_IDLE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HH_ACTIVE_IDX, BTA_DM_PM_HH_ACTIVE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } } #if BLE_INCLUDED == TRUE - /* GATTC : 6 */ + /* GATTC : 8 */ , { (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ #if (BTM_SSR_INCLUDED == TRUE) @@ -278,7 +318,7 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = { {{BTA_DM_PM_RETRY, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ } } - /* GATTS : 7 */ + /* GATTS : 9 */ , { (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ #if (BTM_SSR_INCLUDED == TRUE) @@ -372,7 +412,7 @@ tBTA_DM_SSR_SPEC bta_dm_ssr_spec[] = { seting default max latency and min remote timeout as 0, and always read individual device preference from HH module */ {1200, 2, 2}, /* BTA_DM_PM_SSR2 - others (as long as sniff is allowed)*/ - {360, 160, 2} /* BTA_DM_PM_SSR3 - HD */ + {360, 160, 1600} /* BTA_DM_PM_SSR3 - HD */ }; tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec = (tBTA_DM_SSR_SPEC *) &bta_dm_ssr_spec; diff --git a/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h index 8dfd26af5e..8d2f106d0d 100644 --- a/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h @@ -26,6 +26,7 @@ #include "common/bt_target.h" #include "freertos/semphr.h" +#include "bta/bta_sys.h" #if (BLE_INCLUDED == TRUE && (defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE)) #include "bta/bta_gatt_api.h" #endif @@ -1596,6 +1597,11 @@ extern void bta_dm_ble_get_energy_info(tBTA_DM_MSG *p_data); extern void bta_dm_set_encryption(tBTA_DM_MSG *p_data); extern void bta_dm_confirm(tBTA_DM_MSG *p_data); extern void bta_dm_key_req(tBTA_DM_MSG *p_data); + +#if (BTA_HD_INCLUDED == TRUE) +extern BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr); +#endif /* BTA_HD_INCLUDED */ + #if (BTM_OOB_INCLUDED == TRUE) extern void bta_dm_loc_oob(tBTA_DM_MSG *p_data); extern void bta_dm_oob_reply(tBTA_DM_MSG *p_data); diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c index 9f5417bf76..f35851c60b 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c @@ -35,6 +35,7 @@ #include "gatt_int.h" #include "osi/allocator.h" #include "osi/mutex.h" +#include "bta_hh_int.h" #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) #include "bta_hh_int.h" @@ -304,7 +305,10 @@ void bta_gattc_deregister(tBTA_GATTC_CB *p_cb, tBTA_GATTC_RCB *p_clreg) bta_gattc_deregister_cmpl(p_clreg); } } else { - APPL_TRACE_ERROR("bta_gattc_deregister Deregister Failedm unknown client cif"); + APPL_TRACE_ERROR("Deregister Failed unknown client cif"); +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + bta_hh_cleanup_disable(BTA_HH_OK); +#endif } } /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_act.c b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c new file mode 100644 index 0000000000..addb585a1d --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c @@ -0,0 +1,774 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID device action functions. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_sys.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include + +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata); + +static bool check_descriptor(uint8_t *data, uint16_t length, bool *has_report_id) +{ + uint8_t *ptr = data; + *has_report_id = FALSE; + while (ptr < data + length) { + uint8_t item = *ptr++; + switch (item) { + case 0xfe: // long item indicator + if (ptr < data + length) { + ptr += ((*ptr) + 2); + } else { + return false; + } + break; + case 0x85: // Report ID + *has_report_id = TRUE; + default: + ptr += (item & 0x03); + break; + } + } + return (ptr == data + length); +} + +/******************************************************************************* + * + * Function bta_hd_api_enable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_enable(tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + HID_DevInit(); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); + + HID_DevSetSecurityLevel(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + /* store parameters */ + bta_hd_cb.p_cback = p_data->api_enable.p_cback; + + ret = HID_DevRegister(bta_hd_cback); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to register HID device (%d)", __func__, ret); + } + + /* signal BTA call back event */ + (*bta_hd_cb.p_cback)(BTA_HD_ENABLE_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_api_disable + * + * Description Disables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_disable(void) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + /* service is not enabled */ + if (bta_hd_cb.p_cback == NULL) + return; + + /* Remove service record */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + } + + /* Deregister with lower layer */ + ret = HID_DevDeregister(); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to deregister HID device (%d)", __func__, ret); + } + + (*bta_hd_cb.p_cback)(BTA_HD_DISABLE_EVT, (tBTA_HD *)&status); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); +} + +/******************************************************************************* + * + * Function bta_hd_register_act + * + * Description Registers SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_register_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD ret; + tBTA_HD_REGISTER_APP *p_app_data = (tBTA_HD_REGISTER_APP *)p_data; + bool use_report_id = FALSE; + + APPL_TRACE_API("%s", __func__); + + ret.reg_status.in_use = FALSE; + + /* Check if len doesn't exceed BTA_HD_APP_DESCRIPTOR_LEN and descriptor + * itself is well-formed. Also check if descriptor has Report Id item so we + * know if report will have prefix or not. */ + if (p_app_data->d_len > BTA_HD_APP_DESCRIPTOR_LEN || + !check_descriptor(p_app_data->d_data, p_app_data->d_len, &use_report_id)) { + APPL_TRACE_ERROR("%s: Descriptor is too long or malformed", __func__); + ret.reg_status.status = BTA_HD_ERROR; + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); + return; + } + + ret.reg_status.status = BTA_HD_OK; + + /* Remove old record if for some reason it's already registered */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.use_report_id = use_report_id; + bta_hd_cb.sdp_handle = SDP_CreateRecord(); + HID_DevAddRecord(bta_hd_cb.sdp_handle, p_app_data->name, p_app_data->description, p_app_data->provider, + p_app_data->subclass, p_app_data->d_len, p_app_data->d_data); + bta_sys_add_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + HID_DevSetIncomingQos(p_app_data->in_qos.service_type, p_app_data->in_qos.token_rate, + p_app_data->in_qos.token_bucket_size, p_app_data->in_qos.peak_bandwidth, + p_app_data->in_qos.access_latency, p_app_data->in_qos.delay_variation); + + HID_DevSetOutgoingQos(p_app_data->out_qos.service_type, p_app_data->out_qos.token_rate, + p_app_data->out_qos.token_bucket_size, p_app_data->out_qos.peak_bandwidth, + p_app_data->out_qos.access_latency, p_app_data->out_qos.delay_variation); + + // application is registered so we can accept incoming connections + HID_DevSetIncomingPolicy(TRUE); + + if (HID_DevGetDevice(&ret.reg_status.bda) == HID_SUCCESS) { + ret.reg_status.in_use = TRUE; + } + + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); +} + +/******************************************************************************* + * + * Function bta_hd_unregister_act + * + * Description Unregisters SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_OK; + + APPL_TRACE_API("%s", __func__); + + // application is no longer registered so we do not want incoming connections + HID_DevSetIncomingPolicy(FALSE); + + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.sdp_handle = 0; + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + (*bta_hd_cb.p_cback)(BTA_HD_UNREGISTER_APP_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_unregister2_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister2_act(tBTA_HD_DATA *p_data) +{ + APPL_TRACE_API("%s", __func__); + + // close first + bta_hd_close_act(p_data); + + // then unregister + bta_hd_unregister_act(p_data); + + if (bta_hd_cb.disable_w4_close) { + bta_hd_api_disable(); + } +} + +/******************************************************************************* + * + * Function bta_hd_connect_act + * + * Description Connect to device (must be virtually plugged) + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + do { + ret = HID_DevPlugDevice(p_ctrl->addr); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevPlugDevice returned %d", __func__, ret); + return; + } + + ret = HID_DevConnect(); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevConnect returned %d", __func__, ret); + return; + } + } while (0); + + bdcpy(cback_data.conn.bda, p_ctrl->addr); + cback_data.conn.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTING; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_disconnect_act + * + * Description Disconnect from device + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_disconnect_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + ret = HID_DevDisconnect(); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevDisconnect returned %d", __func__, ret); + return; + } + + if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) { + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTING; + bta_hd_cb.p_cback(BTA_HD_CLOSE_EVT, &cback_data); + } +} + +/******************************************************************************* + * + * Function bta_hd_add_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_remove_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevUnplugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_send_report_act + * + * Description Sends report + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_SEND_REPORT *p_report = (tBTA_HD_SEND_REPORT *)p_data; + uint8_t channel; + uint8_t report_id; + tBTA_HD cback_data; + tHID_STATUS ret; + + APPL_TRACE_VERBOSE("%s", __func__); + + channel = p_report->use_intr ? HID_CHANNEL_INTR : HID_CHANNEL_CTRL; + report_id = (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) ? p_report->id : 0x00; + + ret = HID_DevSendReport(channel, p_report->type, report_id, p_report->len, p_report->data); + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + + cback_data.send_report.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.send_report.reason = ret; + cback_data.send_report.report_id = report_id; + cback_data.send_report.report_type = p_report->type; + bta_hd_cb.p_cback(BTA_HD_SEND_REPORT_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_report_error_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_REPORT_ERR *p_report = (tBTA_HD_REPORT_ERR *)p_data; + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s: error = %d", __func__, p_report->error); + + ret = HID_DevReportError(p_report->error); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevReportError returned %d", __func__, ret); + } + + cback_data.report_err.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.report_err.reason = ret; + bta_hd_cb.p_cback(BTA_HD_REPORT_ERR_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_act + * + * Description Sends Virtual Cable Unplug + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.vc_unplug = TRUE; + ret = HID_DevVirtualCableUnplug(); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); + } + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); +} + +/******************************************************************************* + * + * Function bta_hd_open_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_open_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_cback->addr); + bta_sys_conn_open(BTA_ID_HD, 1, p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTED; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_close_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_close_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + tBTA_HD_EVT cback_event = BTA_HD_CLOSE_EVT; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + if (bta_hd_cb.vc_unplug) { + bta_hd_cb.vc_unplug = FALSE; + HID_DevUnplugDevice(p_cback->addr); + cback_event = BTA_HD_VC_UNPLUG_EVT; + } + + bdcpy(cback_data.conn.bda, p_cback->addr); + memset(bta_hd_cb.bd_addr, 0, sizeof(BD_ADDR)); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + bta_hd_cb.p_cback(cback_event, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_intr_data_act + * + * Description Handles incoming DATA request on intr + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_INTR_DATA ret; + + APPL_TRACE_API("%s", __func__); + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_INTR_DATA_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_get_report_act + * + * Description Handles incoming GET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + bool rep_size_follows = p_cback->data; + BT_HDR *p_msg = p_cback->p_data; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_GET_REPORT ret = {0, 0, 0}; + uint16_t remaining_len = p_msg->len; + + APPL_TRACE_API("%s", __func__); + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + remaining_len--; + + if (bta_hd_cb.use_report_id) { + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.report_id = *p_buf; + p_buf++; + remaining_len--; + } + + if (rep_size_follows) { + if (remaining_len < 2) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.buffer_size = *p_buf | (*(p_buf + 1) << 8); + } + + (*bta_hd_cb.p_cback)(BTA_HD_GET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_report_act + * + * Description Handles incoming SET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_SET_REPORT ret = {0, 0, 0, NULL}; + + APPL_TRACE_API("%s", __func__); + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + len--; + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_SET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_protocol_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.boot_mode = (p_cback->data == HID_PAR_PROTOCOL_BOOT_MODE); + cback_data.set_protocol = p_cback->data; + + (*bta_hd_cb.p_cback)(BTA_HD_SET_PROTOCOL_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_done_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + HID_DevUnplugDevice(p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + (*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_exit_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_busy(BTA_ID_HD, 1, p_cback->addr); + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_cback + * + * Description BTA HD callback function + * + * Returns void + * + ******************************************************************************/ +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata) +{ + tBTA_HD_CBACK_DATA *p_buf = NULL; + uint16_t sm_event = BTA_HD_INVALID_EVT; + + APPL_TRACE_API("%s: event=%d", __func__, event); + + switch (event) { + case HID_DHOST_EVT_OPEN: + sm_event = BTA_HD_INT_OPEN_EVT; + break; + + case HID_DHOST_EVT_CLOSE: + sm_event = BTA_HD_INT_CLOSE_EVT; + break; + + case HID_DHOST_EVT_GET_REPORT: + sm_event = BTA_HD_INT_GET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_REPORT: + sm_event = BTA_HD_INT_SET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_PROTOCOL: + sm_event = BTA_HD_INT_SET_PROTOCOL_EVT; + break; + + case HID_DHOST_EVT_INTR_DATA: + sm_event = BTA_HD_INT_INTR_DATA_EVT; + break; + + case HID_DHOST_EVT_VC_UNPLUG: + sm_event = BTA_HD_INT_VC_UNPLUG_EVT; + break; + + case HID_DHOST_EVT_SUSPEND: + sm_event = BTA_HD_INT_SUSPEND_EVT; + break; + + case HID_DHOST_EVT_EXIT_SUSPEND: + sm_event = BTA_HD_INT_EXIT_SUSPEND_EVT; + break; + } + + if (sm_event != BTA_HD_INVALID_EVT && + (p_buf = (tBTA_HD_CBACK_DATA *)osi_malloc(sizeof(tBTA_HD_CBACK_DATA) + sizeof(BT_HDR))) != NULL) { + p_buf->hdr.event = sm_event; + bdcpy(p_buf->addr, bd_addr); + p_buf->data = data; + p_buf->p_data = pdata; + + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_api.c b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c new file mode 100644 index 0000000000..33875b957a --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c @@ -0,0 +1,287 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID DEVICE API in the subsystem of BTA. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include +#include +#include + +/***************************************************************************** + * Constants + ****************************************************************************/ +static const tBTA_SYS_REG bta_hd_reg = {bta_hd_hdl_event, BTA_HdDisable}; +/******************************************************************************* + * + * Function BTA_HdEnable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void BTA_HdEnable(tBTA_HD_CBACK *p_cback) +{ + tBTA_HD_API_ENABLE *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_register(BTA_ID_HD, &bta_hd_reg); + p_buf = (tBTA_HD_API_ENABLE *)osi_malloc((uint16_t)sizeof(tBTA_HD_API_ENABLE)); + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HD_API_ENABLE)); + p_buf->hdr.event = BTA_HD_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdDisable + * + * Description Disables HID device. + * + * Returns void + * + ******************************************************************************/ +void BTA_HdDisable(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_deregister(BTA_ID_HD); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + *registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos) +{ + tBTA_HD_REGISTER_APP *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REGISTER_APP *)osi_malloc(sizeof(tBTA_HD_REGISTER_APP))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT; + if (p_app_info->p_name) { + strncpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN); + p_buf->name[BTA_HD_APP_NAME_LEN] = '\0'; + } else { + p_buf->name[0] = '\0'; + } + if (p_app_info->p_description) { + strncpy(p_buf->description, p_app_info->p_description, BTA_HD_APP_DESCRIPTION_LEN); + p_buf->description[BTA_HD_APP_DESCRIPTION_LEN] = '\0'; + } else { + p_buf->description[0] = '\0'; + } + if (p_app_info->p_provider) { + strncpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN); + p_buf->provider[BTA_HD_APP_PROVIDER_LEN] = '\0'; + } else { + p_buf->provider[0] = '\0'; + } + p_buf->subclass = p_app_info->subclass; + p_buf->d_len = p_app_info->descriptor.dl_len; + memcpy(p_buf->d_data, p_app_info->descriptor.dsc_list, p_app_info->descriptor.dl_len); + // copy qos data as-is + memcpy(&p_buf->in_qos, p_in_qos, sizeof(tBTA_HD_QOS_INFO)); + memcpy(&p_buf->out_qos, p_out_qos, sizeof(tBTA_HD_QOS_INFO)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + *unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_UNREGISTER_APP_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report) +{ + tBTA_HD_SEND_REPORT *p_buf; + APPL_TRACE_VERBOSE("%s", __func__); + if (p_report->len > BTA_HD_REPORT_LEN) { + APPL_TRACE_WARNING("%s, report len (%d) > MTU len (%d), can't send report." + " Increase value of HID_DEV_MTU_SIZE to send larger reports", + __func__, p_report->len, BTA_HD_REPORT_LEN); + return; + } + if ((p_buf = (tBTA_HD_SEND_REPORT *)osi_malloc(sizeof(tBTA_HD_SEND_REPORT))) != NULL) { + p_buf->hdr.event = BTA_HD_API_SEND_REPORT_EVT; + p_buf->use_intr = p_report->use_intr; + p_buf->type = p_report->type; + p_buf->id = p_report->id; + p_buf->len = p_report->len; + memcpy(p_buf->data, p_report->p_data, p_report->len); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_VC_UNPLUG_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_CONNECT_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISCONNECT_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_ADD_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REMOVE_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error) +{ + tBTA_HD_REPORT_ERR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REPORT_ERR *)osi_malloc(sizeof(tBTA_HD_REPORT_ERR))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REPORT_ERROR_EVT; + p_buf->error = error; + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_main.c b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c new file mode 100644 index 0000000000..4400b3e172 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c @@ -0,0 +1,320 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID host main functions and state machine. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include + +/***************************************************************************** + * Constants and types + ****************************************************************************/ +/* state machine states */ +enum { + BTA_HD_INIT_ST, + BTA_HD_IDLE_ST, /* not connected, waiting for connection */ + BTA_HD_CONN_ST, /* host connected */ + BTA_HD_TRANSIENT_TO_INIT_ST, /* transient state: going back from CONN to INIT */ +}; +typedef uint8_t tBTA_HD_STATE; + +/* state machine actions */ +enum { + BTA_HD_REGISTER_ACT, + BTA_HD_UNREGISTER_ACT, + BTA_HD_UNREGISTER2_ACT, + BTA_HD_CONNECT_ACT, + BTA_HD_DISCONNECT_ACT, + BTA_HD_ADD_DEVICE_ACT, + BTA_HD_REMOVE_DEVICE_ACT, + BTA_HD_SEND_REPORT_ACT, + BTA_HD_REPORT_ERROR_ACT, + BTA_HD_VC_UNPLUG_ACT, + BTA_HD_OPEN_ACT, + BTA_HD_CLOSE_ACT, + BTA_HD_INTR_DATA_ACT, + BTA_HD_GET_REPORT_ACT, + BTA_HD_SET_REPORT_ACT, + BTA_HD_SET_PROTOCOL_ACT, + BTA_HD_VC_UNPLUG_DONE_ACT, + BTA_HD_SUSPEND_ACT, + BTA_HD_EXIT_SUSPEND_ACT, + BTA_HD_NUM_ACTIONS +}; + +#define BTA_HD_IGNORE BTA_HD_NUM_ACTIONS + +typedef void (*tBTA_HD_ACTION)(tBTA_HD_DATA *p_data); +/* action functions */ +const tBTA_HD_ACTION bta_hd_action[] = { + bta_hd_register_act, bta_hd_unregister_act, bta_hd_unregister2_act, bta_hd_connect_act, + bta_hd_disconnect_act, bta_hd_add_device_act, bta_hd_remove_device_act, bta_hd_send_report_act, + bta_hd_report_error_act, bta_hd_vc_unplug_act, bta_hd_open_act, bta_hd_close_act, + bta_hd_intr_data_act, bta_hd_get_report_act, bta_hd_set_report_act, bta_hd_set_protocol_act, + bta_hd_vc_unplug_done_act, bta_hd_suspend_act, bta_hd_exit_suspend_act, +}; + +/* state table information */ +#define BTA_HD_ACTION 0 /* position of action */ +#define BTA_HD_NEXT_STATE 1 /* position of next state */ +#define BTA_HD_NUM_COLS 2 /* number of columns */ + +const uint8_t bta_hd_st_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_REGISTER_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, +}; + +const uint8_t bta_hd_st_idle[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_UNREGISTER_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_CONNECT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_OPEN_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, +}; + +const uint8_t bta_hd_st_conn[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_REPORT_ERROR_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_CLOSE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_INTR_DATA_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_GET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_SET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_SET_PROTOCOL_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_DONE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_SUSPEND_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_EXIT_SUSPEND_ACT, BTA_HD_CONN_ST}, +}; + +const uint8_t bta_hd_st_transient_to_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, +}; + +/* type for state table */ +typedef const uint8_t (*tBTA_HD_ST_TBL)[BTA_HD_NUM_COLS]; +/* state table */ +const tBTA_HD_ST_TBL bta_hd_st_tbl[] = {bta_hd_st_init, bta_hd_st_idle, bta_hd_st_conn, bta_hd_st_transient_to_init}; + +/***************************************************************************** + * Global data + ****************************************************************************/ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HD_CB bta_hd_cb; +#else +tBTA_HD_CB *bta_hd_cb_ptr; +#endif + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code); +static const char *bta_hd_state_code(tBTA_HD_STATE state_code); + +/******************************************************************************* + * + * Function bta_hd_sm_execute + * + * Description State machine event handling function for HID Device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_sm_execute(uint16_t event, tBTA_HD_DATA *p_data) +{ + tBTA_HD_ST_TBL state_table; + tBTA_HD_STATE prev_state; + uint8_t action; + tBTA_HD cback_data; + + APPL_TRACE_EVENT("%s: state=%s (%d) event=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state, + bta_hd_evt_code(event), event); + + prev_state = bta_hd_cb.state; + memset(&cback_data, 0, sizeof(tBTA_HD)); + state_table = bta_hd_st_tbl[bta_hd_cb.state]; + event &= 0xff; + + if ((action = state_table[event][BTA_HD_ACTION]) < BTA_HD_IGNORE) { + (*bta_hd_action[action])(p_data); + } + + bta_hd_cb.state = state_table[event][BTA_HD_NEXT_STATE]; + + if (bta_hd_cb.state != prev_state) { + APPL_TRACE_EVENT("%s: [new] state=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state); + } + return; +} + +/******************************************************************************* + * + * Function bta_hd_hdl_event + * + * Description HID device main event handling function. + * + * Returns void + * + ******************************************************************************/ +bool bta_hd_hdl_event(BT_HDR *p_msg) +{ + APPL_TRACE_API("%s: p_msg->event=%d", __func__, p_msg->event); + + switch (p_msg->event) { + case BTA_HD_API_ENABLE_EVT: + bta_hd_api_enable((tBTA_HD_DATA *)p_msg); + break; + case BTA_HD_API_DISABLE_EVT: + if (bta_hd_cb.state == BTA_HD_CONN_ST) { + APPL_TRACE_WARNING("%s: host connected, disconnect before disabling", __func__); + // unregister (and disconnect) + bta_hd_cb.disable_w4_close = TRUE; + bta_hd_sm_execute(BTA_HD_API_UNREGISTER_APP_EVT, (tBTA_HD_DATA *)p_msg); + } else { + bta_hd_api_disable(); + } + break; + default: + bta_hd_sm_execute(p_msg->event, (tBTA_HD_DATA *)p_msg); + } + return (TRUE); +} + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code) +{ + switch (evt_code) { + case BTA_HD_API_REGISTER_APP_EVT: + return "BTA_HD_API_REGISTER_APP_EVT"; + case BTA_HD_API_UNREGISTER_APP_EVT: + return "BTA_HD_API_UNREGISTER_APP_EVT"; + case BTA_HD_API_CONNECT_EVT: + return "BTA_HD_API_CONNECT_EVT"; + case BTA_HD_API_DISCONNECT_EVT: + return "BTA_HD_API_DISCONNECT_EVT"; + case BTA_HD_API_ADD_DEVICE_EVT: + return "BTA_HD_API_ADD_DEVICE_EVT"; + case BTA_HD_API_REMOVE_DEVICE_EVT: + return "BTA_HD_API_REMOVE_DEVICE_EVT"; + case BTA_HD_API_SEND_REPORT_EVT: + return "BTA_HD_API_SEND_REPORT_EVT"; + case BTA_HD_API_REPORT_ERROR_EVT: + return "BTA_HD_API_REPORT_ERROR_EVT"; + case BTA_HD_API_VC_UNPLUG_EVT: + return "BTA_HD_API_VC_UNPLUG_EVT"; + case BTA_HD_INT_OPEN_EVT: + return "BTA_HD_INT_OPEN_EVT"; + case BTA_HD_INT_CLOSE_EVT: + return "BTA_HD_INT_CLOSE_EVT"; + case BTA_HD_INT_INTR_DATA_EVT: + return "BTA_HD_INT_INTR_DATA_EVT"; + case BTA_HD_INT_GET_REPORT_EVT: + return "BTA_HD_INT_GET_REPORT_EVT"; + case BTA_HD_INT_SET_REPORT_EVT: + return "BTA_HD_INT_SET_REPORT_EVT"; + case BTA_HD_INT_SET_PROTOCOL_EVT: + return "BTA_HD_INT_SET_PROTOCOL_EVT"; + case BTA_HD_INT_VC_UNPLUG_EVT: + return "BTA_HD_INT_VC_UNPLUG_EVT"; + case BTA_HD_INT_SUSPEND_EVT: + return "BTA_HD_INT_SUSPEND_EVT"; + case BTA_HD_INT_EXIT_SUSPEND_EVT: + return "BTA_HD_INT_EXIT_SUSPEND_EVT"; + default: + return ""; + } +} + +static const char *bta_hd_state_code(tBTA_HD_STATE state_code) +{ + switch (state_code) { + case BTA_HD_INIT_ST: + return "BTA_HD_INIT_ST"; + case BTA_HD_IDLE_ST: + return "BTA_HD_IDLE_ST"; + case BTA_HD_CONN_ST: + return "BTA_HD_CONN_ST"; + case BTA_HD_TRANSIENT_TO_INIT_ST: + return "BTA_HD_TRANSIENT_TO_INIT_ST"; + default: + return ""; + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h b/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h new file mode 100644 index 0000000000..7a515970e6 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-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 BTA HID Device internal definitions + * + ******************************************************************************/ +#ifndef BTA_HD_INT_H +#define BTA_HD_INT_H + +#include "bta/bta_hd_api.h" +#include "bta/bta_sys.h" +#include "stack/hiddefs.h" + +enum { + BTA_HD_API_REGISTER_APP_EVT = BTA_SYS_EVT_START(BTA_ID_HD), + BTA_HD_API_UNREGISTER_APP_EVT, + BTA_HD_API_CONNECT_EVT, + BTA_HD_API_DISCONNECT_EVT, + BTA_HD_API_ADD_DEVICE_EVT, + BTA_HD_API_REMOVE_DEVICE_EVT, + BTA_HD_API_SEND_REPORT_EVT, + BTA_HD_API_REPORT_ERROR_EVT, + BTA_HD_API_VC_UNPLUG_EVT, + BTA_HD_INT_OPEN_EVT, + BTA_HD_INT_CLOSE_EVT, + BTA_HD_INT_INTR_DATA_EVT, + BTA_HD_INT_GET_REPORT_EVT, + BTA_HD_INT_SET_REPORT_EVT, + BTA_HD_INT_SET_PROTOCOL_EVT, + BTA_HD_INT_VC_UNPLUG_EVT, + BTA_HD_INT_SUSPEND_EVT, + BTA_HD_INT_EXIT_SUSPEND_EVT, + /* handled outside state machine */ + BTA_HD_API_ENABLE_EVT, + BTA_HD_API_DISABLE_EVT +}; +typedef uint16_t tBTA_HD_INT_EVT; +#define BTA_HD_INVALID_EVT (BTA_HD_API_DISABLE_EVT + 1) +typedef struct { + BT_HDR hdr; + tBTA_HD_CBACK *p_cback; +} tBTA_HD_API_ENABLE; +#define BTA_HD_APP_NAME_LEN 50 +#define BTA_HD_APP_DESCRIPTION_LEN 50 +#define BTA_HD_APP_PROVIDER_LEN 50 +#define BTA_HD_APP_DESCRIPTOR_LEN 2048 +#define BTA_HD_STATE_DISABLED 0x00 +#define BTA_HD_STATE_ENABLED 0x01 +#define BTA_HD_STATE_IDLE 0x02 +#define BTA_HD_STATE_CONNECTED 0x03 +#define BTA_HD_STATE_DISABLING 0x04 +#define BTA_HD_STATE_REMOVING 0x05 +typedef struct { + BT_HDR hdr; + char name[BTA_HD_APP_NAME_LEN + 1]; + char description[BTA_HD_APP_DESCRIPTION_LEN + 1]; + char provider[BTA_HD_APP_PROVIDER_LEN + 1]; + uint8_t subclass; + uint16_t d_len; + uint8_t d_data[BTA_HD_APP_DESCRIPTOR_LEN]; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} tBTA_HD_REGISTER_APP; + +#define BTA_HD_REPORT_LEN HID_DEV_MTU_SIZE + +typedef struct { + BT_HDR hdr; + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t data[BTA_HD_REPORT_LEN]; +} tBTA_HD_SEND_REPORT; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; +} tBTA_HD_DEVICE_CTRL; + +typedef struct { + BT_HDR hdr; + uint8_t error; +} tBTA_HD_REPORT_ERR; + +/* union of all event data types */ +typedef union { + BT_HDR hdr; + tBTA_HD_API_ENABLE api_enable; + tBTA_HD_REGISTER_APP register_app; + tBTA_HD_SEND_REPORT send_report; + tBTA_HD_DEVICE_CTRL device_ctrl; + tBTA_HD_REPORT_ERR report_err; +} tBTA_HD_DATA; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + uint32_t data; + BT_HDR *p_data; +} tBTA_HD_CBACK_DATA; + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ +typedef struct { + tBTA_HD_CBACK *p_cback; + uint32_t sdp_handle; + uint8_t trace_level; + uint8_t state; + BD_ADDR bd_addr; + bool use_report_id; + bool boot_mode; + bool vc_unplug; + bool disable_w4_close; +} tBTA_HD_CB; + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HD_CB bta_hd_cb; +#else +extern tBTA_HD_CB *bta_hd_cb_ptr; +#define bta_hd_cb (*bta_hd_cb_ptr) +#endif + +/***************************************************************************** + * Function prototypes + ****************************************************************************/ +extern bool bta_hd_hdl_event(BT_HDR *p_msg); +extern void bta_hd_api_enable(tBTA_HD_DATA *p_data); +extern void bta_hd_api_disable(void); +extern void bta_hd_register_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister2_act(tBTA_HD_DATA *p_data); +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_disconnect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_act(tBTA_HD_DATA *p_data); +extern void bta_hd_open_act(tBTA_HD_DATA *p_data); +extern void bta_hd_close_act(tBTA_HD_DATA *p_data); +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data); +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data); +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data); +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data); + +#endif diff --git a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c index dcaf665dbc..4158f015f6 100644 --- a/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c +++ b/components/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.c @@ -214,7 +214,7 @@ const UINT8 bta_hf_client_st_closing[][BTA_HF_CLIENT_NUM_COLS] = { /* DISC_OK_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, /* DISC_FAIL_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, /* SCO_OPEN_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, - /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, + /* SCO_CLOSE_EVT */ {BTA_HF_CLIENT_SCO_CONN_CLOSE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, /* SEND_AT_CMD_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, #if (BTM_SCO_HCI_INCLUDED == TRUE ) /* CI_SCO_DATA_EVT */ {BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_CLOSING_ST}, diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_act.c b/components/bt/host/bluedroid/bta/hh/bta_hh_act.c index ce34307707..3edce53bc6 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_act.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_act.c @@ -323,6 +323,7 @@ void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) p_cb->sec_mask = p_data->api_conn.sec_mask; p_cb->mode = p_data->api_conn.mode; + p_cb->new_mode = p_data->api_conn.mode; bta_hh_cb.p_cur = p_cb; #if (BTA_HH_LE_INCLUDED == TRUE) @@ -451,6 +452,8 @@ void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) HID_HostRemoveDev( p_cb->incoming_hid_handle); } conn_dat.status = status; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); /* move state machine W4_CONN ->IDLE */ @@ -521,6 +524,8 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) memset((void *)&conn, 0, sizeof (tBTA_HH_CONN)); conn.handle = dev_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; bdcpy(conn.bda, p_cb->addr); /* increase connection number */ @@ -587,6 +592,7 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) APPL_TRACE_EVENT ("bta_hh_open_act: Device[%d] connected", dev_handle); #endif + p_cb->incoming_conn = TRUE; /* SDP has been done */ if (p_cb->app_id != 0) { bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, p_data); @@ -594,7 +600,6 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* app_id == 0 indicates an incoming conenction request arrives without SDP performed, do it first */ { - p_cb->incoming_conn = TRUE; /* store the handle here in case sdp fails - need to disconnect */ p_cb->incoming_hid_handle = dev_handle; @@ -676,6 +681,11 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) case BTA_HH_SET_IDLE_EVT : cback_data.handle = p_cb->hid_handle; cback_data.status = bta_hh_get_trans_status(p_data->hid_cback.data); + if (cback_data.status == BTA_HH_OK) { + p_cb->mode = p_cb->new_mode; + } else { + p_cb->new_mode = p_cb->mode; + } (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&cback_data); p_cb->w4_evt = 0; break; @@ -684,6 +694,8 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) case BTA_HH_OPEN_EVT: conn.status = p_data->hid_cback.data ? BTA_HH_ERR_PROTO : BTA_HH_OK; conn.handle = p_cb->hid_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; bdcpy(conn.bda, p_cb->addr); (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&conn); #if BTA_HH_DEBUG @@ -787,6 +799,8 @@ void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) conn_dat.handle = p_cb->hid_handle; conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; bdcpy(conn_dat.bda, p_cb->addr); HID_HostCloseDev(p_cb->hid_handle); @@ -836,6 +850,8 @@ void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* Failure in opening connection */ conn_dat.handle = p_cb->hid_handle; conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; bdcpy(conn_dat.bda, p_cb->addr); HID_HostCloseDev(p_cb->hid_handle); @@ -1019,7 +1035,9 @@ void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) *******************************************************************************/ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { + tHID_STATUS status; tBTA_HH_CBDATA cbdata = {BTA_HH_OK, 0}; + tBTA_HH_API_SENDDATA send_data = {BTA_HH_OK, 0, 0}; UINT16 event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; @@ -1031,25 +1049,33 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { cbdata.handle = p_cb->hid_handle; + send_data.handle = p_cb->hid_handle; /* match up BTE/BTA report/boot mode def */ if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) { + p_cb->new_mode = p_data->api_sndcmd.param; p_data->api_sndcmd.param = ( p_data->api_sndcmd.param == BTA_HH_PROTO_RPT_MODE) ? \ HID_PAR_PROTOCOL_REPORT : HID_PAR_PROTOCOL_BOOT_MODE; } - if (HID_HostWriteDev (p_cb->hid_handle, - p_data->api_sndcmd.t_type, - p_data->api_sndcmd.param, - p_data->api_sndcmd.data, - p_data->api_sndcmd.rpt_id, - p_data->api_sndcmd.p_data) != HID_SUCCESS) { - APPL_TRACE_ERROR("HID_HostWriteDev Error "); + status = HID_HostWriteDev(p_cb->hid_handle, p_data->api_sndcmd.t_type, p_data->api_sndcmd.param, + p_data->api_sndcmd.data, p_data->api_sndcmd.rpt_id, p_data->api_sndcmd.p_data); + if (status != HID_SUCCESS) { + APPL_TRACE_ERROR("HID_HostWriteDev status:%d", status); cbdata.status = BTA_HH_ERR; + send_data.status = BTA_HH_ERR; - if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL && - p_data->api_sndcmd.t_type != HID_TRANS_DATA) { - (* bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { + switch (p_data->api_sndcmd.t_type) { + case HID_TRANS_DATA: + event = BTA_HH_DATA_EVT; + send_data.reason = status; + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&send_data); + break; + default: + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + break; + } } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { (* bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, (tBTA_HH *)&cbdata); } @@ -1070,6 +1096,7 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) p_cb->w4_evt = event; break; case HID_TRANS_DATA: /* output report */ + (*bta_hh_cb.p_cback)(BTA_HH_DATA_EVT, (tBTA_HH *)&send_data); /* fall through */ case HID_TRANS_CONTROL: /* no handshake event will be generated */ @@ -1098,7 +1125,6 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); } } - } return; } diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c b/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c index 65e06c7345..383f23262c 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c @@ -24,9 +24,9 @@ ******************************************************************************/ #include "common/bt_target.h" -#include "bta/bta_hh_api.h" #if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" /* max number of device types supported by BTA */ diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c index 967cd61b08..a80314efa6 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c @@ -32,7 +32,7 @@ #include "bta/utl.h" #define LOG_TAG "bt_bta_hh" -#include "osi/include/log.h" +// #include "osi/include/log.h" #ifndef BTA_HH_LE_RECONN #define BTA_HH_LE_RECONN TRUE diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_main.c b/components/bt/host/bluedroid/bta/hh/bta_hh_main.c index a5ca868723..663d28e96f 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_main.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_main.c @@ -292,6 +292,8 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA *p_data) bdcpy(cback_data.conn.bda, ((tBTA_HH_API_CONN *)p_data)->bd_addr); cback_data.conn.status = BTA_HH_ERR_DB_FULL; cback_data.conn.handle = BTA_HH_INVALID_HANDLE; + /* check if host initiate the connection*/ + cback_data.conn.is_orig = !p_cb->incoming_conn; break; /* DB full, BTA_HhAddDev */ case BTA_HH_API_MAINT_DEV_EVT: @@ -340,7 +342,7 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA *p_data) default: /* invalid handle, call bad API event */ - APPL_TRACE_ERROR("wrong device handle: [%d]", p_data->hdr.layer_specific); + APPL_TRACE_ERROR("wrong device handle: [%d], event:%d", p_data->hdr.layer_specific, event - BTA_HH_API_OPEN_EVT); /* Free the callback buffer now */ if (p_data != NULL && p_data->hid_cback.p_data != NULL) { osi_free(p_data->hid_cback.p_data); @@ -443,6 +445,10 @@ BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg) } } else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA *)p_msg)->addr); + uint8_t hdl = BTA_HH_IDX_INVALID; + if (HID_HostGetDev(((tBTA_HH_CBACK_DATA *)p_msg)->addr, &hdl) == HID_SUCCESS && hdl != BTA_HH_IDX_INVALID) { + bta_hh_cb.cb_index[hdl] = bta_hh_cb.kdev[index].index; + } } else { index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); } diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c index 121e11834b..8ca8b09c45 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c @@ -237,7 +237,7 @@ BOOLEAN bta_hh_tod_spt(tBTA_HH_DEV_CB *p_cb, UINT8 sub_class) } } #if BTA_HH_DEBUG - APPL_TRACE_EVENT("bta_hh_tod_spt sub_class:0x%x NOT supported", sub_class); + APPL_TRACE_ERROR("bta_hh_tod_spt sub_class:0x%x NOT supported", sub_class); #endif return FALSE; } @@ -460,9 +460,11 @@ void bta_hh_cleanup_disable(tBTA_HH_STATUS status) } utl_freebuf((void **)&bta_hh_cb.p_disc_db); - (* bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH *)&status); - /* all connections are down, no waiting for diconnect */ - memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + if (bta_hh_cb.p_cback) { + (*bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH*)&status); + /* all connections are down, no waiting for diconnect */ + memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + } } /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h b/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h index 58a8b59a3d..0efb2fcab4 100644 --- a/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h +++ b/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h @@ -243,6 +243,7 @@ typedef struct { UINT8 incoming_hid_handle; /* temporary handle for incoming connection? */ BOOLEAN opened; /* TRUE if device successfully opened HID connection */ tBTA_HH_PROTO_MODE mode; /* protocol mode */ + tBTA_HH_PROTO_MODE new_mode; /* protocol mode */ tBTA_HH_STATE state; /* CB state */ #if (BTA_HH_LE_INCLUDED == TRUE) @@ -364,6 +365,7 @@ extern void bta_hh_disc_cmpl(void); extern tBTA_HH_STATUS bta_hh_read_ssr_param(BD_ADDR bd_addr, UINT16 *p_max_ssr_lat, UINT16 *p_min_ssr_tout); /* functions for LE HID */ +#if (BTA_HH_LE_INCLUDED == TRUE) extern void bta_hh_le_enable(void); extern BOOLEAN bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if); extern void bta_hh_le_deregister(void); @@ -391,6 +393,7 @@ extern void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); extern void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); extern void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); extern void bta_hh_ci_load_rpt (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +#endif #if BTA_HH_DEBUG extern void bta_hh_trace_dev_db(void); diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_api.h index 32e59d27e2..735c210f2f 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_api.h @@ -78,7 +78,7 @@ typedef UINT8 tBTA_STATUS; #define BTA_SAP_SERVICE_ID 17 /* SIM Access profile */ #define BTA_A2DP_SINK_SERVICE_ID 18 /* A2DP Sink */ #define BTA_AVRCP_SERVICE_ID 19 /* A/V remote control */ -#define BTA_HID_SERVICE_ID 20 /* HID */ +#define BTA_HID_SERVICE_ID 20 /* HID Host*/ #define BTA_VDP_SERVICE_ID 21 /* Video distribution */ #define BTA_PBAP_SERVICE_ID 22 /* PhoneBook Access Server*/ #define BTA_HSP_HS_SERVICE_ID 23 /* HFP HS role */ @@ -1331,8 +1331,8 @@ typedef UINT8 tBTA_DM_PM_ACTION; #endif #ifndef BTA_DM_PM_SNIFF2_MAX -#define BTA_DM_PM_SNIFF2_MAX 180 -#define BTA_DM_PM_SNIFF2_MIN 150 +#define BTA_DM_PM_SNIFF2_MAX 54 //180 +#define BTA_DM_PM_SNIFF2_MIN 30 //150 #define BTA_DM_PM_SNIFF2_ATTEMPT 4 #define BTA_DM_PM_SNIFF2_TIMEOUT 1 #endif @@ -1345,8 +1345,8 @@ typedef UINT8 tBTA_DM_PM_ACTION; #endif #ifndef BTA_DM_PM_SNIFF4_MAX -#define BTA_DM_PM_SNIFF4_MAX 54 -#define BTA_DM_PM_SNIFF4_MIN 30 +#define BTA_DM_PM_SNIFF4_MAX 18 //54 +#define BTA_DM_PM_SNIFF4_MIN 10 //30 #define BTA_DM_PM_SNIFF4_ATTEMPT 4 #define BTA_DM_PM_SNIFF4_TIMEOUT 1 #endif diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h new file mode 100644 index 0000000000..1c7e708e2f --- /dev/null +++ b/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTA_HD_API_H +#define BTA_HD_API_H + +#include "bta_api.h" +#include "stack/hidd_api.h" + +#if BTA_HD_INCLUDED == TRUE + +/***************************************************************************** + * Constants and Type Definitions + ****************************************************************************/ + +#ifndef BTA_HD_DEBUG +#define BTA_HD_DEBUG TRUE +#endif + +/* BTA HID Device callback events */ +#define BTA_HD_ENABLE_EVT 0 /* BT-HD enabled */ +#define BTA_HD_DISABLE_EVT 1 /* BT-HD disabled */ +#define BTA_HD_REGISTER_APP_EVT 2 /* application registered */ +#define BTA_HD_UNREGISTER_APP_EVT 3 /* application unregistered */ +#define BTA_HD_OPEN_EVT 4 /* connection to host opened */ +#define BTA_HD_CLOSE_EVT 5 /* connection to host closed */ +#define BTA_HD_GET_REPORT_EVT 6 /* GET_REPORT request from host */ +#define BTA_HD_SET_REPORT_EVT 7 /* SET_REPORT request from host */ +#define BTA_HD_SET_PROTOCOL_EVT 8 /* SET_PROTOCOL request from host */ +#define BTA_HD_INTR_DATA_EVT 9 /* DATA received from host on intr */ +#define BTA_HD_VC_UNPLUG_EVT 10 /* Virtual Cable Unplug */ +// #define BTA_HD_CONN_STATE_EVT 11 /* Report connection state change */ +#define BTA_HD_SEND_REPORT_EVT 12 /* Send report finish */ +#define BTA_HD_REPORT_ERR_EVT 13 /* Report Handshake finish */ +#define BTA_HD_API_ERR_EVT 99 /* BT-HD API error */ + +typedef uint16_t tBTA_HD_EVT; + +enum { BTA_HD_OK, BTA_HD_ERROR }; + +typedef enum { + BTA_HD_CONN_STATE_CONNECTED, + BTA_HD_CONN_STATE_CONNECTING, + BTA_HD_CONN_STATE_DISCONNECTED, + BTA_HD_CONN_STATE_DISCONNECTING, + BTA_HD_CONN_STATE_UNKNOWN +} tBTA_HD_CONN_STAT; + +typedef uint8_t tBTA_HD_STATUS; +typedef tHID_DEV_DSCP_INFO tBTA_HD_DEV_DESCR; + +typedef struct { + char *p_name; + char *p_description; + char *p_provider; + uint8_t subclass; + tBTA_HD_DEV_DESCR descriptor; +} tBTA_HD_APP_INFO; + +typedef struct { + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t access_latency; + uint32_t delay_variation; +} tBTA_HD_QOS_INFO; + +typedef struct { + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + bool in_use; + BD_ADDR bda; +} tBTA_HD_REG_STATUS; + +typedef struct { + BD_ADDR bda; + tBTA_HD_STATUS status; + tBTA_HD_CONN_STAT conn_status; +} tBTA_HD_CONN; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t buffer_size; +} tBTA_HD_GET_REPORT; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_SET_REPORT; + +typedef uint8_t tBTA_HD_SET_PROTOCOL; + +typedef struct { + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_INTR_DATA; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; + uint8_t report_type; + uint8_t report_id; +} tBTA_HD_API_SEND_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; +} tBTA_HD_API_REPORT_ERR; + +/* union of data associated with HD callback */ +typedef union { + tBTA_HD_STATUS status; /* BTA_HD_ENABLE_EVT + BTA_HD_DISABLE_EVT + BTA_HD_UNREGISTER_APP_EVT */ + tBTA_HD_REG_STATUS reg_status; /* BTA_HD_REGISTER_APP_EVT */ + tBTA_HD_CONN conn; /* BTA_HD_OPEN_EVT + BTA_HD_CLOSE_EVT + BTA_HD_VC_UNPLUG_EVT + BTA_HD_OWN_VC_UNPLUG_EVT */ + tBTA_HD_GET_REPORT get_report; /* BTA_HD_GET_REPORT */ + tBTA_HD_SET_REPORT set_report; /* BTA_HD_SET_REPORT */ + tBTA_HD_SET_PROTOCOL set_protocol; /* BTA_HD_SETPROTOCOL */ + tBTA_HD_INTR_DATA intr_data; /* BTA_HD_INTR_DATA_EVT */ + tBTA_HD_API_SEND_REPORT send_report; /* BTA_HD_API_SEND_REPORT_EVT */ + tBTA_HD_API_REPORT_ERR report_err; /* BTA_HD_API_REPORT_ERR_EVT */ +} tBTA_HD; + +/* BTA HD callback function */ +typedef void (tBTA_HD_CBACK)(tBTA_HD_EVT event, tBTA_HD *p_data); + +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* + * + * Function BTA_HhRegister + * + * Description This function enable HID host and registers HID-Host with + * lower layers. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdEnable(tBTA_HD_CBACK *p_cback); + +/******************************************************************************* + * + * Function BTA_HhDeregister + * + * Description This function is called when the host is about power down. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisable(void); + +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + * registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos); + +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + * unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void); + +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report); + +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void); + +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error); + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_HD_INCLUDED */ +#endif /* BTA_HD_API_H */ diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h index e1bf4017fb..88cd192627 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h @@ -58,7 +58,8 @@ #define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */ #define BTA_HH_DATA_EVT 15 #define BTA_HH_API_ERR_EVT 16 /* API error is caught */ -#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ +#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ +#define BTA_HH_DATA_IND_EVT 18 /* Data on interrupt channel */ typedef UINT16 tBTA_HH_EVT; @@ -131,8 +132,8 @@ enum { BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */ BTA_HH_ERR_NO_RES, /* out of system resources */ BTA_HH_ERR_AUTH_FAILED, /* authentication fail */ - BTA_HH_ERR_HDL, - BTA_HH_ERR_SEC + BTA_HH_ERR_HDL, /* connection handle error */ + BTA_HH_ERR_SEC, /* encryption error */ }; typedef UINT8 tBTA_HH_STATUS; @@ -210,6 +211,7 @@ typedef struct { BD_ADDR bda; /* HID device bd address */ tBTA_HH_STATUS status; /* operation status */ UINT8 handle; /* device handle */ + BOOLEAN is_orig; /* indicate if host initiate connection */ #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) BOOLEAN le_hid; /* is LE devices? */ BOOLEAN scps_supported; /* scan parameter service supported */ @@ -257,9 +259,9 @@ typedef struct { typedef struct { tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */ union { - tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ - tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ - } data_rpt; + tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ + tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ + } data_rpt; } tBTA_HH_BOOT_RPT; /* handshake data */ @@ -267,13 +269,29 @@ typedef struct { tBTA_HH_STATUS status; /* handshake status */ UINT8 handle; /* device handle */ union { - tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ - BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ - UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ - } rsp_data; + tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ + BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ + UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ + } rsp_data; } tBTA_HH_HSDATA; + +/* upper layer send data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + UINT8 reason; /* send data failed reason */ +} tBTA_HH_API_SENDDATA; + +/* interrupt channel data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + tBTA_HH_PROTO_MODE proto_mode; /* protocol mode */ + BT_HDR *p_data; /* DATA_EVT : feature report data */ +} tBTA_HH_INTDATA; + /* union of data associated with HD callback */ typedef union { tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */ @@ -290,10 +308,12 @@ typedef union { BTA_HH_GET_RPT_EVT BTA_HH_GET_PROTO_EVT BTA_HH_GET_IDLE_EVT */ + tBTA_HH_API_SENDDATA send_data; /* BTA_HH_DATA_EVT */ + tBTA_HH_INTDATA int_data; /* BTA_HH_DATA_IND_EVT */ } tBTA_HH; /* BTA HH callback function */ -typedef void (tBTA_HH_CBACK) (tBTA_HH_EVT event, tBTA_HH *p_data); +typedef void (tBTA_HH_CBACK)(tBTA_HH_EVT event, tBTA_HH *p_data); /***************************************************************************** diff --git a/components/bt/host/bluedroid/btc/core/btc_storage.c b/components/bt/host/bluedroid/btc/core/btc_storage.c index 97bef05fc7..bbaa7f8389 100644 --- a/components/bt/host/bluedroid/btc/core/btc_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_storage.c @@ -16,11 +16,13 @@ #include "btc/btc_storage.h" #include "btc/btc_util.h" #include "osi/osi.h" +#include "osi/allocator.h" #include "common/bt_trace.h" #include "esp_system.h" #include "bta/bta_api.h" #include "device/bdaddr.h" #include "btc/btc_config.h" +#include "btc_hh.h" /******************************************************************************* ** @@ -247,3 +249,252 @@ bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *d return BT_STATUS_SUCCESS; } + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list) +{ + BTC_TRACE_DEBUG("btc_storage_add_hid_device_info:"); + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidAttrMask", attr_mask); + ret &= btc_config_set_int(bdstr, "HidSubClass", sub_class); + ret &= btc_config_set_int(bdstr, "HidAppId", app_id); + ret &= btc_config_set_int(bdstr, "HidVendorId", vendor_id); + ret &= btc_config_set_int(bdstr, "HidProductId", product_id); + ret &= btc_config_set_int(bdstr, "HidVersion", version); + ret &= btc_config_set_int(bdstr, "HidCountryCode", ctry_code); + ret &= btc_config_set_int(bdstr, "HidSSRMaxLatency", ssr_max_latency); + ret &= btc_config_set_int(bdstr, "HidSSRMinTimeout", ssr_min_tout); + if (dl_len > 0) + btc_config_set_bin(bdstr, "HidDescriptor", dsc_list, dl_len); + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("Storage add hid device info %d\n", ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void) +{ + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR) && btc_config_exist(name, "HidAttrMask")) { + btc_config_get_int(name, "HidAttrMask", &value); + uint16_t attr_mask = (uint16_t)value; + + tBTA_HH_DEV_DSCP_INFO dscp_info; + memset(&dscp_info, 0, sizeof(dscp_info)); + + btc_config_get_int(name, "HidSubClass", &value); + uint8_t sub_class = (uint8_t)value; + + btc_config_get_int(name, "HidAppId", &value); + uint8_t app_id = (uint8_t)value; + + btc_config_get_int(name, "HidVendorId", &value); + dscp_info.vendor_id = (uint16_t)value; + + btc_config_get_int(name, "HidProductId", &value); + dscp_info.product_id = (uint16_t)value; + + btc_config_get_int(name, "HidVersion", &value); + dscp_info.version = (uint8_t)value; + + btc_config_get_int(name, "HidCountryCode", &value); + dscp_info.ctry_code = (uint8_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMaxLatency", &value); + dscp_info.ssr_max_latency = (uint16_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMinTimeout", &value); + dscp_info.ssr_min_tout = (uint16_t)value; + + size_t len = btc_config_get_bin_length(name, "HidDescriptor"); + if (len > 0) { + dscp_info.descriptor.dl_len = (uint16_t)len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(len); + btc_config_get_bin(name, "HidDescriptor", (uint8_t *)dscp_info.descriptor.dsc_list, &len); + } + + // add extracted information to BTA HH + bt_bdaddr_t bd_addr; + if (string_to_bdaddr(name, &bd_addr) && btc_hh_add_added_dev(*(BD_ADDR *)&bd_addr, attr_mask)) { + BTA_HhAddDev(*(BD_ADDR *)&bd_addr, attr_mask, sub_class, app_id, dscp_info); + } + + if (dscp_info.descriptor.dsc_list) { + osi_free(dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dsc_list = NULL; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = 1; + btc_config_lock(); + if (btc_config_exist(bdstr, "HidAttrMask")) { + ret &= btc_config_remove(bdstr, "HidAttrMask"); + } + if (btc_config_exist(bdstr, "HidSubClass")) { + ret &= btc_config_remove(bdstr, "HidSubClass"); + } + if (btc_config_exist(bdstr, "HidAppId")) { + ret &= btc_config_remove(bdstr, "HidAppId"); + } + if (btc_config_exist(bdstr, "HidVendorId")) { + ret &= btc_config_remove(bdstr, "HidVendorId"); + } + if (btc_config_exist(bdstr, "HidProductId")) { + ret &= btc_config_remove(bdstr, "HidProductId"); + } + if (btc_config_exist(bdstr, "HidVersion")) { + ret &= btc_config_remove(bdstr, "HidVersion"); + } + if (btc_config_exist(bdstr, "HidCountryCode")) { + ret &= btc_config_remove(bdstr, "HidCountryCode"); + } + if (btc_config_exist(bdstr, "HidSSRMaxLatency")) { + ret &= btc_config_remove(bdstr, "HidSSRMaxLatency"); + } + if (btc_config_exist(bdstr, "HidSSRMinTimeout")) { + ret &= btc_config_remove(bdstr, "HidSSRMinTimeout"); + } + if (btc_config_exist(bdstr, "HidDescriptor")) { + ret &= btc_config_remove(bdstr, "HidDescriptor"); + } + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#include "bta/bta_hd_api.h" +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void) +{ + bt_bdaddr_t bd_addr; + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + BTC_TRACE_DEBUG("Remote device:%s", name); + if (btc_config_get_int(name, "HidDeviceCabled", &value)) { + string_to_bdaddr(name, &bd_addr); + BTA_HdAddDevice(bd_addr.address); + break; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr = {0}; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidDeviceCabled", 1); + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + int ret = 0; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + if (btc_config_exist(bdstr, "HidVersion")) { + ret = btc_config_remove(bdstr, "HidDeviceCabled"); + } + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) diff --git a/components/bt/host/bluedroid/btc/core/btc_util.c b/components/bt/host/bluedroid/btc/core/btc_util.c index 09f178565c..0417198a57 100644 --- a/components/bt/host/bluedroid/btc/core/btc_util.c +++ b/components/bt/host/bluedroid/btc/core/btc_util.c @@ -35,6 +35,10 @@ #include "bta/bta_ag_api.h" #endif ///BTA_AG_INCLUDED == TRUE +#if (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" +#endif ///BTA_HH_INCLUDED == TRUE +#include "bta/bta_hd_api.h" #include "common/bt_defs.h" #include "stack/btm_api.h" #include "bta/bta_api.h" @@ -209,6 +213,56 @@ const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state #endif // #if (BTA_AG_INCLUDED == TRUE) +#if (BTA_HH_INCLUDED == TRUE) +const char *dump_hh_event(uint16_t event) +{ + switch (event) { + CASE_RETURN_STR(BTA_HH_ENABLE_EVT) + CASE_RETURN_STR(BTA_HH_DISABLE_EVT) + CASE_RETURN_STR(BTA_HH_OPEN_EVT) + CASE_RETURN_STR(BTA_HH_CLOSE_EVT) + CASE_RETURN_STR(BTA_HH_GET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_SET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_GET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_SET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_GET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_SET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_GET_DSCP_EVT) + CASE_RETURN_STR(BTA_HH_ADD_DEV_EVT) + CASE_RETURN_STR(BTA_HH_RMV_DEV_EVT) + CASE_RETURN_STR(BTA_HH_VC_UNPLUG_EVT) + CASE_RETURN_STR(BTA_HH_DATA_EVT) + CASE_RETURN_STR(BTA_HH_API_ERR_EVT) + CASE_RETURN_STR(BTA_HH_UPDATE_SCPP_EVT) + CASE_RETURN_STR(BTA_HH_DATA_IND_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HH_INCLUDED + +#if BTA_HD_INCLUDED == TRUE +const char* dump_hd_event(uint16_t event) { + switch (event) { + CASE_RETURN_STR(BTA_HD_ENABLE_EVT) + CASE_RETURN_STR(BTA_HD_DISABLE_EVT) + CASE_RETURN_STR(BTA_HD_REGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_UNREGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_OPEN_EVT) + CASE_RETURN_STR(BTA_HD_CLOSE_EVT) + CASE_RETURN_STR(BTA_HD_GET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_PROTOCOL_EVT) + CASE_RETURN_STR(BTA_HD_INTR_DATA_EVT) + CASE_RETURN_STR(BTA_HD_VC_UNPLUG_EVT) + //CASE_RETURN_STR(BTA_HD_CONN_STATE_EVT) + CASE_RETURN_STR(BTA_HD_API_ERR_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HD_INCLUDED + UINT32 devclass2uint(DEV_CLASS dev_class) { UINT32 cod = 0; diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_storage.h b/components/bt/host/bluedroid/btc/include/btc/btc_storage.h index f74c3eeb36..57916e522a 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_storage.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_storage.h @@ -27,6 +27,10 @@ #define BTC_STORAGE_PIN_LENGTH_STR "PinLength" #define BTC_STORAGE_SC_SUPPORT "SCSupport" +#ifdef __cplusplus +extern "C" { +#endif + /******************************************************************************* ** ** Function btc_storage_add_bonded_device @@ -91,4 +95,85 @@ int btc_storage_get_num_bt_bond_devices(void); *******************************************************************************/ bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *dev_num); +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list); + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void); + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr); +#endif // (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void); + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr); + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr); +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#ifdef __cplusplus +} +#endif + #endif /* BTC_STORAGE_H */ diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_util.h b/components/bt/host/bluedroid/btc/include/btc/btc_util.h index aa96e46016..1cc3c9bd06 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_util.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_util.h @@ -31,7 +31,9 @@ ********************************************************************************/ typedef char bdstr_t[18]; - +#ifdef __cplusplus +extern "C" { +#endif /******************************************************************************* ** Functions ********************************************************************************/ @@ -48,6 +50,14 @@ const char *dump_hf_call_state(esp_hf_call_status_t call_state); const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state); #endif +#if(BTA_HD_INCLUDED == TRUE) +const char* dump_hd_event(uint16_t event); +#endif + +#if(BTA_HH_INCLUDED == TRUE) +const char* dump_hh_event(uint16_t event); +#endif + UINT32 devclass2uint(DEV_CLASS dev_class); void uint2devclass(UINT32 dev, DEV_CLASS dev_class); void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128); @@ -58,4 +68,8 @@ esp_bt_status_t btc_hci_to_esp_status(uint8_t hci_status); esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status); esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status); +#ifdef __cplusplus +} +#endif + #endif /* __BTC_UTIL_H__ */ diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c b/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c new file mode 100644 index 0000000000..5743f89042 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c @@ -0,0 +1,152 @@ +#include "btc_hh.h" +#include "osi/allocator.h" +#include "string.h" +#if HID_HOST_INCLUDED == TRUE + +/******************************************************************************* + * + * Function bta_hh_co_open + * + * Description When connection is opened, this call-out function is executed + * by HH to do platform specific initialization. + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK attr_mask, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + // We found a device with the same handle. Must be a device reconnected. + APPL_TRACE_WARNING("%s: Found an existing device with the same handle dev_status=%d, " + "dev_handle=0x%2x, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d", + __func__, p_dev->dev_status, dev_handle, p_dev->attr_mask, p_dev->sub_class, + p_dev->app_id); + break; + } + p_dev = NULL; + } + + if (p_dev == NULL) { + // Did not find a device reconnection case. Find an empty slot now. + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_UNKNOWN) { + p_dev = &btc_hh_cb.devices[i]; + p_dev->dev_handle = dev_handle; + p_dev->attr_mask = attr_mask; + p_dev->sub_class = sub_class; + p_dev->app_id = app_id; + p_dev->local_vup = false; + + btc_hh_cb.device_num++; + break; + } + } + } + + if (p_dev == NULL) { + APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__); + return; + } + + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status); +} + +/******************************************************************************* + * + * Function bta_hh_co_close + * + * Description When connection is closed, this call-out function is executed + * by HH to do platform specific finalization. + * + * Parameters dev_handle - device handle + * app_id - application id + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + APPL_TRACE_WARNING("%s: dev_handle = %d, app_id = %d", __func__, dev_handle, app_id); + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + APPL_TRACE_WARNING("%s: Found an existing device with the same handle " + "dev_status = %d, dev_handle =%d", + __func__, p_dev->dev_status, p_dev->dev_handle); + break; + } + } +} + +/******************************************************************************* + * + * Function bta_hh_co_data + * + * Description This function is executed by BTA when HID host receive a + * data report on interrupt channel. + * + * Parameters dev_handle - device handle + * *p_rpt - pointer to the report data + * len - length of report data + * mode - Hid host Protocol Mode + * sub_clas - Device Subclass + * app_id - application id + * + * Returns void + ******************************************************************************/ +void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class, UINT8 ctry_code, + BD_ADDR peer_addr, UINT8 app_id) +{ + btc_msg_t msg; + tBTA_HH p_data; + BT_HDR *p_buf = NULL; + bt_status_t status; + tBTA_HH_STATUS ret = BTA_HH_OK; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_DATA_IND_EVT; + + APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, " + "ctry_code = %d, app_id = %d", + __func__, dev_handle, sub_class, mode, ctry_code, app_id); + + do { + if ((p_buf = osi_malloc(sizeof(BT_HDR) + len)) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + ret = BTA_HH_ERR_NO_RES; + break; + } + p_buf->offset = 0; + p_buf->len = len; + p_buf->event = 0; + p_buf->layer_specific = dev_handle; + memcpy(p_buf->data, p_rpt, len); + } while (0); + + p_data.int_data.status = ret; + p_data.int_data.handle = dev_handle; + p_data.int_data.p_data = p_buf; + p_data.int_data.proto_mode = mode; + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL); + assert(status == BT_STATUS_SUCCESS); +} + +#endif /* HID_HOST_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c new file mode 100644 index 0000000000..0deccf1251 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c @@ -0,0 +1,831 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include + +#include "bta/bta_api.h" +#include "bta/bta_hd_api.h" +#include "bta/bta_hh_api.h" +#include "bta/utl.h" +#include "btc/btc_storage.h" +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" + +#include "osi/allocator.h" + +#include "esp_hidd_api.h" + +#if HID_DEV_INCLUDED == TRUE +#include "bta_dm_int.h" + +/* HD request events */ +typedef enum { BTC_HD_DUMMY_REQ_EVT = 0 } btc_hd_req_evt_t; + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hd_cb_t btc_hd_cb; + +// static tBTA_HD_APP_INFO app_info; +// static tBTA_HD_QOS_INFO in_qos; +// static tBTA_HD_QOS_INFO out_qos; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define BTC_HD_APP_NAME_LEN 50 +#define BTC_HD_APP_DESCRIPTION_LEN 50 +#define BTC_HD_APP_PROVIDER_LEN 50 +#define BTC_HD_APP_DESCRIPTOR_LEN 2048 +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 + +#define is_hidd_init() (btc_hd_cb.status > BTC_HD_DISABLED) +#define is_hidd_app_register() (btc_hd_cb.app_registered) + +typedef void (bt_hid_copy_cb_t)(btc_msg_t *msg, void *p_dest, void *p_src); + +static inline void btc_hd_cb_to_app(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) +{ + esp_hd_cb_t *btc_hd_cb = (esp_hd_cb_t *)btc_profile_cb_get(BTC_PID_HD); + if (btc_hd_cb) { + btc_hd_cb(event, param); + } +} + +static void free_app_info_param(void) +{ + utl_freebuf((void **)&btc_hd_cb.app_info.descriptor.dsc_list); + utl_freebuf((void **)&btc_hd_cb.app_info.p_provider); + utl_freebuf((void **)&btc_hd_cb.app_info.p_description); + utl_freebuf((void **)&btc_hd_cb.app_info.p_name); +} + +static void bte_hd_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HD *p_dst_data = (tBTA_HD *)p_dst; + tBTA_HD *p_src_data = (tBTA_HD *)p_src; + switch (msg->act) + { + case BTA_HD_SET_REPORT_EVT: { + uint8_t *src_data = p_src_data->set_report.p_data; + if (src_data) { + p_dst_data->set_report.p_data = osi_malloc(p_src_data->set_report.len); + if (p_dst_data->set_report.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc set_report data failed!", __func__); + break; + } + memcpy(p_dst_data->set_report.p_data, src_data, p_src_data->set_report.len); + } + break; + } + case BTA_HD_INTR_DATA_EVT: { + uint8_t *src_data = p_src_data->intr_data.p_data; + if (src_data) { + p_dst_data->intr_data.p_data = osi_malloc(p_src_data->intr_data.len); + if (p_dst_data->intr_data.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc intr_data data failed!", __func__); + break; + } + memcpy(p_dst_data->intr_data.p_data, src_data, p_src_data->intr_data.len); + } + break; + } + default: + break; + } +} + +/******************************************************************************* + * + * Function btc_hd_remove_device + * + * Description Removes plugged device + * + * Returns void + * + ******************************************************************************/ +void btc_hd_remove_device(bt_bdaddr_t bd_addr) +{ + BTA_HdRemoveDevice((uint8_t *)&bd_addr); + // btc_storage_remove_hidd(&bd_addr); +} + +/******************************************************************************* + * + * Function bte_hd_evt + * + * Description Switches context from BTE to BTC for all BT-HD events + * + * Returns void + * + ******************************************************************************/ +static void bte_hd_evt(tBTA_HD_EVT event, tBTA_HD *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HD_ENABLE_EVT: + case BTA_HD_DISABLE_EVT: + case BTA_HD_UNREGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_STATUS); + break; + case BTA_HD_REGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_REG_STATUS); + break; + case BTA_HD_OPEN_EVT: + case BTA_HD_CLOSE_EVT: + case BTA_HD_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HD_CONN); + break; + case BTA_HD_GET_REPORT_EVT: + param_len += sizeof(tBTA_HD_GET_REPORT); + break; + case BTA_HD_SET_REPORT_EVT: + param_len = sizeof(tBTA_HD_SET_REPORT); + break; + case BTA_HD_SET_PROTOCOL_EVT: + param_len += sizeof(p_data->set_protocol); + break; + case BTA_HD_INTR_DATA_EVT: + param_len = sizeof(tBTA_HD_INTR_DATA); + break; + case BTA_HD_SEND_REPORT_EVT: + param_len = sizeof(tBTA_HD_API_SEND_REPORT); + break; + case BTA_HD_REPORT_ERR_EVT: + param_len = sizeof(tBTA_HD_API_REPORT_ERR); + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HD; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hd_arg_deep_copy); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("context transfer failed"); + } +} + +/******************************************************************************* + * + * Function btc_hd_init + * + * Description Initializes BT-HD interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has been initiated, shall uninit first!", __func__); + ret = ESP_HIDD_NEED_DEINIT; + break; + } + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + /* enable HD */ + BTA_HdEnable(bte_hd_evt); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.init.status = ret; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_deinit + * + * Description de-initializes the hd interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(void); +static void btc_hd_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + btc_hd_cb.service_dereg_active = FALSE; + btc_hd_cb.status = BTC_HD_DISABLING; + // unresgister app will also relase the connection + // and disable after receiving unregister event from lower layer + if (is_hidd_app_register()) { + btc_hd_unregister_app(); + } else { + BTC_TRACE_WARNING("%s disabling hid device service now", __func__); + BTA_HdDisable(); + } + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.deinit.status = ret; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_register_app + * + * Description Registers HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_register_app(esp_hidd_app_param_t *p_app_param, esp_hidd_qos_param_t *p_in_qos, + esp_hidd_qos_param_t *p_out_qos) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application already registered, shall deregister first!", __func__); + ret = ESP_HIDD_NEED_DEREG; + break; + } + + if ((btc_hd_cb.app_info.p_name = (char *)osi_malloc(BTC_HD_APP_NAME_LEN)) == NULL || + (btc_hd_cb.app_info.p_description = (char *)osi_malloc(BTC_HD_APP_DESCRIPTION_LEN)) == NULL || + (btc_hd_cb.app_info.p_provider = (char *)osi_malloc(BTC_HD_APP_PROVIDER_LEN)) == NULL || + (btc_hd_cb.app_info.descriptor.dsc_list = (uint8_t *)osi_malloc(p_app_param->desc_list_len)) == NULL) { + BTC_TRACE_ERROR( + "%s malloc app_info failed! p_name:%p, p_description:%p, p_provider:%p, descriptor.dsc_list:%p", + __func__, btc_hd_cb.app_info.p_name, btc_hd_cb.app_info.p_description, btc_hd_cb.app_info.p_provider, + btc_hd_cb.app_info.descriptor.dsc_list); + ret = ESP_HIDD_NO_RES; + break; + } + memcpy(btc_hd_cb.app_info.p_name, p_app_param->name, BTC_HD_APP_NAME_LEN); + memcpy(btc_hd_cb.app_info.p_description, p_app_param->description, BTC_HD_APP_DESCRIPTION_LEN); + memcpy(btc_hd_cb.app_info.p_provider, p_app_param->provider, BTC_HD_APP_PROVIDER_LEN); + memcpy(btc_hd_cb.app_info.descriptor.dsc_list, p_app_param->desc_list, p_app_param->desc_list_len); + btc_hd_cb.app_info.subclass = p_app_param->subclass; + btc_hd_cb.app_info.descriptor.dl_len = p_app_param->desc_list_len; + + btc_hd_cb.in_qos.service_type = p_in_qos->service_type; + btc_hd_cb.in_qos.token_rate = p_in_qos->token_rate; + btc_hd_cb.in_qos.token_bucket_size = p_in_qos->token_bucket_size; + btc_hd_cb.in_qos.peak_bandwidth = p_in_qos->peak_bandwidth; + btc_hd_cb.in_qos.access_latency = p_in_qos->access_latency; + btc_hd_cb.in_qos.delay_variation = p_in_qos->delay_variation; + btc_hd_cb.out_qos.service_type = p_out_qos->service_type; + btc_hd_cb.out_qos.token_rate = p_out_qos->token_rate; + btc_hd_cb.out_qos.token_bucket_size = p_out_qos->token_bucket_size; + btc_hd_cb.out_qos.peak_bandwidth = p_out_qos->peak_bandwidth; + btc_hd_cb.out_qos.access_latency = p_out_qos->access_latency; + btc_hd_cb.out_qos.delay_variation = p_out_qos->delay_variation; + + BTA_HdRegisterApp(&btc_hd_cb.app_info, &btc_hd_cb.in_qos, &btc_hd_cb.out_qos); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.register_app.status = ret; + param.register_app.in_use = false; + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + } + free_app_info_param(); +} + +/******************************************************************************* + * + * Function btc_hd_unregister_app + * + * Description Unregisters HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + if (btc_hd_cb.service_dereg_active) { + BTC_TRACE_ERROR("%s: BT-HD deregistering in progress", __func__); + ret = ESP_HIDD_BUSY; + break; + } + btc_hd_cb.service_dereg_active = TRUE; + BTA_HdUnregisterApp(); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.unregister_app.status = ret; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_connect + * + * Description Connects to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_connect(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdConnect(bd_addr); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.open.status = ret; + param.open.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + memcpy(param.open.bd_addr, bd_addr, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_disconnect + * + * Description Disconnects from host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_disconnect(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdDisconnect(); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.close.status = ret; + param.close.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_send_report + * + * Description Sends Reports to hid host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + tBTA_HD_REPORT report; + + BTC_TRACE_API("%s: type=%d id=%d len=%d", __func__, type, id, len); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + if (type == ESP_HIDD_REPORT_TYPE_INTRDATA) { + report.type = ESP_HIDD_REPORT_TYPE_INPUT; + report.use_intr = TRUE; + } else { + report.type = (type & 0x03); + report.use_intr = FALSE; + } + + report.id = id; + report.len = len; + report.p_data = p_data; + + BTA_HdSendReport(&report); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.send_report.status = ret; + param.send_report.reason = 0; + param.send_report.report_type = report.type; + param.send_report.report_id = report.id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_report_error + * + * Description Sends HANDSHAKE with error info for invalid SET_REPORT + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_report_error(uint8_t error) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdReportError(error); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.report_err.status = ret; + param.report_err.reason = 0; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_virtual_cable_unplug + * + * Description Sends Virtual Cable Unplug to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_virtual_cable_unplug(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdVirtualCableUnplug(); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.report_err.status = ret; + param.report_err.reason = 0; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + } +} + +static void btc_hd_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + utl_freebuf((void **)&arg->send_report.data); + break; + default: + break; + } +} + +void btc_hd_call_handler(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HD_INIT_EVT: + btc_hd_init(); + break; + case BTC_HD_DEINIT_EVT: + btc_hd_deinit(); + break; + case BTC_HD_REGISTER_APP_EVT: + btc_hd_register_app(arg->register_app.app_param, arg->register_app.in_qos, arg->register_app.out_qos); + break; + case BTC_HD_UNREGISTER_APP_EVT: + btc_hd_unregister_app(); + break; + case BTC_HD_CONNECT_EVT: + btc_hd_connect(arg->connect.bd_addr); + break; + case BTC_HD_DISCONNECT_EVT: + btc_hd_disconnect(); + break; + case BTC_HD_SEND_REPORT_EVT: + btc_hd_send_report(arg->send_report.type, arg->send_report.id, arg->send_report.len, arg->send_report.data); + break; + case BTC_HD_REPORT_ERROR_EVT: + btc_hd_report_error(arg->error); + break; + case BTC_HD_UNPLUG_EVT: + btc_hd_virtual_cable_unplug(); + break; + default: + BTC_TRACE_WARNING("unknown hidd action %i", msg->act); + break; + } + btc_hd_call_arg_deep_free(msg); +} + +static void btc_hd_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HD *arg = (tBTA_HD *)msg->arg; + + switch (msg->act) { + case BTA_HD_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.p_data); + break; + case BTA_HD_INTR_DATA_EVT: + utl_freebuf((void **)&arg->intr_data.p_data); + break; + default: + break; + } +} + +void btc_hd_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_HD *p_data = (tBTA_HD *)msg->arg; + esp_hidd_cb_param_t param = {0}; + BTC_TRACE_API("%s: event=%s", __func__, dump_hd_event(event)); + + switch (event) { + case BTA_HD_ENABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK) { + btc_storage_load_hidd(); + btc_hd_cb.status = BTC_HD_ENABLED; + } else { + btc_hd_cb.status = BTC_HD_DISABLED; + BTC_TRACE_WARNING("Failed to enable BT-HD, status=%d", p_data->status); + } + param.init.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + break; + case BTA_HD_DISABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK){ + btc_hd_cb.status = BTC_HD_DISABLED; + if (btc_hd_cb.service_dereg_active) { + btc_hd_cb.service_dereg_active = FALSE; + } + free_app_info_param(); + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + } else { + BTC_TRACE_WARNING("Failed to disable BT-HD, status=%d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + break; + case BTA_HD_REGISTER_APP_EVT: + if (p_data->reg_status.status == BTA_HD_OK) { + btc_hd_cb.app_registered = TRUE; + } + param.register_app.status = p_data->reg_status.status; + param.register_app.in_use = p_data->reg_status.in_use; + if (!p_data->reg_status.in_use) { + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + } else { + memcpy(param.register_app.bd_addr, p_data->reg_status.bda, BD_ADDR_LEN); + } + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + break; + case BTA_HD_UNREGISTER_APP_EVT: + btc_hd_cb.app_registered = FALSE; + param.unregister_app.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_WARNING("disabling hid device service now"); + BTA_HdDisable(); + } + break; + case BTA_HD_OPEN_EVT: { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_EVENT("BTA_HD_OPEN_EVT, address (%02x:%02x:%02x:%02x:%02x:%02x)", addr->address[0], addr->address[1], + addr->address[2], addr->address[3], addr->address[4], addr->address[5]); + if (p_data->conn.status == BTA_HD_OK && p_data->conn.conn_status == BTA_HD_CONN_STATE_CONNECTED) { + // /* Check if the connection is from hid host and not hid device */ + // if (check_cod_hid(addr)) { + // /* Incoming connection from hid device, reject it */ + // BTC_TRACE_WARNING("remote device is not hid host, disconnecting"); + // btc_hd_cb.forced_disc = TRUE; + // BTA_HdDisconnect(); + // break; + // } + // btc_storage_set_hidd((bt_bdaddr_t *)&p_data->conn.bda); + } + param.open.status = p_data->conn.status; + param.open.conn_status = p_data->conn.conn_status; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + break; + } + case BTA_HD_CLOSE_EVT: + if (btc_hd_cb.forced_disc && p_data->conn.conn_status == BTA_HD_CONN_STATE_DISCONNECTED) { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_WARNING("remote device was forcefully disconnected"); + btc_hd_remove_device(*addr); + btc_hd_cb.forced_disc = FALSE; + break; + } + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + break; + case BTA_HD_GET_REPORT_EVT: + param.get_report.report_type = p_data->get_report.report_type; + param.get_report.report_id = p_data->get_report.report_id; + param.get_report.buffer_size = p_data->get_report.buffer_size; + btc_hd_cb_to_app(ESP_HIDD_GET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_REPORT_EVT: + param.set_report.report_type = p_data->set_report.report_type; + param.set_report.report_id = p_data->set_report.report_id; + param.set_report.len = p_data->set_report.len; + param.set_report.data = p_data->set_report.p_data; + btc_hd_cb_to_app(ESP_HIDD_SET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_PROTOCOL_EVT: + switch (p_data->set_protocol) { + case HID_PAR_PROTOCOL_BOOT_MODE: + param.set_protocol.protocol_mode = ESP_HIDD_BOOT_MODE; + break; + case HID_PAR_PROTOCOL_REPORT: + param.set_protocol.protocol_mode = ESP_HIDD_REPORT_MODE; + break; + default: + param.set_protocol.protocol_mode = ESP_HIDD_UNSUPPORTED_MODE; + break; + } + btc_hd_cb_to_app(ESP_HIDD_SET_PROTOCOL_EVT, ¶m); + break; + case BTA_HD_INTR_DATA_EVT: + param.intr_data.report_id = p_data->intr_data.report_id; + param.intr_data.len = p_data->intr_data.len; + param.intr_data.data = p_data->intr_data.p_data; + btc_hd_cb_to_app(ESP_HIDD_INTR_DATA_EVT, ¶m); + break; + case BTA_HD_VC_UNPLUG_EVT: { + bt_bdaddr_t *bd_addr = (bt_bdaddr_t *)&p_data->conn.bda; + if (bta_dm_check_if_only_hd_connected(p_data->conn.bda)) { + BTC_TRACE_DEBUG("%s: Removing bonding as only HID profile connected", __func__); + BTA_DmRemoveDevice((uint8_t *)&p_data->conn.bda, BT_TRANSPORT_BR_EDR); + } else { + BTC_TRACE_DEBUG("%s: Only removing HID data as some other profiles connected", __func__); + btc_hd_remove_device(*bd_addr); + } + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + param.vc_unplug.status = p_data->conn.status; + param.vc_unplug.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + break; + } + case BTA_HD_SEND_REPORT_EVT: + param.send_report.status = p_data->send_report.status; + param.send_report.reason = p_data->send_report.reason; + param.send_report.report_type = p_data->send_report.report_type; + param.send_report.report_id = p_data->send_report.report_id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + break; + case BTA_HD_REPORT_ERR_EVT: + param.report_err.status = p_data->report_err.status; + param.report_err.reason = p_data->report_err.reason; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + break; + default: + BTC_TRACE_WARNING("%s: unknown event (%d)", __func__, event); + break; + } + btc_hd_cb_arg_deep_free(msg); +} + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidd_args_t *dst = (btc_hidd_args_t *)p_dest; + btc_hidd_args_t *src = (btc_hidd_args_t *)p_src; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + dst->send_report.data = (uint8_t *)osi_malloc(src->send_report.len); + if (dst->send_report.data) { + memcpy(dst->send_report.data, src->send_report.data, src->send_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_DEV_INCLUDED==TRUE diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c new file mode 100644 index 0000000000..d0744c3236 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c @@ -0,0 +1,1569 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "device/bdaddr.h" +#include "btc/btc_storage.h" +#include "osi/allocator.h" +#include "bta/utl.h" +#include "bta/bta_hh_api.h" +#include "stack/l2c_api.h" +// #include "bta_dm_int.h" + +#if HID_HOST_INCLUDED == TRUE +#include "btc_hh.h" + + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hh_cb_t btc_hh_cb; +static bdstr_t bdstr; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define COD_MASK 0x07FF + +#define COD_UNCLASSIFIED ((0x1F) << 8) +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 +#define COD_HID_MASK 0x0700 + +#define is_hidh_init() (btc_hh_cb.status > BTC_HH_DISABLED) +#define BTC_TIMEOUT_VUP_MS (3 * 1000) + +static inline void btc_hh_cb_to_app(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param) +{ + esp_hh_cb_t *btc_hh_cb = (esp_hh_cb_t *)btc_profile_cb_get(BTC_PID_HH); + if (btc_hh_cb) { + btc_hh_cb(event, param); + } +} + +/******************************************************************************* + * + * Function proto_mode_change_to_lower_layer + * + * Description Change the upper layer protocol mode definition to the lower layer protocol mode definition + * + * Returns Lower layer protocol mode definition + ******************************************************************************/ +static inline tBTA_HH_PROTO_MODE proto_mode_change_to_lower_layer(esp_hidh_protocol_mode_t protocol_mode) +{ + tBTA_HH_PROTO_MODE proto_mode = BTA_HH_PROTO_UNKNOWN; + + switch (protocol_mode) { + case ESP_HIDH_REPORT_MODE: + proto_mode = BTA_HH_PROTO_RPT_MODE; + break; + case ESP_HIDH_BOOT_MODE: + proto_mode = BTA_HH_PROTO_BOOT_MODE; + break; + default: + break; + } + + return proto_mode; +} + +/******************************************************************************* + * + * Function proto_mode_change_to_upper_layer + * + * Description Change the lower layer protocol mode definition to the upper layer protocol mode definition + * + * Returns Upper layer protocol mode definition + ******************************************************************************/ +static inline esp_hidh_protocol_mode_t proto_mode_change_to_upper_layer(tBTA_HH_PROTO_MODE proto_mode) +{ + esp_hidh_protocol_mode_t protocol_mode = ESP_HIDH_UNSUPPORTED_MODE; + + switch (proto_mode) { + case BTA_HH_PROTO_RPT_MODE: + protocol_mode = ESP_HIDH_REPORT_MODE; + break; + case BTA_HH_PROTO_BOOT_MODE: + protocol_mode = ESP_HIDH_BOOT_MODE; + break; + default: + break; + } + + return protocol_mode; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_handle + * + * Description Return the connected device pointer of the specified device + * handle + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +btc_hh_device_t *btc_hh_find_connected_dev_by_handle(uint8_t handle) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && btc_hh_cb.devices[i].dev_handle == handle) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_dev_by_bda + * + * Description Return the device pointer of the specified bd_addr. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_dev_by_bda(BD_ADDR bd_addr) +{ + uint8_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_bda + * + * Description Return the connected device pointer of the specified + * RawAddress. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_connected_dev_by_bda(BD_ADDR bd_addr) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_stop_vup_timer + * + * Description stop vitual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_stop_vup_timer(BD_ADDR bd_addr) +{ + BTIF_TRACE_API("%s", __func__); + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + + if (p_dev != NULL) { + BTC_TRACE_DEBUG("stop VUP timer"); + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + } +} + +/******************************************************************************* + * + * Function btc_hh_timer_timeout + * + * Description Process timer timeout + * + * Returns void + ******************************************************************************/ +void btc_hh_timer_timeout(void *data) +{ + btc_hh_device_t *p_dev = (btc_hh_device_t *)data; + bt_status_t status; + tBTA_HH p_data; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_VC_UNPLUG_EVT; + + BTC_TRACE_API("%s", __func__); + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_CONNECTED){ + BTC_TRACE_ERROR("%s Device[%s] is not connected!", __func__, + bdaddr_to_string((const bt_bdaddr_t *)p_dev->bd_addr, bdstr, sizeof(bdstr))); + return; + } + + memset(&p_data, 0, sizeof(tBTA_HH)); + p_data.dev_status.status = BTA_HH_ERR; + p_data.dev_status.handle = p_dev->dev_handle; + + /* switch context to btif task context */ + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s context transfer failed", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_start_vup_timer + * + * Description start virtual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_start_vup_timer(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + return; + } + + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + if ((p_dev->vup_timer = osi_alarm_new("btc_hh.vup_timer", btc_hh_timer_timeout, p_dev, BTC_TIMEOUT_VUP_MS)) == + NULL) { + BTC_TRACE_ERROR("%s unable to malloc vup_timer!", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_add_added_dev + * + * Description Add a new device to the added device list. + * + * Returns true if add successfully, otherwise false. + ******************************************************************************/ +bool btc_hh_add_added_dev(BD_ADDR bd_addr, tBTA_HH_ATTR_MASK attr_mask) +{ + int i; + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return false; + } + } + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr_null, BD_ADDR_LEN) == 0) { + memcpy(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + btc_hh_cb.added_devices[i].attr_mask = attr_mask; + return true; + } + } + + BTC_TRACE_ERROR("%s: Error, out of space to add device", __func__); + return false; +} + +/******************************************************************************* + ** + ** Function btc_hh_remove_device + ** + ** Description Remove an added device from the stack. + ** + ** Returns void + ******************************************************************************/ +void btc_hh_remove_device(BD_ADDR bd_addr) +{ + int i; + btc_hh_device_t *p_dev; + btc_hh_added_device_t *p_added_dev; + + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + p_added_dev = &btc_hh_cb.added_devices[i]; + if (p_added_dev->bd_addr == bd_addr) { + BTA_HhRemoveDev(p_added_dev->dev_handle); + btc_storage_remove_hid_info((bt_bdaddr_t *)p_added_dev->bd_addr); + memset(p_added_dev->bd_addr, 0, 6); + p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE; + break; + } + } + + p_dev = btc_hh_find_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Oops, can't find device", __func__); + return; + } + + /* need to notify up-layer device is disconnected to avoid state out of sync + * with up-layer */ //[boblane] + // HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr), BTHH_CONN_STATE_DISCONNECTED); + + p_dev->dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + p_dev->dev_handle = BTA_HH_INVALID_HANDLE; + p_dev->ready_for_data = false; + + if (btc_hh_cb.device_num > 0) { + btc_hh_cb.device_num--; + } else { + BTC_TRACE_WARNING("%s: device_num = 0", __func__); + } +} + +static void bte_hh_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HH *p_dst_data = (tBTA_HH *)p_dst; + tBTA_HH *p_src_data = (tBTA_HH *)p_src; + switch (msg->act) + { + case BTA_HH_GET_RPT_EVT: { + BT_HDR *src_hdr = p_src_data->hs_data.rsp_data.p_rpt_data; + if (src_hdr) { + p_dst_data->hs_data.rsp_data.p_rpt_data = osi_malloc(sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + if (p_dst_data->hs_data.rsp_data.p_rpt_data == NULL) { + BTC_TRACE_ERROR("%s malloc p_rpt_data failed!", __func__); + p_dst_data->hs_data.status = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(p_dst_data->hs_data.rsp_data.p_rpt_data, src_hdr, sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + } + break; + } + default: + break; + } +} + +static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HH_ENABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_DISABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_OPEN_EVT: + param_len = sizeof(tBTA_HH_CONN); + break; + case BTA_HH_CLOSE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_RPT_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_RPT_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_PROTO_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_PROTO_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_IDLE_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_IDLE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_DSCP_EVT: + param_len = sizeof(tBTA_HH_DEV_DSCP_INFO); + break; + case BTA_HH_ADD_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_RMV_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_DATA_EVT: + param_len = sizeof(tBTA_HH_API_SENDDATA); + break; + case BTA_HH_API_ERR_EVT: + param_len = 0; + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hh_arg_deep_copy); + assert(status == BT_STATUS_SUCCESS); +} + +/******************************************************************************* + * + * Function btc_hh_init + * + * Description initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has been initiated, shall uninit first!", __func__); + ret = ESP_HIDH_NEED_DEINIT; + break; + } + + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + BTA_HhEnable(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, bte_hh_evt); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.init.status = ret; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_deinit + * + * Description de-initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + // close all connections + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if(btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED){ + BTA_HhClose(btc_hh_cb.devices[i].dev_handle); + } + } + btc_hh_cb.service_dereg_active = TRUE; + btc_hh_cb.status = BTC_HH_DISABLING; + BTA_HhDisable(); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.deinit.status = ret; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_connect + * + * Description connection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_connect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_added_device_t* added_dev = NULL; + btc_hh_device_t* dev = NULL; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + if (btc_hh_cb.status == BTC_HH_DEV_CONNECTING) { + BTC_TRACE_ERROR("%s HH is connecting, ignore!", __func__); + ret = ESP_HIDH_BUSY; + break; + } + + dev = btc_hh_find_dev_by_bda(arg->connect.bd_addr); + if (!dev && btc_hh_cb.device_num >= BTC_HH_MAX_HID) { + BTC_TRACE_ERROR("%s exceeded the maximum supported HID device number %d!", __func__, BTC_HH_MAX_HID); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + + for (int i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, arg->connect.bd_addr, BD_ADDR_LEN) == 0) { + added_dev = &btc_hh_cb.added_devices[i]; + BTC_TRACE_WARNING("%s Device[%s] already added, attr_mask = 0x%x", __func__, + bdaddr_to_string((const bt_bdaddr_t *)arg->connect.bd_addr, bdstr, sizeof(bdstr)), + added_dev->attr_mask); + } + } + + if (added_dev != NULL) { + if (added_dev->dev_handle == BTA_HH_INVALID_HANDLE) { + // No space for more HID device now. + BTC_TRACE_ERROR("device added but addition failed"); + memset(added_dev->bd_addr, 0, sizeof(added_dev->bd_addr)); + ret = ESP_HIDH_ERR; + break; + } + } + + /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways + sending this request from host, for subsequent user initiated connection. If the remote is + not in pagescan mode, we will do 2 retries to connect before giving up */ + btc_hh_cb.status = BTC_HH_DEV_CONNECTING; + memcpy(btc_hh_cb.pending_conn_address, arg->connect.bd_addr, BD_ADDR_LEN); + BTA_HhOpen(arg->connect.bd_addr, BTA_HH_PROTO_RPT_MODE, (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)); + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTING; + ret = ESP_HIDH_OK; + } while (0); + + if (ret != ESP_HIDH_OK) { + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.status = ret; + param.open.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.open.bd_addr, arg->connect.bd_addr, BD_ADDR_LEN); + param.open.is_orig = true; + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_disconnect + * + * Description disconnection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_disconnect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->disconnect.bd_addr); + if (p_dev != NULL) { + BTA_HhClose(p_dev->dev_handle); + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.close.handle = p_dev->dev_handle; + } else { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + } + + } while (0); + + if (ret != ESP_HIDH_OK) { + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.handle = BTA_HH_INVALID_HANDLE; + } + param.close.status = ret; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_virtual_unplug + * + * Description Virtual unplug initiated from the BTC thread context + * Special handling for HID mouse- + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_virtual_unplug(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_dev_by_bda(arg->unplug.bd_addr); + if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED) && + (p_dev->attr_mask & HID_VIRTUAL_CABLE)) { + BTC_TRACE_DEBUG("%s: Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED)) { + BTC_TRACE_WARNING("%s: Virtual unplug not suported, disconnecting device", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhClose(p_dev->dev_handle); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else { + BTC_TRACE_ERROR("%s: Error, device not opened, status = %d", __func__, btc_hh_cb.status); + ret = ESP_HIDH_NO_CONNECTION; + if (memcmp(btc_hh_cb.pending_conn_address, arg->unplug.bd_addr, BD_ADDR_LEN) == 0 && + (btc_hh_cb.status == BTC_HH_DEV_CONNECTING)) { + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + } + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.unplug.handle = BTA_HH_INVALID_HANDLE; + } + param.unplug.status = ret; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_set_info + * + * Description Set the HID device descriptor for the specified HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_info(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + tBTA_HH_DEV_DSCP_INFO dscp_info; + + BTC_TRACE_DEBUG("%s: sub_class = 0x%02x, app_id = %d, vendor_id = 0x%04x, " + "product_id = 0x%04x, version= 0x%04x", + __func__, arg->set_info.hid_info->sub_class, arg->set_info.hid_info->app_id, + arg->set_info.hid_info->vendor_id, arg->set_info.hid_info->product_id, + arg->set_info.hid_info->version); + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + memset(&dscp_info, 0, sizeof(dscp_info)); + dscp_info.vendor_id = arg->set_info.hid_info->vendor_id; + dscp_info.product_id = arg->set_info.hid_info->product_id; + dscp_info.version = arg->set_info.hid_info->version; + dscp_info.ctry_code = arg->set_info.hid_info->ctry_code; + + dscp_info.descriptor.dl_len = arg->set_info.hid_info->dl_len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(dscp_info.descriptor.dl_len); + if (dscp_info.descriptor.dsc_list == NULL) { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(dscp_info.descriptor.dsc_list, arg->set_info.hid_info->dsc_list, dscp_info.descriptor.dl_len); + + if (btc_hh_add_added_dev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask)) { + btc_hh_cb.add_event = BTC_HH_SET_INFO_EVT; + BTA_HhAddDev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask, arg->set_info.hid_info->sub_class, + arg->set_info.hid_info->app_id, dscp_info); + } else { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR; + break; + } + } while(0); + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + + if (ret != ESP_HIDH_OK) { + param.set_info.status = ret; + param.set_info.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.set_info.bd_addr, arg->set_info.bd_addr, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_protocol + * + * Description Get the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetProtoMode(p_dev->dev_handle); + } while(0); + + if (ret != ESP_HIDH_OK) { + param.get_proto.proto_mode = ESP_HIDH_UNSUPPORTED_MODE; + param.get_proto.handle = BTA_HH_INVALID_HANDLE; + param.get_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_set_protocol + * + * Description Set the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->set_protocol.protocol_mode != ESP_HIDH_REPORT_MODE && arg->set_protocol.protocol_mode != ESP_HIDH_BOOT_MODE) { + BTC_TRACE_ERROR("%s: Error, device proto_mode = %d.", __func__, arg->set_protocol.protocol_mode); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhSetProtoMode(p_dev->dev_handle, proto_mode_change_to_lower_layer(arg->set_protocol.protocol_mode)); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_proto.handle = BTA_HH_INVALID_HANDLE; + param.set_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_report + * + * Description Send a GET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->get_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->get_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->get_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhGetReport(p_dev->dev_handle, arg->get_report.report_type, arg->get_report.report_id, + arg->get_report.buffer_size); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_rpt.handle = BTA_HH_INVALID_HANDLE; + param.get_rpt.status = ret; + param.get_rpt.len = 0; + param.get_rpt.data = NULL; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function create_pbuf + * + * Description Helper function to create p_buf for send_data or set_report + * + * Returns BT_HDR * + * + ******************************************************************************/ +static BT_HDR *create_pbuf(uint16_t len, uint8_t *data) +{ + uint8_t *pbuf_data; + BT_HDR *p_buf = (BT_HDR *)osi_malloc(len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR)); + if (p_buf == NULL) { + BTC_TRACE_ERROR("%s failed!", __func__); + return NULL; + } + p_buf->len = len; + p_buf->offset = BTA_HH_MIN_OFFSET; + + pbuf_data = (uint8_t *)(p_buf + 1) + p_buf->offset; + memcpy(pbuf_data, data, len); + + return p_buf; +} + +/******************************************************************************* + * + * Function btc_hh_set_report + * + * Description Send a SET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->set_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->set_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->set_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else if (arg->set_report.report == NULL || arg->set_report.len == 0) { + BTC_TRACE_ERROR("%s Error: report is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR* p_buf = create_pbuf(arg->set_report.len, arg->set_report.report); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + BTA_HhSetReport(p_dev->dev_handle, arg->set_report.report_type, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.set_rpt.handle = BTA_HH_INVALID_HANDLE; + param.set_rpt.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_send_data + * + * Description Send a SEND_DATA to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_send_data(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->send_data.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->send_data.data == NULL || arg->send_data.len == 0) { + BTC_TRACE_ERROR("%s Error: send data is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR *p_buf = create_pbuf(arg->send_data.len, arg->send_data.data); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; + BTA_HhSendData(p_dev->dev_handle, arg->send_data.bd_addr, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.send_data.handle = BTA_HH_INVALID_HANDLE; + param.send_data.status = ret; + param.send_data.reason = 0; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_get_idle_time +** +** Description Get the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_get_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetIdle(p_dev->dev_handle); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_idle.handle = BTA_HH_INVALID_HANDLE; + param.get_idle.status = ret; + param.get_idle.idle_rate = 0; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_set_idle_time +** +** Description Set the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_set_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhSetIdle(p_dev->dev_handle, arg->set_idle.idle_time); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_idle.handle = BTA_HH_INVALID_HANDLE; + param.set_idle.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_IDLE_EVT, ¶m); + } +} + +static void btc_hh_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + utl_freebuf((void **)&arg->set_info.hid_info); + break; + case BTC_HH_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.report); + break; + case BTC_HH_SEND_DATA_EVT: + utl_freebuf((void **)&arg->send_data.data); + break; + default: + break; + } +} + +void btc_hh_call_handler(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HH_INIT_EVT: + btc_hh_init(); + break; + case BTC_HH_CONNECT_EVT: + btc_hh_connect(arg); + break; + case BTC_HH_DISCONNECT_EVT: + btc_hh_disconnect(arg); + break; + case BTC_HH_UNPLUG_EVT: + btc_hh_virtual_unplug(arg); + break; + case BTC_HH_SET_INFO_EVT: + btc_hh_set_info(arg); + break; + case BTC_HH_GET_PROTO_EVT: + btc_hh_get_protocol(arg); + break; + case BTC_HH_SET_PROTO_EVT: + btc_hh_set_protocol(arg); + break; + case BTC_HH_GET_IDLE_EVT: + btc_hh_get_idle_time(arg); + break; + case BTC_HH_SET_IDLE_EVT: + btc_hh_set_idle_time(arg); + break; + case BTC_HH_GET_REPORT_EVT: + btc_hh_get_report(arg); + break; + case BTC_HH_SET_REPORT_EVT: + btc_hh_set_report(arg); + break; + case BTC_HH_SEND_DATA_EVT: + btc_hh_send_data(arg); + break; + case BTC_HH_DEINIT_EVT: + btc_hh_deinit(); + break; + default: + BTC_TRACE_WARNING("unknown hidh action %d", msg->act); + break; + } + btc_hh_call_arg_deep_free(msg); +} + +static void btc_hh_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HH *arg = (tBTA_HH *)msg->arg; + + switch (msg->act) { + case BTA_HH_GET_RPT_EVT: + utl_freebuf((void **)&arg->hs_data.rsp_data.p_rpt_data); + break; + case BTA_HH_DATA_IND_EVT: + utl_freebuf((void **)&arg->int_data.p_data); + break; + default: + break; + } +} + +bool btc_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO *dest, tBTA_HH_DEV_DSCP_INFO *src) +{ + dest->descriptor.dl_len = 0; + if (src->descriptor.dl_len > 0) { + dest->descriptor.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (dest->descriptor.dsc_list) { + memcpy(dest->descriptor.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + dest->descriptor.dl_len = src->descriptor.dl_len; + } + dest->vendor_id = src->vendor_id; + dest->product_id = src->product_id; + dest->version = src->version; + dest->ctry_code = src->ctry_code; + dest->ssr_max_latency = src->ssr_max_latency; + dest->ssr_min_tout = src->ssr_min_tout; + return true; +} + +bool btc_hh_cb_copy_hid_info(esp_hidh_cb_param_t *param, tBTA_HH_DEV_DSCP_INFO *src) +{ + param->dscp.dl_len = 0; + if (src->descriptor.dl_len > 0) { + param->dscp.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (param->dscp.dsc_list) { + memcpy(param->dscp.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + param->dscp.dl_len = src->descriptor.dl_len; + } + param->dscp.vendor_id = src->vendor_id; + param->dscp.product_id = src->product_id; + param->dscp.version = src->version; + param->dscp.ctry_code = src->ctry_code; + param->dscp.ssr_max_latency = src->ssr_max_latency; + param->dscp.ssr_min_tout = src->ssr_min_tout; + return true; +} + +void btc_hh_cb_handler(btc_msg_t *msg) +{ + esp_hidh_cb_param_t param = {0}; + tBTA_HH *p_data = (tBTA_HH *)msg->arg; + btc_hh_device_t *p_dev = NULL; + int len, i; + BTC_TRACE_DEBUG("%s: event=%s dereg = %d", __func__, dump_hh_event(msg->act), btc_hh_cb.service_dereg_active); + switch (msg->act) { + case BTA_HH_ENABLE_EVT: + if (p_data->status == BTA_HH_OK) { + btc_hh_cb.status = BTC_HH_ENABLED; + BTC_TRACE_DEBUG("Loading added devices"); + /* Add hid descriptors for already bonded hid devices*/ + // btc_storage_load_bonded_hid_info(); + } else { + btc_hh_cb.status = BTC_HH_DISABLED; + BTC_TRACE_ERROR("Error, HH enabling failed, status = %d", p_data->status); + } + param.init.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + break; + case BTA_HH_DISABLE_EVT: + btc_hh_cb.status = BTC_HH_DISABLED; + if (btc_hh_cb.service_dereg_active) { + BTIF_TRACE_DEBUG("BTA_HH_DISABLE_EVT: enabling HID Device service"); + // btif_hd_service_registration(); + btc_hh_cb.service_dereg_active = FALSE; + } + if (p_data->status == BTA_HH_OK) { + // Clear the control block + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].vup_timer) { + osi_alarm_free(btc_hh_cb.devices[i].vup_timer); + } + } + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + } else { + BTC_TRACE_ERROR("Error, HH disabling failed, status = %d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + break; + case BTA_HH_OPEN_EVT: + BTC_TRACE_DEBUG("handle=%d, status =%d", p_data->conn.handle, p_data->conn.status); + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + if (p_data->conn.status == BTA_HH_OK) { + p_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + if (p_dev == NULL) { + BTC_TRACE_ERROR("Error, cannot find device with handle %d", p_data->conn.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + // The connect request must come from device side and exceeded the + // connected HID device number. + BTA_HhClose(p_data->conn.handle); + + param.open.status = ESP_HIDH_ERR; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } else { + BTC_TRACE_DEBUG("Found device...Getting dscp info for handle " + "... %d", + p_data->conn.handle); + memcpy(p_dev->bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_CONNECTED; + // Send set_idle if the peer_device is a keyboard [boblane] + // if (check_cod(&p_data->conn.bda, COD_HID_KEYBOARD) || check_cod(&p_data->conn.bda, COD_HID_COMBO)) + // BTA_HhSetIdle(p_data->conn.handle, 0); + btc_hh_cb.p_curr_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + BTA_HhGetDscpInfo(p_data->conn.handle); + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + + param.open.status = ESP_HIDH_OK; + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTED; + } + } else { + p_dev = btc_hh_find_dev_by_bda(p_data->conn.bda); + if (p_dev != NULL) { + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + + param.open.status = p_data->conn.status; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.handle = p_data->conn.handle; + param.open.is_orig = p_data->conn.is_orig; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); + break; + case BTA_HH_GET_DSCP_EVT: + len = p_data->dscp_info.descriptor.dl_len; + BTC_TRACE_DEBUG("len = %d", len); + do { + param.dscp.status = ESP_HIDH_OK; + param.dscp.handle = BTA_HH_INVALID_HANDLE; + param.dscp.added = false; + p_dev = btc_hh_cb.p_curr_dev; + if (p_dev == NULL) { + BTC_TRACE_ERROR("No HID device is currently connected"); + param.dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + + if (btc_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask)) { + tBTA_HH_DEV_DSCP_INFO dscp_info; + bt_status_t ret; + btc_hh_copy_hid_info(&dscp_info, &p_data->dscp_info); + btc_hh_cb.add_event = BTC_HH_CONNECT_EVT; + BTA_HhAddDev(p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, dscp_info); + // write hid info to nvs + ret = btc_storage_add_hid_device_info((bt_bdaddr_t *)p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, + p_data->dscp_info.vendor_id, p_data->dscp_info.product_id, + p_data->dscp_info.version, p_data->dscp_info.ctry_code, + p_data->dscp_info.ssr_max_latency, p_data->dscp_info.ssr_min_tout, + len, p_data->dscp_info.descriptor.dsc_list); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("write hid info to nvs failed!"); + } + // Free buffer created for dscp_info; + if (dscp_info.descriptor.dl_len > 0 && dscp_info.descriptor.dsc_list != NULL) { + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dl_len = 0; + } + } else { + // Device already added. + BTC_TRACE_WARNING("%s: Device already added ", __func__); + param.dscp.added = true; + } + btc_hh_cb_copy_hid_info(¶m, &p_data->dscp_info); + param.dscp.handle = p_dev->dev_handle; + } while(0); + btc_hh_cb_to_app(ESP_HIDH_GET_DSCP_EVT, ¶m); + if (param.dscp.dl_len > 0 && param.dscp.dsc_list != NULL) { + utl_freebuf((void **)¶m.dscp.dsc_list); + param.dscp.dl_len = 0; + } + break; + case BTA_HH_CLOSE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + if (p_dev != NULL) { + BTC_TRACE_DEBUG("uhid local_vup=%d", p_dev->local_vup); + btc_hh_stop_vup_timer(p_dev->bd_addr); + /* If this is a locally initiated VUP, remove the bond as ACL got + * disconnected while VUP being processed. + */ + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } + + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.close.status = ESP_HIDH_NO_CONNECTION; + } + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + break; + case BTA_HH_VC_UNPLUG_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + if (p_dev != NULL) { + /* Stop the VUP timer */ + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + BTC_TRACE_DEBUG("%s---Sending connection state change", __func__); + param.close.status = ESP_HIDH_OK; + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + BTC_TRACE_DEBUG("%s---Removing HID bond", __func__); + /* If it is locally initiated VUP or remote device has its major COD as + Peripheral removed the bond.*/ + // [boblane] + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } else { + btc_hh_remove_device(p_dev->bd_addr); + } + param.unplug.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.unplug.status = ESP_HIDH_NO_CONNECTION; + } + param.unplug.handle = p_data->dev_status.handle; + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); + break; + case BTA_HH_DATA_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->send_data.status, + p_data->send_data.handle); + param.send_data.handle = p_data->send_data.handle; + param.send_data.status = p_data->send_data.status; + param.send_data.reason = p_data->send_data.reason; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + break; + case BTA_HH_GET_PROTO_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d, proto = [%d], %s", p_data->hs_data.status, + p_data->hs_data.handle, p_data->hs_data.rsp_data.proto_mode, + (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) + ? "Report Mode" + : (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_BOOT_MODE) ? "Boot Mode" + : "Unsupported"); + param.get_proto.proto_mode = proto_mode_change_to_upper_layer(p_data->hs_data.rsp_data.proto_mode); + param.get_proto.handle = p_data->hs_data.handle; + param.get_proto.status = p_data->hs_data.status; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + break; + case BTA_HH_SET_PROTO_EVT: + BTIF_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_proto.handle = p_data->dev_status.handle; + param.set_proto.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + break; + case BTA_HH_GET_RPT_EVT: { + BT_HDR *hdr = p_data->hs_data.rsp_data.p_rpt_data; + uint8_t *data = NULL; + uint16_t len = 0; + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->hs_data.status, p_data->hs_data.handle); + /* p_rpt_data is NULL in HANDSHAKE response case */ + if (hdr) { + data = (uint8_t *)(hdr + 1) + hdr->offset; + len = hdr->len; + } + param.get_rpt.handle = p_data->hs_data.handle; + param.get_rpt.status = p_data->hs_data.status; + param.get_rpt.len = len; + param.get_rpt.data = data; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + break; + } + case BTA_HH_SET_RPT_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_rpt.handle = p_data->dev_status.handle; + param.set_rpt.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + break; + case BTA_HH_GET_IDLE_EVT: + BTC_TRACE_DEBUG("handle = %d, status = %d, rate = %d", p_data->hs_data.handle, p_data->hs_data.status, + p_data->hs_data.rsp_data.idle_rate); + param.get_idle.handle = p_data->hs_data.handle; + param.get_idle.status = p_data->hs_data.status; + param.get_idle.idle_rate = p_data->hs_data.rsp_data.idle_rate; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + break; + case BTA_HH_SET_IDLE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, p_data->dev_status.handle); + param.set_idle.handle = p_data->dev_status.handle; + param.set_idle.status = p_data->dev_status.status; + btc_hh_cb_to_app(BTA_HH_SET_IDLE_EVT, ¶m); + break; + case BTA_HH_ADD_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + for (uint8_t i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, p_data->dev_info.bda, BD_ADDR_LEN) == 0) { + if (p_data->dev_info.status == BTA_HH_OK) { + btc_hh_cb.added_devices[i].dev_handle = p_data->dev_info.handle; + } else { + memset(btc_hh_cb.added_devices[i].bd_addr, 0, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + } + break; + } + } + if (btc_hh_cb.add_event == BTC_HH_SET_INFO_EVT) { + param.set_info.handle = p_data->dev_info.handle; + param.set_info.status = p_data->dev_info.status; + memcpy(param.set_info.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } else { + param.add_dev.handle = p_data->dev_info.handle; + param.add_dev.status = p_data->dev_info.status; + memcpy(param.add_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_ADD_DEV_EVT, ¶m); + } + break; + case BTA_HH_RMV_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + param.rmv_dev.handle = p_data->dev_info.status; + param.rmv_dev.status = p_data->dev_info.handle; + memcpy(param.rmv_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_RMV_DEV_EVT, ¶m); + break; + case BTA_HH_DATA_IND_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->int_data.status, p_data->int_data.handle); + if (p_data->int_data.status == BTA_HH_OK && p_data->int_data.p_data) { + param.data_ind.len = p_data->int_data.p_data->len; + param.data_ind.data = p_data->int_data.p_data->data + p_data->int_data.p_data->offset; + } + param.data_ind.handle = p_data->int_data.handle; + param.data_ind.status = p_data->int_data.status; + param.data_ind.proto_mode = proto_mode_change_to_upper_layer(p_data->int_data.proto_mode); + btc_hh_cb_to_app(ESP_HIDH_DATA_IND_EVT, ¶m); + break; + case BTA_HH_API_ERR_EVT: + break; + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __func__, msg->act); + break; + } + btc_hh_cb_arg_deep_free(msg); +} + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidh_args_t *dst = (btc_hidh_args_t *)p_dest; + btc_hidh_args_t *src = (btc_hidh_args_t *)p_src; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + dst->set_info.hid_info = (esp_hidh_hid_info_t *)osi_malloc(sizeof(esp_hidh_hid_info_t)); + if (dst->set_info.hid_info) { + memcpy(dst->set_info.hid_info, src->set_info.hid_info, sizeof(esp_hidh_hid_info_t)); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SET_REPORT_EVT: + dst->set_report.report = (uint8_t *)osi_malloc(src->set_report.len); + if (dst->set_report.report) { + memcpy(dst->set_report.report, src->set_report.report, src->set_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SEND_DATA_EVT: + dst->send_data.data = (uint8_t *)osi_malloc(src->send_data.len); + if (dst->send_data.data) { + memcpy(dst->send_data.data, src->send_data.data, src->send_data.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_HOST_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h new file mode 100644 index 0000000000..a2b92cf7ff --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h @@ -0,0 +1,103 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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 BTC_HD_H +#define BTC_HD_H + +#if BTC_HD_INCLUDED == TRUE + +#include +#include "bta/bta_hd_api.h" +#include "btc/btc_task.h" +#include "esp_hidd_api.h" + +typedef enum { + BTC_HD_INIT_EVT = 0, + BTC_HD_DEINIT_EVT, + BTC_HD_REGISTER_APP_EVT, + BTC_HD_UNREGISTER_APP_EVT, + BTC_HD_CONNECT_EVT, + BTC_HD_DISCONNECT_EVT, + BTC_HD_SEND_REPORT_EVT, + BTC_HD_REPORT_ERROR_EVT, + BTC_HD_UNPLUG_EVT, +} BTC_HD_EVT; + +typedef enum { BTC_HD_DISABLED = 0, BTC_HD_ENABLED, BTC_HD_DISABLING } BTC_HD_STATUS; + +/* BTIF-HD control block */ +typedef struct { + BTC_HD_STATUS status; + bool app_registered; + bool service_dereg_active; + bool forced_disc; + tBTA_HD_APP_INFO app_info; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} btc_hd_cb_t; + +/* btc_hidd_args_t */ +typedef union { + // BTC_HD_CONNECT_EVT + struct connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HD_REGISTER_APP_EVT + struct register_app_arg { + esp_hidd_app_param_t *app_param; + esp_hidd_qos_param_t *in_qos; + esp_hidd_qos_param_t *out_qos; + } register_app; + + // BTC_HD_SEND_REPORT_EVT + struct send_report_arg { + esp_hidd_report_type_t type; + uint8_t id; + uint16_t len; + uint8_t *data; + } send_report; + + // BTC_HD_REPORT_ERROR_EVT + uint8_t error; +} btc_hidd_args_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Functions + ******************************************************************************/ +void btc_hd_call_handler(btc_msg_t *msg); + +void btc_hd_cb_handler(btc_msg_t *msg); + +// extern btc_hd_cb_t btc_hd_cb; +// extern void btc_hd_remove_device(bt_bdaddr_t bd_addr); +// extern void btc_hd_service_registration(); + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +#ifdef __cplusplus +} +#endif + +#endif /* BTC_HD_INCLUDED == TRUE */ +#endif /* BTC_HD_H */ diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h new file mode 100644 index 0000000000..c07636ed1c --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h @@ -0,0 +1,187 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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 BTC_HH_H +#define BTC_HH_H + +#include +#include "bta/bta_hh_api.h" +#include "btc/btc_task.h" +#include "osi/alarm.h" +#include "esp_hidh_api.h" + +#define BTC_HH_MAX_HID 8 +#define BTC_HH_MAX_ADDED_DEV 32 + +#define BTC_HH_MAX_KEYSTATES 3 +#define BTC_HH_KEYSTATE_MASK_NUMLOCK 0x01 +#define BTC_HH_KEYSTATE_MASK_CAPSLOCK 0x02 +#define BTC_HH_KEYSTATE_MASK_SCROLLLOCK 0x04 + +#define BTC_HH_MAX_POLLING_ATTEMPTS 10 +#define BTC_HH_POLLING_SLEEP_DURATION_US 5000 + +/******************************************************************************* + * Type definitions and return values + ******************************************************************************/ +typedef enum { + BTC_HH_INIT_EVT = 0, + BTC_HH_CONNECT_EVT, + BTC_HH_DISCONNECT_EVT, + BTC_HH_UNPLUG_EVT, + BTC_HH_SET_INFO_EVT, + BTC_HH_GET_PROTO_EVT, + BTC_HH_SET_PROTO_EVT, + BTC_HH_GET_IDLE_EVT, + BTC_HH_SET_IDLE_EVT, + BTC_HH_GET_REPORT_EVT, + BTC_HH_SET_REPORT_EVT, + BTC_HH_SEND_DATA_EVT, + BTC_HH_DEINIT_EVT, +} BTC_HH_EVT; + +typedef enum { + BTC_HH_DISABLED = 0, + BTC_HH_ENABLED, + BTC_HH_DISABLING, + BTC_HH_DEV_UNKNOWN, + BTC_HH_DEV_CONNECTING, + BTC_HH_DEV_CONNECTED, + BTC_HH_DEV_DISCONNECTED +} BTC_HH_STATUS; + +typedef struct { + esp_hidh_connection_state_t dev_status; + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; + uint8_t sub_class; + uint8_t app_id; + bool ready_for_data; + osi_alarm_t *vup_timer; + bool local_vup; // Indicated locally initiated VUP +} btc_hh_device_t; + +/* Control block to maintain properties of devices */ +typedef struct { + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; +} btc_hh_added_device_t; + +/** + * BTC-HH control block to maintain added devices and currently + * connected hid devices + */ +typedef struct { + BTC_HH_STATUS status; + btc_hh_device_t devices[BTC_HH_MAX_HID]; + uint32_t device_num; + BTC_HH_EVT add_event; + btc_hh_added_device_t added_devices[BTC_HH_MAX_ADDED_DEV]; + btc_hh_device_t *p_curr_dev; + bool service_dereg_active; + BD_ADDR pending_conn_address; +} btc_hh_cb_t; + +/* btc_spp_args_t */ +typedef union { + // BTC_HH_CONNECT_EVT + struct connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HH_DISCONNECT_EVT + struct disconnect_arg { + BD_ADDR bd_addr; + } disconnect; + + // BTC_HH_UNPLUG_EVT + struct unplug_arg { + BD_ADDR bd_addr; + } unplug; + + // BTC_HH_SET_INFO_EVT + struct set_info_arg { + BD_ADDR bd_addr; + esp_hidh_hid_info_t *hid_info; + } set_info; + + // BTC_HH_GET_PROTO_EVT + struct get_protocol_arg { + BD_ADDR bd_addr; + } get_protocol; + + // BTC_HH_SET_PROTO_EVT + struct set_protocol_arg { + BD_ADDR bd_addr; + esp_hidh_protocol_mode_t protocol_mode; + } set_protocol; + + // BTC_HH_GET_IDLE_EVT + struct get_idle_arg { + BD_ADDR bd_addr; + } get_idle; + + // BTC_HH_SET_IDLE_EVT + struct set_idle_arg { + BD_ADDR bd_addr; + uint16_t idle_time; + } set_idle; + + // BTC_HH_GET_REPORT_EVT + struct get_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + uint8_t report_id; + int buffer_size; + } get_report; + + // BTC_HH_SET_REPORT_EVT + struct set_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + size_t len; + uint8_t *report; + } set_report; + + // BTC_HH_SEND_DATA_EVT + struct send_data_arg { + BD_ADDR bd_addr; + size_t len; + uint8_t *data; + } send_data; +} btc_hidh_args_t; +/******************************************************************************* + * Variables + ******************************************************************************/ +extern btc_hh_cb_t btc_hh_cb; +/******************************************************************************* + * Functions + ******************************************************************************/ + +void btc_hh_call_handler(btc_msg_t *msg); + +void btc_hh_cb_handler(btc_msg_t *msg); + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +bool btc_hh_add_added_dev(BD_ADDR bd_addr, uint16_t attr_mask); + +#endif /* BTC_HH_H */ diff --git a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h index 07663e6da2..5e94a03403 100644 --- a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h +++ b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -67,6 +67,13 @@ #define UC_BT_HFP_CLIENT_ENABLED FALSE #endif +//HID +#ifdef CONFIG_BT_HID_ENABLED +#define UC_BT_HID_ENABLED CONFIG_BT_HID_ENABLED +#else +#define UC_BT_HID_ENABLED FALSE +#endif + //HID HOST(BT) #ifdef CONFIG_BT_HID_HOST_ENABLED #define UC_BT_HID_HOST_ENABLED CONFIG_BT_HID_HOST_ENABLED @@ -74,6 +81,13 @@ #define UC_BT_HID_HOST_ENABLED FALSE #endif +//HID Device(BT) +#ifdef CONFIG_BT_HID_DEVICE_ENABLED +#define UC_BT_HID_DEVICE_ENABLED CONFIG_BT_HID_DEVICE_ENABLED +#else +#define UC_BT_HID_DEVICE_ENABLED FALSE +#endif + //SSP #ifdef CONFIG_BT_SSP_ENABLED #define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED @@ -148,7 +162,7 @@ #define UC_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE FALSE #endif -#if CONFIG_BT_CTRL_ESP32 +#if CONFIG_IDF_TARGET_ESP32 //BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP #ifdef CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP #define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP @@ -170,9 +184,9 @@ #define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 #endif -#endif //CONFIG_BT_CTRL_ESP32 +#endif //CONFIG_IDF_TARGET_ESP32 -#if (CONFIG_BT_CTRL_ESP32C3 || CONFIG_BT_CTRL_ESP32S3) +#if (CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3) //BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP #ifdef CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP #define UC_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP @@ -194,7 +208,7 @@ #define UC_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 #endif -#endif //(CONFIG_BT_CTRL_ESP32C3 || CONFIG_BT_CTRL_ESP32S3) +#endif //(CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3) //BT ACL CONNECTIONS #ifdef CONFIG_BT_ACL_CONNECTIONS @@ -365,10 +379,18 @@ #define UC_BT_LOG_MCA_TRACE_LEVEL UC_TRACE_LEVEL_WARNING #endif -#ifdef CONFIG_BT_LOG_HIDH_TRACE_LEVEL -#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HIDH_TRACE_LEVEL +#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL +#if UC_BT_HID_HOST_ENABLED +#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#elif UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#endif #else +#if UC_BT_HID_HOST_ENABLED #define UC_BT_LOG_HIDH_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#elif UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif #endif #ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 598fa92774..4afb853e6e 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -130,11 +130,22 @@ #define BT_SSP_INCLUDED TRUE #endif /* UC_BT_SSP_ENABLED */ +#if UC_BT_HID_ENABLED +#define BT_HID_INCLUDED TRUE +#endif /* UC_BT_HID_ENABLED */ + #if UC_BT_HID_HOST_ENABLED #define HID_HOST_INCLUDED TRUE #define BTA_HH_INCLUDED TRUE +#define BTC_HH_INCLUDED TRUE #endif /* UC_BT_HID_HOST_ENABLED */ +#if UC_BT_HID_DEVICE_ENABLED +#define HID_DEV_INCLUDED TRUE +#define BTA_HD_INCLUDED TRUE +#define BTC_HD_INCLUDED TRUE +#endif /* UC_BT_HID_DEVICE_ENABLED */ + #endif /* UC_BT_CLASSIC_ENABLED */ /* This is set to enable use of GAP L2CAP connections. */ @@ -321,6 +332,14 @@ #define BTC_SPP_INCLUDED FALSE #endif +#ifndef BTC_HH_INCLUDED +#define BTC_HH_INCLUDED FALSE +#endif + +#ifndef BTC_HD_INCLUDED +#define BTC_HD_INCLUDED FALSE +#endif + #ifndef SBC_DEC_INCLUDED #define SBC_DEC_INCLUDED FALSE #endif @@ -350,6 +369,10 @@ #define BTA_PAN_INCLUDED FALSE #endif +#ifndef BTA_HD_INCLUDED +#define BTA_HD_INCLUDED FALSE +#endif + #ifndef BTA_HH_INCLUDED #define BTA_HH_INCLUDED FALSE #endif @@ -1378,7 +1401,11 @@ /* The maximum number of attributes in each record. */ #ifndef SDP_MAX_REC_ATTR +#if defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) +#define SDP_MAX_REC_ATTR 25 +#else #define SDP_MAX_REC_ATTR 8 +#endif /* defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) */ #endif #ifndef SDP_MAX_PAD_LEN @@ -1845,6 +1872,18 @@ Range: 2 octets ** HID ** ******************************************************************************/ +#ifndef BT_HID_INCLUDED +#define BT_HID_INCLUDED FALSE +#endif + +/* HID Device Role Included */ +#ifndef HID_DEV_INCLUDED +#define HID_DEV_INCLUDED FALSE +#endif + +#ifndef HID_DEV_SUBCLASS +#define HID_DEV_SUBCLASS COD_MINOR_POINTING +#endif #ifndef HID_CONTROL_BUF_SIZE #define HID_CONTROL_BUF_SIZE BT_DEFAULT_BUFFER_SIZE @@ -1854,6 +1893,14 @@ Range: 2 octets #define HID_INTERRUPT_BUF_SIZE BT_DEFAULT_BUFFER_SIZE #endif +#ifndef HID_DEV_MTU_SIZE +#define HID_DEV_MTU_SIZE 64 +#endif + +#ifndef HID_DEV_FLUSH_TO +#define HID_DEV_FLUSH_TO 0xffff +#endif + /************************************************************************* ** Definitions for Both HID-Host & Device */ diff --git a/components/bt/host/bluedroid/common/include/common/bt_trace.h b/components/bt/host/bluedroid/common/include/common/bt_trace.h index 55babed961..a4f1ca749a 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_trace.h +++ b/components/bt/host/bluedroid/common/include/common/bt_trace.h @@ -106,6 +106,7 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define BTTRC_ID_STK_CE 51 #define BTTRC_ID_STK_SNEP 52 #define BTTRC_ID_STK_NDEF 53 +#define BTTRC_ID_STK_HIDD 54 /* LayerIDs for BTA */ #define BTTRC_ID_BTA_ACC 55 /* Advanced Camera Client */ @@ -199,6 +200,7 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define AVRC_INITIAL_TRACE_LEVEL UC_BT_LOG_AVRC_TRACE_LEVEL #define MCA_INITIAL_TRACE_LEVEL UC_BT_LOG_MCA_TRACE_LEVEL #define HIDH_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDH_TRACE_LEVEL +#define HIDD_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDD_TRACE_LEVEL #define APPL_INITIAL_TRACE_LEVEL UC_BT_LOG_APPL_TRACE_LEVEL #define GATT_INITIAL_TRACE_LEVEL UC_BT_LOG_GATT_TRACE_LEVEL #define SMP_INITIAL_TRACE_LEVEL UC_BT_LOG_SMP_TRACE_LEVEL @@ -258,6 +260,14 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define HIDH_TRACE_EVENT(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDH,EVENT)) BT_PRINT_D("BT_HIDH", fmt, ## args);} #define HIDH_TRACE_DEBUG(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDH,DEBUG)) BT_PRINT_D("BT_HIDH", fmt, ## args);} +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(HIDD, ERROR)) BT_PRINT_E("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_WARNING(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(HIDD, WARNING)) BT_PRINT_W("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_API(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(HIDD,API)) BT_PRINT_I("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_EVENT(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDD,EVENT)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_DEBUG(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDD,DEBUG)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_VERBOSE(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(HIDD,VERBOSE)) BT_PRINT_D("BT_HIDD", fmt, ## args);} + /* define traces for BNEP */ #define BNEP_TRACE_ERROR(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BNEP, ERROR)) BT_PRINT_E("BT_BNEP", fmt, ## args);} @@ -418,6 +428,14 @@ extern UINT8 btif_trace_level; #define HIDH_TRACE_EVENT(fmt, args...) #define HIDH_TRACE_DEBUG(fmt, args...) +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) +#define HIDD_TRACE_WARNING(fmt, args...) +#define HIDD_TRACE_API(fmt, args...) +#define HIDD_TRACE_EVENT(fmt, args...) +#define HIDD_TRACE_DEBUG(fmt, args...) +#define HIDD_TRACE_VERBOSE(fmt, args...) + /* define traces for BNEP */ #define BNEP_TRACE_ERROR(fmt, args...) diff --git a/components/bt/host/bluedroid/main/bte_init.c b/components/bt/host/bluedroid/main/bte_init.c index d2373d10eb..80c4357b6a 100644 --- a/components/bt/host/bluedroid/main/bte_init.c +++ b/components/bt/host/bluedroid/main/bte_init.c @@ -52,6 +52,14 @@ #include "pan_api.h" #endif +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) +#include "stack/hidh_api.h" +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +#endif + #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) #include "stack/avrc_api.h" #endif @@ -118,6 +126,10 @@ #include "bta_hh_int.h" #endif +#if BTA_HD_INCLUDED==TRUE +#include "bta_hd_int.h" +#endif + #if BTA_JV_INCLUDED==TRUE #include "bta_jv_int.h" #endif @@ -175,6 +187,12 @@ void BTE_DeinitStack(void) bta_gattc_cb_ptr = NULL; } #endif +#if BTA_HD_INCLUDED==TRUE + if (bta_hd_cb_ptr){ + osi_free(bta_hd_cb_ptr); + bta_hd_cb_ptr = NULL; + } +#endif #if BTA_HH_INCLUDED==TRUE if (bta_hh_cb_ptr){ osi_free(bta_hh_cb_ptr); @@ -249,6 +267,14 @@ void BTE_DeinitStack(void) } #endif // BTA_INCLUDED == TRUE +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + HID_DevDeinit(); +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + HID_HostDeinit(); +#endif + #if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) GAP_Deinit(); #endif @@ -347,7 +373,15 @@ bt_status_t BTE_InitStack(void) #endif #if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) - HID_HostInit(); + if (HID_HostInit() != HID_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + if (HID_DevInit() != HID_SUCCESS) { + goto error_exit; + } #endif #if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) @@ -434,6 +468,12 @@ bt_status_t BTE_InitStack(void) } memset((void *)bta_hh_cb_ptr, 0, sizeof(tBTA_HH_CB)); #endif +#if BTA_HD_INCLUDED==TRUE + if ((bta_hd_cb_ptr = (tBTA_HD_CB *)osi_malloc(sizeof(tBTA_HD_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hd_cb_ptr, 0, sizeof(tBTA_HD_CB)); +#endif #if BTA_HL_INCLUDED==TRUE memset((void *)bta_hl_cb_ptr, 0, sizeof(tBTA_HL_CB)); #endif diff --git a/components/bt/host/bluedroid/stack/btm/btm_sec.c b/components/bt/host/bluedroid/stack/btm/btm_sec.c index 50c0123f6b..673cdd4b4d 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_sec.c +++ b/components/bt/host/bluedroid/stack/btm/btm_sec.c @@ -2981,6 +2981,7 @@ void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT int i; DEV_CLASS dev_class; UINT8 old_sec_state; + UINT8 res; BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete\n"); if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) @@ -3161,9 +3162,12 @@ void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT /* This is required when different entities receive link notification and auth complete */ if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) { if (btm_cb.api.p_auth_complete_callback) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, - p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, HCI_SUCCESS); + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } } @@ -3853,6 +3857,7 @@ static void btm_sec_auth_collision (UINT16 handle) #if (SMP_INCLUDED == TRUE) void btm_sec_auth_complete (UINT16 handle, UINT8 status) { + UINT8 res; UINT8 old_sm4; tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); @@ -3942,9 +3947,12 @@ void btm_sec_auth_complete (UINT16 handle, UINT8 status) if (btm_cb.api.p_auth_complete_callback) { /* report the authentication status */ if (old_state != BTM_PAIR_STATE_IDLE) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, - p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, status); + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } } @@ -4241,6 +4249,7 @@ void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) { tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); UINT8 res; + UINT8 sec_dev_rec_status; BOOLEAN is_pairing_device = FALSE; tACL_CONN *p_acl_cb; UINT8 bit_shift = 0; @@ -4379,9 +4388,12 @@ void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) /* We need to notify host that the key is not known any more */ if (btm_cb.api.p_auth_complete_callback) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, - p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, status); + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } } /* @@ -4412,9 +4424,12 @@ void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) /* We need to notify host that the key is not known any more */ if (btm_cb.api.p_auth_complete_callback) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, - p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, status); + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } } @@ -4447,9 +4462,12 @@ void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) } if (btm_cb.api.p_auth_complete_callback) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, - p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, HCI_SUCCESS); + sec_dev_rec_status = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); @@ -4668,6 +4686,7 @@ void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_t tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); BOOLEAN we_are_bonding = FALSE; BOOLEAN ltk_derived_lk = FALSE; + UINT8 res; BTM_TRACE_EVENT ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d\n", (p_bda[0] << 8) + p_bda[1], (p_bda[2] << 24) + (p_bda[3] << 16) + (p_bda[4] << 8) + p_bda[5], @@ -4772,8 +4791,11 @@ void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_t /* for derived key, always send authentication callback for BR channel */ || ltk_derived_lk) { if (btm_cb.api.p_auth_complete_callback) { - (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, - p_dev_rec->sec_bd_name, HCI_SUCCESS); + res = (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + if (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } } } @@ -5760,9 +5782,10 @@ static char *btm_pair_state_descr (tBTM_PAIRING_STATE state) *******************************************************************************/ void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport) { - tBTM_SEC_CALLBACK *p_callback = p_dev_rec->p_callback; + tBTM_SEC_CALLBACK *p_callback; - if (p_dev_rec->p_callback) { + if (p_dev_rec && p_dev_rec->p_callback) { + p_callback = p_dev_rec->p_callback; p_dev_rec->p_callback = NULL; #if BLE_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h index 017eb89223..790806283b 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h @@ -394,9 +394,7 @@ tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); void btm_ble_stop_scan(void); void btm_clear_all_pending_le_entry(void); -BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, - UINT32 scan_win, UINT8 addr_type_own, - UINT8 scan_filter_policy); +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, UINT32 scan_win, UINT8 addr_type_own, UINT8 scan_filter_policy); void btm_ble_stop_inquiry(void); void btm_ble_init (void); void btm_ble_free (void); @@ -407,8 +405,7 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced); void btm_read_ble_local_supported_states_complete(UINT8 *p, UINT16 evt_len); tBTM_BLE_CONN_ST btm_ble_get_conn_st(void); void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st); -UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, - tBTM_BLE_ADV_DATA *p_data); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); tBTM_STATUS btm_ble_start_adv(void); tBTM_STATUS btm_ble_stop_adv(void); tBTM_STATUS btm_ble_start_scan(void); diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_int.h index ade758a81b..d1498a38a2 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_int.h @@ -961,12 +961,10 @@ extern tBTM_CallbackFunc conn_param_update_cb; typedef UINT8 tBTM_SEC_ACTION; -/* #ifdef __cplusplus extern "C" { #endif -*/ #if BTM_DYNAMIC_MEMORY == FALSE extern tBTM_CB btm_cb; diff --git a/components/bt/host/bluedroid/stack/btu/btu_task.c b/components/bt/host/bluedroid/stack/btu/btu_task.c index e60fcfa43b..83d4219542 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_task.c +++ b/components/bt/host/bluedroid/stack/btu/btu_task.c @@ -54,7 +54,7 @@ #endif #if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE ) -#include "hidh_int.h" +#include "hid_int.h" #endif #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) diff --git a/components/bt/host/bluedroid/stack/hid/hidd_api.c b/components/bt/host/bluedroid/stack/hid/hidd_api.c new file mode 100644 index 0000000000..347ef7ebc2 --- /dev/null +++ b/components/bt/host/bluedroid/stack/hid/hidd_api.c @@ -0,0 +1,586 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID Device API entry points + * + ******************************************************************************/ +//#include +//#include +//#include +#include "stack/hidd_api.h" +#include "esp_hidd_api.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hiddefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +#if HID_DYNAMIC_MEMORY == FALSE +tHID_DEV_CTB hd_cb; +#else +tHID_DEV_CTB *hidd_cb_ptr = NULL; +#endif + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevInit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (!hidd_cb_ptr) { + hidd_cb_ptr = (tHID_DEV_CTB *)osi_malloc(sizeof(tHID_DEV_CTB)); + if (!hidd_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ + memset(&hd_cb, 0, sizeof(tHID_DEV_CTB)); +#if defined(HIDD_INITIAL_TRACE_LEVEL) + hd_cb.trace_level = HIDD_INITIAL_TRACE_LEVEL; +#else + hd_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevDeinit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +void HID_DevDeinit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidd_cb_ptr) { + osi_free(hidd_cb_ptr); + hidd_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ +} + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with + * a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +uint8_t HID_DevSetTraceLevel(uint8_t new_level) +{ + if (new_level != 0xFF) { + hd_cb.trace_level = new_level; + } + + return (hd_cb.trace_level); +} + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK *host_cback) +{ + tHID_STATUS st; + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.reg_flag) { + return HID_ERR_ALREADY_REGISTERED; + } + + if (host_cback == NULL) { + return HID_ERR_INVALID_PARAM; + } + /* Register with L2CAP */ + if ((st = hidd_conn_reg()) != HID_SUCCESS) { + return st; + } + + hd_cb.callback = host_cback; + hd_cb.reg_flag = TRUE; + + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDeregister(void) +{ + HIDD_TRACE_API("%s", __func__); + + if (!hd_cb.reg_flag) + return (HID_ERR_NOT_REGISTERED); + hidd_conn_dereg(); + hd_cb.reg_flag = FALSE; + + return (HID_SUCCESS); +} + +tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl) +{ + HIDD_TRACE_API("%s", __func__); + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevAddRecord(uint32_t handle, char *p_name, char *p_description, char *p_provider, uint16_t subclass, + uint16_t desc_len, uint8_t *p_desc_data) +{ + bool result = TRUE; + + HIDD_TRACE_API("%s", __func__); + + // Service Class ID List + if (result) { + uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + result &= SDP_AddServiceClassIdList(handle, 1, &uuid); + } + // Protocol Descriptor List + if (result) { + tSDP_PROTOCOL_ELEM proto_list[2]; + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 1; + proto_list[0].params[0] = BT_PSM_HIDC; + proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP; + proto_list[1].num_params = 0; + result &= SDP_AddProtocolList(handle, 2, proto_list); + } + // Language Base Attribute ID List + if (result) { + result &= + SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID); + } + // Additional Protocol Descriptor List + if (result) { + tSDP_PROTO_LIST_ELEM add_proto_list; + add_proto_list.num_elems = 2; + add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + add_proto_list.list_elem[0].num_params = 1; + add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI; + add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP; + add_proto_list.list_elem[1].num_params = 0; + result &= SDP_AddAdditionProtoLists(handle, 1, &add_proto_list); + } + // Service Name (O) + // Service Description (O) + // Provider Name (O) + if (result) { + const char *srv_name = p_name; + const char *srv_desc = p_description; + const char *provider_name = p_provider; + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, strlen(srv_name) + 1, + (uint8_t *)srv_name); + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, strlen(srv_desc) + 1, + (uint8_t *)srv_desc); + result &= SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, strlen(provider_name) + 1, + (uint8_t *)provider_name); + } + // Bluetooth Profile Descriptor List + if (result) { + const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + const uint16_t version = 0x0100; + result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version); + } + // HID Parser Version + if (result) { + uint8_t *p; + const uint16_t rel_num = 0x0100; + const uint16_t parser_version = 0x0111; + const uint16_t prof_ver = 0x0100; + const uint8_t dev_subclass = subclass; + const uint8_t country_code = 0x21; + const uint8_t bool_false = 0x00; + const uint8_t bool_true = 0x01; + uint16_t temp; + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, rel_num); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, parser_version); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1, (uint8_t *)&dev_subclass); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1, (uint8_t *)&country_code); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + { + static uint8_t cdt = 0x22; + uint8_t *p_buf; + uint8_t seq_len = 4 + desc_len; + p_buf = (uint8_t *)osi_malloc(2048); + if (p_buf == NULL) { + HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ", __func__); + return HID_ERR_NOT_REGISTERED; + } + p = p_buf; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + UINT8_TO_BE_STREAM(p, cdt); + UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, desc_len); + ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE, p - p_buf, p_buf); + osi_free(p_buf); + } + { + uint8_t lang_buf[8]; + p = lang_buf; + uint8_t seq_len = 6; + uint16_t lang_english = 0x0409; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, lang_english); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE, p - lang_buf, lang_buf); + } + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_false); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, prof_ver); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + } + if (result) { + uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_group); + } + if (!result) { + HIDD_TRACE_ERROR("%s: failed to complete SDP record", __func__); + return HID_ERR_NOT_REGISTERED; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __func__, channel, type, id, len); + + if (channel == HID_CHANNEL_CTRL) { + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len, p_data); + } + + if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) { + // on INTR we can only send INPUT + return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, HID_PAR_REP_TYPE_INPUT, id, len, p_data); + } + + return HID_ERR_INVALID_PARAM; +} + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevVirtualCableUnplug(void) +{ + HIDD_TRACE_API("%s", __func__); + + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_CONTROL, HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevPlugDevice(BD_ADDR addr) +{ + hd_cb.device.in_use = TRUE; + memcpy(hd_cb.device.addr, addr, sizeof(BD_ADDR)); + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr) +{ + if (!memcmp(hd_cb.device.addr, addr, sizeof(BD_ADDR))) { + hd_cb.device.in_use = FALSE; + hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; + hd_cb.device.conn.ctrl_cid = 0; + hd_cb.device.conn.intr_cid = 0; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevConnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state != HIDD_DEV_NO_CONN) { + return HID_ERR_ALREADY_CONN; + } + return hidd_conn_initiate(); +} + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDisconnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state == HIDD_DEV_NO_CONN) { + return HID_ERR_NO_CONNECTION; + } + return hidd_conn_disconnect(); +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingPolicy(bool allow) +{ + hd_cb.allow_incoming = allow; + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevReportError(uint8_t error) +{ + uint8_t handshake_param; + + HIDD_TRACE_API("%s: error = %d", __func__, error); + + switch (error) { + case HID_PAR_HANDSHAKE_RSP_SUCCESS: + case HID_PAR_HANDSHAKE_RSP_NOT_READY: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: + case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM: + case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN: + case HID_PAR_HANDSHAKE_RSP_ERR_FATAL: + handshake_param = error; + break; + default: + handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN; + break; + } + + return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevGetDevice(BD_ADDR *addr) +{ + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.device.in_use) { + memcpy(addr, hd_cb.device.addr, sizeof(BD_ADDR)); + } else { + return HID_ERR_NOT_REGISTERED; + } + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.use_in_qos = TRUE; + hd_cb.in_qos.service_type = service_type; + hd_cb.in_qos.token_rate = token_rate; + hd_cb.in_qos.token_bucket_size = token_bucket_size; + hd_cb.in_qos.peak_bandwidth = peak_bandwidth; + hd_cb.in_qos.latency = latency; + hd_cb.in_qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetOutgoingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.l2cap_intr_cfg.qos_present = TRUE; + hd_cb.l2cap_intr_cfg.qos.service_type = service_type; + hd_cb.l2cap_intr_cfg.qos.token_rate = token_rate; + hd_cb.l2cap_intr_cfg.qos.token_bucket_size = token_bucket_size; + hd_cb.l2cap_intr_cfg.qos.peak_bandwidth = peak_bandwidth; + hd_cb.l2cap_intr_cfg.qos.latency = latency; + hd_cb.l2cap_intr_cfg.qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +#endif diff --git a/components/bt/host/bluedroid/stack/hid/hidd_conn.c b/components/bt/host/bluedroid/stack/hid/hidd_conn.c new file mode 100644 index 0000000000..05a14799e0 --- /dev/null +++ b/components/bt/host/bluedroid/stack/hid/hidd_conn.c @@ -0,0 +1,780 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * this file contains the connection interface functions + * + ******************************************************************************/ +#include "btm_int.h" +#include "hid_conn.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hidd_api.h" +#include "stack/hiddefs.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id); +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed); +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg); +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested); + +static const tL2CAP_APPL_INFO dev_reg_info = {hidd_l2cif_connect_ind, + hidd_l2cif_connect_cfm, + NULL, + hidd_l2cif_config_ind, + hidd_l2cif_config_cfm, + hidd_l2cif_disconnect_ind, + hidd_l2cif_disconnect_cfm, + NULL, + hidd_l2cif_data_ind, + hidd_l2cif_cong_ind, + NULL}; +/******************************************************************************* + * + * Function hidd_check_config_done + * + * Description Checks if connection is configured and callback can be fired + * + * Returns void + * + ******************************************************************************/ +static void hidd_check_config_done(void) +{ + tHID_CONN *p_hcon; + p_hcon = &hd_cb.device.conn; + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) && + (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + hd_cb.device.state = HIDD_DEV_CONNECTED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_OPEN, 0, NULL); + // send outstanding data on intr + if (hd_cb.pending_data) { + L2CA_DataWrite(p_hcon->intr_cid, hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + } +} +/******************************************************************************* + * + * Function hidh_sec_check_complete_term + * + * Description HID security check complete callback function. + * + * Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise + * send security block L2C connection response. + * + ******************************************************************************/ +static void hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY) { + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else if (res != BTM_SUCCESS) { + HIDD_TRACE_WARNING("%s: connection rejected by security", __func__); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, + L2CAP_CONN_OK); + return; + } +} +/******************************************************************************* + * + * Function hidd_sec_check_complete_orig + * + * Description HID security check complete callback function (device + *originated) + * + * Returns void + * + ******************************************************************************/ +void hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (p_dev->conn.conn_state != HID_CONN_STATE_SECURITY) { + HIDD_TRACE_WARNING("%s: invalid state (%02x)", __func__, p_dev->conn.conn_state); + return; + } + if (res == BTM_SUCCESS) { + HIDD_TRACE_EVENT("%s: security ok", __func__); + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else { + HIDD_TRACE_WARNING("%s: security check failed (%02x)", __func__, res); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + hidd_conn_disconnect(); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_ind + * + * Description Handles incoming L2CAP connection (we act as server) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id) +{ + tHID_CONN *p_hcon; + tHID_DEV_DEV_CTB *p_dev; + bool accept = TRUE; // accept by default + HIDD_TRACE_EVENT("%s: psm=%04x cid=%04x id=%02x", __func__, psm, cid, id); + p_dev = &hd_cb.device; + if (!hd_cb.allow_incoming) { + HIDD_TRACE_WARNING("%s: incoming connections not allowed, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + if (p_dev->in_use && memcmp(bd_addr, p_dev->addr, sizeof(BD_ADDR))) { + HIDD_TRACE_WARNING("%s: incoming connections from different device, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } else if (!p_dev->in_use) { + p_dev->in_use = TRUE; + memcpy(p_dev->addr, bd_addr, sizeof(BD_ADDR)); + p_dev->state = HIDD_DEV_NO_CONN; + } + p_hcon = &hd_cb.device.conn; + switch (psm) { + case HID_PSM_INTERRUPT: + if (p_hcon->ctrl_cid == 0) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR without CTRL, rejecting", __func__); + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + case HID_PSM_CONTROL: + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming CTRL in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + default: + accept = FALSE; + HIDD_TRACE_ERROR("%s: received invalid PSM, rejecting", __func__); + break; + } + if (!accept) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + // for CTRL we need to go through security and we reply in callback from there + if (psm == HID_PSM_CONTROL) { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = cid; + p_hcon->ctrl_id = id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if (btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN, + &hidd_sec_check_complete, p_dev) == BTM_CMD_STARTED) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + // for INTR we go directly to config state + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = cid; + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_cfm + * + * Description Handles L2CAP connection response (we act as client) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + tHID_CONN *p_hcon = &hd_cb.device.conn; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d, conn_state=%d", __func__, cid, result, p_hcon->conn_state); + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) || + ((cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR))) || + ((cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDD_TRACE_WARNING("%s: unexpected, cid:0x%04x, ctrl_cid:0x%04x, intr_cid:0x%04x, conn_state:%d", __func__, cid, + p_hcon->ctrl_cid, p_hcon->intr_cid, p_hcon->conn_state); + return; + } + if (result != L2CAP_CONN_OK) { + HIDD_TRACE_WARNING("%s: connection failed, now disconnect", __func__); + if (cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + hidd_conn_disconnect(); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_L2CAP_CONN_FAIL | (uint32_t)result, NULL); + return; + } + /* CTRL connect conf */ + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* in case disconnected before sec completed */ + btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, TRUE, BTM_SEC_PROTO_HID, HIDD_SEC_CHN, + &hidd_sec_check_complete_orig, p_dev); + } else { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); + } + return; +} +/******************************************************************************* + * + * Function hidd_l2cif_config_ind + * + * Description Handles incoming L2CAP configuration request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_DEV_MTU_SIZE)) + p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE; + else + p_hcon->rem_mtu_size = p_cfg->mtu; + // accept without changes + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + if (cid == p_hcon->intr_cid && hd_cb.use_in_qos && !p_cfg->qos_present) { + p_cfg->qos_present = TRUE; + memcpy(&p_cfg->qos, &hd_cb.in_qos, sizeof(FLOW_SPEC)); + } + L2CA_ConfigRsp(cid, p_cfg); + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_config_cfm + * + * Description Handles incoming L2CAP configuration response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + uint32_t reason; + HIDD_TRACE_EVENT("%s: cid=%04x pcfg->result=%d", __func__, cid, p_cfg->result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS && p_cfg->qos_present) { + tL2CAP_CFG_INFO new_qos; + // QoS parameters not accepted for intr, try again with host proposal + memcpy(&new_qos, &hd_cb.l2cap_intr_cfg, sizeof(new_qos)); + memcpy(&new_qos.qos, &p_cfg->qos, sizeof(FLOW_SPEC)); + new_qos.qos_present = TRUE; + HIDD_TRACE_WARNING("%s: config failed, retry", __func__); + L2CA_ConfigReq(cid, &new_qos); + return; + } else if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNKNOWN_OPTIONS) { + // QoS not understood by remote device, try configuring without QoS + HIDD_TRACE_WARNING("%s: config failed, retry without QoS", __func__); + L2CA_ConfigReq(cid, &hd_cb.l2cap_cfg); + return; + } else if (p_cfg->result != L2CAP_CFG_OK) { + HIDD_TRACE_WARNING("%s: config failed, disconnecting", __func__); + hidd_conn_disconnect(); + reason = HID_L2CAP_CFG_FAIL | (uint32_t)p_cfg->result; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, reason, NULL); + return; + } + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_ind + * + * Description Handler incoming L2CAP disconnection request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x ack_needed=%d", __func__, cid, ack_needed); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (ack_needed) + L2CA_DisconnectRsp(cid); + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + } else { + p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_cfm + * + * Description Handles L2CAP disconection response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d", __func__, cid, result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + } else { + p_hcon->intr_cid = 0; + // now disconnect CTRL + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + if (hd_cb.pending_vc_unplug) { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG, p_hcon->disc_reason, NULL); + hd_cb.pending_vc_unplug = FALSE; + } else { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } + } +} +/******************************************************************************* + * + * Function hidd_l2cif_cong_ind + * + * Description Handles L2CAP congestion status event + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x congested=%d", __func__, cid, congested); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (congested) { + p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; + } else { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + } +} +/******************************************************************************* + * + * Function hidd_l2cif_data_ind + * + * Description Handler incoming data on L2CAP channel + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg) +{ + tHID_CONN *p_hcon; + uint8_t *p_data = (uint8_t *)(p_msg + 1) + p_msg->offset; + uint8_t msg_type, param; + bool err = FALSE; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + osi_free(p_msg); + return; + } + msg_type = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // skip HID header + p_msg->offset++; + p_msg->len--; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_INTR_DATA, 0, p_msg); + return; + } + switch (msg_type) { + case HID_TRANS_GET_REPORT: + // at this stage we don't know if Report Id shall be included in request + // so we pass complete packet in callback and let other code analyze this + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_GET_REPORT, !!(param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS), p_msg); + break; + case HID_TRANS_SET_REPORT: + // as above + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_REPORT, 0, p_msg); + break; + case HID_TRANS_GET_IDLE: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, hd_cb.device.idle_time, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_IDLE: + if (p_msg->len != 2) { + HIDD_TRACE_ERROR("%s: invalid len (%d) set idle request received", __func__, p_msg->len); + err = TRUE; + } else { + hd_cb.device.idle_time = p_data[1]; + HIDD_TRACE_DEBUG("%s: idle_time = %d", __func__, hd_cb.device.idle_time); + if (hd_cb.device.idle_time) { + HIDD_TRACE_WARNING("%s: idle_time of %d ms not supported by HID Device", __func__, + (hd_cb.device.idle_time * 4)); + err = TRUE; + } + } + if (!err) { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + } else { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM, 0, 0, NULL); + } + osi_free(p_msg); + break; + case HID_TRANS_GET_PROTOCOL: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, !hd_cb.device.boot_mode, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_PROTOCOL: + hd_cb.device.boot_mode = !(param & HID_PAR_PROTOCOL_MASK); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_PROTOCOL, param & HID_PAR_PROTOCOL_MASK, NULL); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_CONTROL: + switch (param) { + case HID_PAR_CONTROL_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_EXIT_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_EXIT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidd_conn_disconnect(); + // set flag so we can notify properly when disconnected + hd_cb.pending_vc_unplug = TRUE; + break; + } + osi_free(p_msg); + break; + case HID_TRANS_DATA: + default: + HIDD_TRACE_WARNING("%s: got unsupported msg (%d)", __func__, msg_type); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ, 0, 0, NULL); + osi_free(p_msg); + break; + } +} +/******************************************************************************* + * + * Function hidd_conn_reg + * + * Description Registers L2CAP channels + * + * Returns void + * + ******************************************************************************/ +tHID_STATUS hidd_conn_reg(void) +{ + HIDD_TRACE_API("%s", __func__); + memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_cfg.mtu_present = TRUE; + hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_cfg.flush_to_present = TRUE; + hd_cb.l2cap_cfg.flush_to = HID_DEV_FLUSH_TO; + memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_intr_cfg.mtu_present = TRUE; + hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_intr_cfg.flush_to_present = TRUE; + hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO; + if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + HIDD_TRACE_ERROR("HID Control (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + L2CA_Deregister(HID_PSM_CONTROL); + HIDD_TRACE_ERROR("HID Interrupt (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_dereg + * + * Description Deregisters L2CAP channels + * + * Returns void + * + ******************************************************************************/ +void hidd_conn_dereg(void) +{ + HIDD_TRACE_API("%s", __func__); + L2CA_Deregister(HID_PSM_CONTROL); + L2CA_Deregister(HID_PSM_INTERRUPT); +} +/******************************************************************************* + * + * Function hidd_conn_initiate + * + * Description Initiates HID connection to plugged device + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_initiate(void) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + HIDD_TRACE_API("%s", __func__); + if (!p_dev->in_use) { + HIDD_TRACE_WARNING("%s: no virtual cable established", __func__); + return (HID_ERR_NOT_REGISTERED); + } + if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) { + HIDD_TRACE_WARNING("%s: connection already in progress", __func__); + return (HID_ERR_CONN_IN_PROCESS); + } + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + BTM_SetOutService(p_dev->addr, BTM_SEC_SERVICE_HIDD_SEC_CTRL, HIDD_SEC_CHN); + /* Check if L2CAP started the connection process */ + if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq(HID_PSM_CONTROL, p_dev->addr)) == 0) { + HIDD_TRACE_WARNING("%s: could not start L2CAP connection", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + } else { + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_disconnect + * + * Description Disconnects existing HID connection + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_disconnect(void) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_API("%s", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + p_hcon = &hd_cb.device.conn; + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { + /* Set l2cap idle timeout to 0 (so ACL link is disconnected + * immediately after last channel is closed) */ + L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR); + if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + L2CA_DisconnectReq(p_hcon->intr_cid); + } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + } else { + HIDD_TRACE_WARNING("%s: already disconnected", __func__); + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_send_data + * + * Description Sends data to host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data) +{ + tHID_CONN *p_hcon; + BT_HDR *p_buf; + uint8_t *p_out; + uint16_t cid; + uint16_t buf_size; + HIDD_TRACE_VERBOSE("%s: channel(%d), msg_type(%d), len(%d)", __func__, channel, msg_type, len); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { + return HID_ERR_CONGESTED; + } + switch (msg_type) { + case HID_TRANS_HANDSHAKE: + case HID_TRANS_CONTROL: + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + break; + case HID_TRANS_DATA: + if (channel == HID_CHANNEL_CTRL) { + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + } else { + cid = p_hcon->intr_cid; + buf_size = HID_INTERRUPT_BUF_SIZE; + } + break; + default: + return (HID_ERR_INVALID_PARAM); + } + p_buf = (BT_HDR *)osi_malloc(buf_size); + if (p_buf == NULL) + return (HID_ERR_NO_RESOURCES); + p_buf->offset = L2CAP_MIN_OFFSET; + p_out = (uint8_t *)(p_buf + 1) + p_buf->offset; + *p_out = HID_BUILD_HDR(msg_type, param); + p_out++; + p_buf->len = 1; // start with header only + // add report id prefix only if non-zero (which is reserved) + if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) { + *p_out = data; // report_id + p_out++; + p_buf->len++; + } + if (len > 0 && p_data != NULL) { + memcpy(p_out, p_data, len); + p_buf->len += len; + } + // check if connected + if (hd_cb.device.state != HIDD_DEV_CONNECTED) { + // for DATA on intr we hold transfer and try to reconnect + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // drop previous data, we do not queue it for now + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + } + hd_cb.pending_data = p_buf; + if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) { + hidd_conn_initiate(); + } + return HID_SUCCESS; + } + return HID_ERR_NO_CONNECTION; + } +#ifdef REPORT_TRANSFER_TIMESTAMP + if (report_transfer) { + HIDD_TRACE_ERROR("%s: report sent", __func__); + } +#endif + HIDD_TRACE_VERBOSE("%s: report sent", __func__); + if (!L2CA_DataWrite(cid, p_buf)) + return (HID_ERR_CONGESTED); + return (HID_SUCCESS); +} + +#endif diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c b/components/bt/host/bluedroid/stack/hid/hidh_api.c similarity index 91% rename from components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c rename to components/bt/host/bluedroid/stack/hid/hidh_api.c index e5a6ab6fcd..188aa0a4ee 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c +++ b/components/bt/host/bluedroid/stack/hid/hidh_api.c @@ -31,7 +31,7 @@ #include "stack/bt_types.h" #include "stack/hiddefs.h" #include "stack/hidh_api.h" -#include "hidh_int.h" +#include "hid_int.h" #include "stack/btm_api.h" #include "stack/btu.h" #include "btm_int.h" @@ -39,7 +39,9 @@ #if (HID_HOST_INCLUDED == TRUE) #if HID_DYNAMIC_MEMORY == FALSE -tHID_HOST_CTB hh_cb; +tHID_HOST_CTB hh_cb; +#else +tHID_HOST_CTB *hidh_cb_ptr = NULL; #endif static void hidh_search_callback (UINT16 sdp_result); @@ -218,18 +220,46 @@ static void hidh_search_callback (UINT16 sdp_result) ** ** Description This function initializes the control block and trace variable ** -** Returns void +** Returns tHID_STATUS ** *******************************************************************************/ -void HID_HostInit (void) +tHID_STATUS HID_HostInit (void) { +#if (HID_DYNAMIC_MEMORY) + if (!hidh_cb_ptr) { + hidh_cb_ptr = (tHID_HOST_CTB *)osi_malloc(sizeof(tHID_HOST_CTB)); + if (!hidh_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ memset(&hh_cb, 0, sizeof(tHID_HOST_CTB)); -#if defined(HID_INITIAL_TRACE_LEVEL) - hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL; +#if defined(HIDH_INITIAL_TRACE_LEVEL) + hh_cb.trace_level = HIDH_INITIAL_TRACE_LEVEL; #else hh_cb.trace_level = BT_TRACE_LEVEL_NONE; #endif + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function deinitializes the control block +** +** Returns void +** +*******************************************************************************/ +void HID_HostDeinit (void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidh_cb_ptr) { + osi_free(hidh_cb_ptr); + hidh_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ } /******************************************************************************* @@ -362,6 +392,36 @@ tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ) return (HID_SUCCESS); } +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle) +{ + int i; + /* Find an entry for this device in hh_cb.devices array */ + if (!hh_cb.reg_flag) { + return (HID_ERR_NOT_REGISTERED); + } + + for (i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ((hh_cb.devices[i].in_use) && (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == HID_HOST_MAX_DEVICES) { + *handle = 0xff; + } else { + *handle = i; + } + return (HID_SUCCESS); +} /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c b/components/bt/host/bluedroid/stack/hid/hidh_conn.c similarity index 97% rename from components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c rename to components/bt/host/bluedroid/stack/hid/hidh_conn.c index 83a4c484af..d94d9d1fa0 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c +++ b/components/bt/host/bluedroid/stack/hid/hidh_conn.c @@ -41,7 +41,7 @@ #include "stack/hiddefs.h" #include "stack/hidh_api.h" -#include "hidh_int.h" +#include "hid_int.h" #include "osi/osi.h" #if (HID_HOST_INCLUDED == TRUE) @@ -132,15 +132,16 @@ tHID_STATUS hidh_conn_disconnect (UINT8 dhandle) HIDH_TRACE_EVENT ("HID-Host disconnect"); if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { - p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; /* Set l2cap idle timeout to 0 (so ACL link is disconnected * immediately after last channel is closed) */ L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0, BT_TRANSPORT_BR_EDR); /* Disconnect both interrupt and control channels */ if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; L2CA_DisconnectReq (p_hcon->intr_cid); } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; L2CA_DisconnectReq (p_hcon->ctrl_cid); } } else { @@ -360,12 +361,12 @@ static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) p_hcon = &hh_cb.devices[dhandle].conn; } - if ((p_hcon == NULL) - || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) - || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL)) - || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) - && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING))) { - HIDH_TRACE_WARNING ("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + if ((p_hcon == NULL) || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) || + ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR)) || + ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDH_TRACE_WARNING("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); return; } @@ -592,12 +593,12 @@ static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); - p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; - if (l2cap_cid == p_hcon->ctrl_cid) { p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; } else { p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; } if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h b/components/bt/host/bluedroid/stack/hid/include/hid_conn.h similarity index 78% rename from components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h rename to components/bt/host/bluedroid/stack/hid/include/hid_conn.h index 320694c537..38bb5978f8 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h +++ b/components/bt/host/bluedroid/stack/hid/include/hid_conn.h @@ -26,18 +26,21 @@ #define HID_CONN_H #include "common/bt_defs.h" -#if (HID_HOST_INCLUDED == TRUE) + +#if (BT_HID_INCLUDED == TRUE) /* Define the HID Connection Block */ typedef struct hid_conn { -#define HID_CONN_STATE_UNUSED (0) -#define HID_CONN_STATE_CONNECTING_CTRL (1) -#define HID_CONN_STATE_CONNECTING_INTR (2) -#define HID_CONN_STATE_CONFIG (3) -#define HID_CONN_STATE_CONNECTED (4) -#define HID_CONN_STATE_DISCONNECTING (5) -#define HID_CONN_STATE_SECURITY (6) +#define HID_CONN_STATE_UNUSED (0) +#define HID_CONN_STATE_CONNECTING_CTRL (1) +#define HID_CONN_STATE_CONNECTING_INTR (2) +#define HID_CONN_STATE_CONFIG (3) +#define HID_CONN_STATE_CONNECTED (4) +#define HID_CONN_STATE_DISCONNECTING (5) +#define HID_CONN_STATE_SECURITY (6) +#define HID_CONN_STATE_DISCONNECTING_CTRL (7) +#define HID_CONN_STATE_DISCONNECTING_INTR (8) UINT8 conn_state; @@ -62,9 +65,8 @@ typedef struct hid_conn { #define HID_SEC_CHN 1 #define HID_NOSEC_CHN 2 +#define HIDD_SEC_CHN 3 +#define HIDD_NOSEC_CHN 4 -#define HIDD_SEC_CHN 3 -#define HIDD_NOSEC_CHN 4 - -#endif ///HID_HOST_INCLUDED == TRUE +#endif ///BT_HID_INCLUDED == TRUE #endif diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h b/components/bt/host/bluedroid/stack/hid/include/hid_int.h similarity index 60% rename from components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h rename to components/bt/host/bluedroid/stack/hid/include/hid_int.h index 20eda6e3a0..d926e3a891 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h +++ b/components/bt/host/bluedroid/stack/hid/include/hid_int.h @@ -1,5 +1,6 @@ /****************************************************************************** * + * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2002-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,26 +16,21 @@ * limitations under the License. * ******************************************************************************/ - /****************************************************************************** * - * This file contains HID HOST internal definitions + * This file contains HID DEVICE internal definitions * ******************************************************************************/ +#ifndef HID_INT_H +#define HID_INT_H -#ifndef HIDH_INT_H -#define HIDH_INT_H - -#include "stack/hidh_api.h" #include "hid_conn.h" #include "stack/l2c_api.h" +#if (BT_HID_INCLUDED == TRUE) #if (HID_HOST_INCLUDED == TRUE) - -enum { - HID_DEV_NO_CONN, - HID_DEV_CONNECTED -}; +#include "stack/hidh_api.h" +enum { HID_DEV_NO_CONN, HID_DEV_CONNECTED }; typedef struct per_device_ctb { BOOLEAN in_use; @@ -70,17 +66,16 @@ extern void hidh_conn_dereg( void ); extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle); extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle); extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle); - #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif /****************************************************************************** -** Main Control Block -*******************************************************************************/ + * Main Control Block + ******************************************************************************/ + #if HID_DYNAMIC_MEMORY == FALSE -extern tHID_HOST_CTB hh_cb; +extern tHID_HOST_CTB hh_cb; #else extern tHID_HOST_CTB *hidh_cb_ptr; #define hh_cb (*hidh_cb_ptr) @@ -89,7 +84,60 @@ extern tHID_HOST_CTB *hidh_cb_ptr; #ifdef __cplusplus } #endif +#endif /* HID_HOST_INCLUDED == TRUE */ -#endif ///HID_HOST_INCLUDED == TRUE +#if (HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +enum { HIDD_DEV_NO_CONN, HIDD_DEV_CONNECTED }; +typedef struct device_ctb { + bool in_use; + BD_ADDR addr; + uint8_t state; + tHID_CONN conn; + bool boot_mode; + uint8_t idle_time; +} tHID_DEV_DEV_CTB; + +typedef struct dev_ctb { + tHID_DEV_DEV_CTB device; + tHID_DEV_HOST_CALLBACK *callback; + tL2CAP_CFG_INFO l2cap_cfg; + tL2CAP_CFG_INFO l2cap_intr_cfg; + bool use_in_qos; + FLOW_SPEC in_qos; + bool reg_flag; + uint8_t trace_level; + bool allow_incoming; + BT_HDR *pending_data; + bool pending_vc_unplug; +} tHID_DEV_CTB; + +extern tHID_STATUS hidd_conn_reg(void); +extern void hidd_conn_dereg(void); +extern tHID_STATUS hidd_conn_initiate(void); +extern tHID_STATUS hidd_conn_disconnect(void); +extern tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data); +#ifdef __cplusplus +extern "C" { #endif + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ + +#if HID_DYNAMIC_MEMORY == FALSE +extern tHID_DEV_CTB hd_cb; +#else +extern tHID_DEV_CTB *hidd_cb_ptr; +#define hd_cb (*hidd_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* HID_DEV_INCLUDED == TRUE */ + +#endif /* BT_HID_INCLUDED == TRUE */ +#endif /* HID_INT_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_api.h index 6af1b3ba05..d2dd2c4038 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_api.h @@ -74,6 +74,7 @@ enum { BTM_SET_PRIVACY_FAIL, /* 24 enable/disable local privacy failed*/ BTM_SET_STATIC_RAND_ADDR_FAIL, /* 25 Command failed */ BTM_INVALID_STATIC_RAND_ADDR, /* 26 invalid static rand addr */ + BTM_SEC_DEV_REC_REMOVED, /* 27 Device record relate to the bd_addr is removed */ }; typedef uint8_t tBTM_STATUS; @@ -1252,9 +1253,12 @@ typedef UINT8 tBTM_LINK_KEY_TYPE; #define BTM_SEC_SERVICE_HDP_SNK 48 #define BTM_SEC_SERVICE_HDP_SRC 49 #define BTM_SEC_SERVICE_ATT 50 +#define BTM_SEC_SERVICE_HIDD_SEC_CTRL 51 +#define BTM_SEC_SERVICE_HIDD_NOSEC_CTRL 52 +#define BTM_SEC_SERVICE_HIDD_INTR 53 /* Update these as services are added */ -#define BTM_SEC_SERVICE_FIRST_EMPTY 51 +#define BTM_SEC_SERVICE_FIRST_EMPTY 54 #ifndef BTM_SEC_MAX_SERVICES #define BTM_SEC_MAX_SERVICES 65 @@ -1915,11 +1919,11 @@ typedef UINT8 tBTM_CONTRL_STATE; /***************************************************************************** ** EXTERNAL FUNCTION DECLARATIONS *****************************************************************************/ -/* + #ifdef __cplusplus extern "C" { #endif -*/ + /***************************************************************************** ** DEVICE CONTROL and COMMON FUNCTIONS *****************************************************************************/ @@ -4141,10 +4145,8 @@ tBTM_STATUS BTM_SetAfhChannels (AFH_CHANNELS channels, tBTM_CMPL_CB *p_afh_chann *******************************************************************************/ tBTM_STATUS BTM_BleSetChannels (BLE_CHANNELS channels, tBTM_CMPL_CB *p_ble_channels_cmpl_cback); -/* #ifdef __cplusplus } #endif -*/ #endif /* BTM_API_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/hidd_api.h b/components/bt/host/bluedroid/stack/include/stack/hidd_api.h new file mode 100644 index 0000000000..3e99db9da1 --- /dev/null +++ b/components/bt/host/bluedroid/stack/include/stack/hidd_api.h @@ -0,0 +1,273 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef HIDD_API_H +#define HIDD_API_H +#include "hiddefs.h" +#include "sdp_api.h" + +#if (HID_DEV_INCLUDED == TRUE) +/***************************************************************************** + * Type Definitions + ****************************************************************************/ +enum { + HID_CHANNEL_INTR, + HID_CHANNEL_CTRL +}; +/* + HID_DHOST_EVT_OPEN - connected to host device (CTRL and INTR), data = n/a + HID_DHOST_EVT_CLOSE - disconnected from host device, data=reason + HID_DHOST_EVT_GET_REPORT - got GET_REPORT from host + HID_DHOST_EVT_SET_REPORT - got SET_REPORT from host + HID_DHOST_EVT_SET_PROTOCOL - got SET_PROTOCOL from host +*/ + +enum { + HID_DHOST_EVT_OPEN, + HID_DHOST_EVT_CLOSE, + HID_DHOST_EVT_GET_REPORT, + HID_DHOST_EVT_SET_REPORT, + HID_DHOST_EVT_SET_PROTOCOL, + HID_DHOST_EVT_INTR_DATA, + HID_DHOST_EVT_VC_UNPLUG, + HID_DHOST_EVT_SUSPEND, + HID_DHOST_EVT_EXIT_SUSPEND, +}; + +typedef void (tHID_DEV_HOST_CALLBACK)(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR* p_buf); + +#ifdef __cplusplus +extern "C" { +#endif +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevInit(void); + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +extern void HID_DevDeinit(void); + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback); + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDeregister(void); + +/******************************************************************************* + * + * Function HID_DevSetSecurityLevel + * + * Description Sets security level for HID device connections + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl); + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, + char* p_description, char* p_provider, + uint16_t subclass, uint16_t desc_len, + uint8_t* p_desc_data); + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, + uint16_t len, uint8_t* p_data); + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevPlugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevConnect(void); + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDisconnect(void); + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingPolicy(bool allow); + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevReportError(uint8_t error); + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevGetDevice(BD_ADDR* addr); + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetOutgoingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +extern uint8_t HID_DevSetTraceLevel(uint8_t new_level); + +#ifdef __cplusplus +} +#endif + +#endif +#endif /* HIDD_API_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/hiddefs.h b/components/bt/host/bluedroid/stack/include/stack/hiddefs.h index fab7ce2b53..71dd73e052 100644 --- a/components/bt/host/bluedroid/stack/include/stack/hiddefs.h +++ b/components/bt/host/bluedroid/stack/include/stack/hiddefs.h @@ -25,7 +25,8 @@ #ifndef HIDDEFS_H #define HIDDEFS_H #include "common/bt_target.h" -#if (HID_HOST_INCLUDED == TRUE) + +#if (HID_HOST_INCLUDED == TRUE || HID_DEV_INCLUDED == TRUE) #if (SDP_INCLUDED == TRUE) #include "stack/sdp_api.h" diff --git a/components/bt/host/bluedroid/stack/include/stack/hidh_api.h b/components/bt/host/bluedroid/stack/include/stack/hidh_api.h index 3211138cd4..2ddaf1c51a 100644 --- a/components/bt/host/bluedroid/stack/include/stack/hidh_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/hidh_api.h @@ -139,6 +139,17 @@ extern tHID_STATUS HID_HostDeregister(void); extern tHID_STATUS HID_HostAddDev (BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ); +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle); + /******************************************************************************* ** ** Function HID_HostRemoveDev @@ -191,9 +202,18 @@ extern tHID_STATUS HID_HostCloseDev(UINT8 dev_handle ); ** ** Description This function initializes the control block and trace variable ** +** Returns tHID_STATUS +*******************************************************************************/ +extern tHID_STATUS HID_HostInit(void); + +/******************************************************************************* +** Function HID_HostDeinit +** +** Description This function deinitializes the control block +** ** Returns void *******************************************************************************/ -extern void HID_HostInit(void); +extern void HID_HostDeinit(void); /******************************************************************************* ** Function HID_HostSetSecurityLevel diff --git a/components/bt/host/nimble/Kconfig.in b/components/bt/host/nimble/Kconfig.in index dac35641e8..a3a23e1574 100644 --- a/components/bt/host/nimble/Kconfig.in +++ b/components/bt/host/nimble/Kconfig.in @@ -44,8 +44,6 @@ choice BT_NIMBLE_LOG_LEVEL config BT_NIMBLE_LOG_LEVEL_NONE bool "No logs" - config BT_NIMBLE_LOG_LEVEL_CRIT - bool "Critical logs" config BT_NIMBLE_LOG_LEVEL_ERROR bool "Error logs" config BT_NIMBLE_LOG_LEVEL_WARNING @@ -62,8 +60,7 @@ config BT_NIMBLE_LOG_LEVEL default 1 if BT_NIMBLE_LOG_LEVEL_INFO default 2 if BT_NIMBLE_LOG_LEVEL_WARNING default 3 if BT_NIMBLE_LOG_LEVEL_ERROR - default 4 if BT_NIMBLE_LOG_LEVEL_CRIT - default 5 if BT_NIMBLE_LOG_LEVEL_NONE + default 4 if BT_NIMBLE_LOG_LEVEL_NONE config BT_NIMBLE_MAX_CONNECTIONS int "Maximum number of concurrent connections" diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index aef55bbf63..6a06e0459e 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit aef55bbf636ed580d4d6408a5c2e75d1f70a875e +Subproject commit 6a06e0459e43771066160f5cfade55bb749fbacd diff --git a/components/bt/include/esp32/include/esp_bt.h b/components/bt/include/esp32/include/esp_bt.h index 020b48bcf3..7c8f2ea744 100644 --- a/components/bt/include/esp32/include/esp_bt.h +++ b/components/bt/include/esp32/include/esp_bt.h @@ -149,6 +149,12 @@ the adv packet will be discarded until the memory is restored. */ #define BTDM_CTRL_AUTO_LATENCY_EFF false #endif +#ifdef CONFIG_BTDM_CTRL_HLI +#define BTDM_CTRL_HLI CONFIG_BTDM_CTRL_HLI +#else +#define BTDM_CTRL_HLI false +#endif + #ifdef CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF #define BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF #else @@ -183,6 +189,7 @@ the adv packet will be discarded until the memory is restored. */ .ble_sca = CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF, \ .pcm_role = CONFIG_BTDM_CTRL_PCM_ROLE_EFF, \ .pcm_polar = CONFIG_BTDM_CTRL_PCM_POLAR_EFF, \ + .hli = BTDM_CTRL_HLI, \ .magic = ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL, \ }; @@ -224,6 +231,7 @@ typedef struct { uint8_t ble_sca; /*!< BLE low power crystal accuracy index */ uint8_t pcm_role; /*!< PCM role (master & slave)*/ uint8_t pcm_polar; /*!< PCM polar trig (falling clk edge & rising clk edge) */ + bool hli; /*!< Using high level interrupt or not */ uint32_t magic; /*!< Magic number */ } esp_bt_controller_config_t; diff --git a/components/cbor/CMakeLists.txt b/components/cbor/CMakeLists.txt index cb31da5e5a..f213c22733 100644 --- a/components/cbor/CMakeLists.txt +++ b/components/cbor/CMakeLists.txt @@ -14,8 +14,7 @@ idf_component_register(SRCS "tinycbor/src/cborencoder_close_container_checked.c" # for open_memstream.c set_source_files_properties(tinycbor/src/open_memstream.c PROPERTIES COMPILE_DEFINITIONS "__linux__") -# cbortojson.c:378:17: assignment discards 'const' qualifier from pointer target type -target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-discarded-qualifiers") - -# cborvalidation.c:429:22: 'valf' may be used uninitialized -target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-maybe-uninitialized") +# workaround for the fact that we are passing -ffreestanding to Clang +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + target_compile_options(${COMPONENT_LIB} PRIVATE "-U __STDC_HOSTED__") +endif() diff --git a/components/cbor/component.mk b/components/cbor/component.mk index 7f4f1b9632..211ec49ffd 100644 --- a/components/cbor/component.mk +++ b/components/cbor/component.mk @@ -4,5 +4,3 @@ COMPONENT_SRCDIRS := tinycbor/src COMPONENT_PRIV_INCLUDEDIRS := tinycbor/src tinycbor/src/open_memstream.o: CFLAGS += -D__linux__ -tinycbor/src/cbortojson.o: CFLAGS += -Wno-discarded-qualifiers -tinycbor/src/cborvalidation.o: CFLAGS += -Wno-maybe-uninitialized diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index 2c3b57e1a2..77576e0a40 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -6,17 +6,24 @@ if(NOT CONFIG_LWIP_IPV6 AND NOT CMAKE_BUILD_EARLY_EXPANSION) return() endif() -set(include_dirs port/include port/include/coap libcoap/include libcoap/include/coap2) +set(include_dirs port/include port/include libcoap/include) set(srcs "libcoap/src/address.c" "libcoap/src/async.c" "libcoap/src/block.c" + "libcoap/src/coap_asn1.c" + "libcoap/src/coap_cache.c" + "libcoap/src/coap_debug.c" "libcoap/src/coap_event.c" "libcoap/src/coap_hashkey.c" + "libcoap/src/coap_io.c" + "libcoap/src/coap_mbedtls.c" + "libcoap/src/coap_notls.c" + "libcoap/src/coap_prng.c" "libcoap/src/coap_session.c" + "libcoap/src/coap_tcp.c" "libcoap/src/coap_time.c" - "port/coap_debug.c" "libcoap/src/encode.c" "libcoap/src/mem.c" "libcoap/src/net.c" @@ -25,17 +32,14 @@ set(srcs "libcoap/src/resource.c" "libcoap/src/str.c" "libcoap/src/subscribe.c" - "libcoap/src/uri.c" - "libcoap/src/coap_io.c" - "port/coap_notls.c" - "port/coap_mbedtls.c") + "libcoap/src/uri.c") idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" REQUIRES lwip mbedtls) # Silence format truncation warning, until it is fixed upstream -set_source_files_properties(port/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) +# set_source_files_properties(libcoap/src/coap_debug.c PROPERTIES COMPILE_FLAGS -Wno-format-truncation) # Needed for coap headers in public builds, also. # diff --git a/components/coap/Kconfig b/components/coap/Kconfig index a5d613e146..1221523c2b 100644 --- a/components/coap/Kconfig +++ b/components/coap/Kconfig @@ -52,6 +52,8 @@ menu "CoAP Configuration" bool "Info" config COAP_LOG_DEBUG bool "Debug" + config COAP_LOG_MBEDTLS + bool "mbedTLS" endchoice config COAP_LOG_DEFAULT_LEVEL @@ -65,5 +67,6 @@ menu "CoAP Configuration" default 5 if COAP_LOG_NOTICE default 6 if COAP_LOG_INFO default 7 if COAP_LOG_DEBUG + default 9 if COAP_LOG_MBEDTLS endmenu diff --git a/components/coap/component.mk b/components/coap/component.mk index eeec323f0c..19ef7fd50b 100644 --- a/components/coap/component.mk +++ b/components/coap/component.mk @@ -4,15 +4,15 @@ ifdef CONFIG_LWIP_IPV6 -COMPONENT_ADD_INCLUDEDIRS := port/include port/include/coap libcoap/include libcoap/include/coap2 +COMPONENT_ADD_INCLUDEDIRS := port/include port/include libcoap/include -COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_session.o libcoap/src/coap_time.o port/coap_debug.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o port/coap_mbedtls.o libcoap/src/coap_io.o port/coap_notls.o +COMPONENT_OBJS = libcoap/src/address.o libcoap/src/async.o libcoap/src/block.o libcoap/src/coap_asn1.o libcoap/src/coap_cache.o libcoap/src/coap_debug.o libcoap/src/coap_event.o libcoap/src/coap_hashkey.o libcoap/src/coap_io.o libcoap/src/coap_mbedtls.o libcoap/src/coap_notls.o libcoap/src/coap_prng.o libcoap/src/coap_session.o libcoap/src/coap_tcp.o libcoap/src/coap_time.o libcoap/src/encode.o libcoap/src/mem.o libcoap/src/net.o libcoap/src/option.o libcoap/src/pdu.o libcoap/src/resource.o libcoap/src/str.o libcoap/src/subscribe.o libcoap/src/uri.o COMPONENT_SRCDIRS := libcoap/src libcoap port COMPONENT_SUBMODULES += libcoap # Silence format truncation warning, until it is fixed upstream -port/coap_debug.o: CFLAGS += -Wno-format-truncation +# libcoap/src/coap_debug.o: CFLAGS += -Wno-format-truncation endif # CONFIG_LWIP_IPV6 diff --git a/components/coap/libcoap b/components/coap/libcoap index 98954eb30a..9b6451c365 160000 --- a/components/coap/libcoap +++ b/components/coap/libcoap @@ -1 +1 @@ -Subproject commit 98954eb30a2e728e172a6cd29430ae5bc999b585 +Subproject commit 9b6451c3654bc1f625e36c87f018c2da34bea5e6 diff --git a/components/coap/port/coap_debug.c b/components/coap/port/coap_debug.c deleted file mode 100644 index 64d6a01800..0000000000 --- a/components/coap/port/coap_debug.c +++ /dev/null @@ -1,888 +0,0 @@ -/* debug.c -- debug utilities - * - * Copyright (C) 2010--2012,2014--2019 Olaf Bergmann and others - * - * This file is part of the CoAP library libcoap. Please see - * README for terms of use. - */ - -#include "coap_config.h" - -#if defined(HAVE_STRNLEN) && defined(__GNUC__) && !defined(_GNU_SOURCE) -#define _GNU_SOURCE 1 -#endif - -#if defined(HAVE_ASSERT_H) && !defined(assert) -# include -#endif - -#include -#include -#include -#include - -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_WS2TCPIP_H -#include -#endif - -#ifdef HAVE_TIME_H -#include -#endif - -#include "libcoap.h" -#include "coap_dtls.h" -#include "block.h" -#include "coap_debug.h" -#include "encode.h" -#include "net.h" -#include "coap_mutex.h" - -#ifdef WITH_LWIP -# define fprintf(fd, ...) LWIP_PLATFORM_DIAG((__VA_ARGS__)) -# define fflush(...) -#endif - -#ifdef WITH_CONTIKI -# ifndef DEBUG -# define DEBUG DEBUG_PRINT -# endif /* DEBUG */ -#include "net/ip/uip-debug.h" -#endif - -static coap_log_t maxlog = LOG_WARNING; /* default maximum log level */ - -static int use_fprintf_for_show_pdu = 1; /* non zero to output with fprintf */ - -const char *coap_package_name(void) { - return PACKAGE_NAME; -} - -const char *coap_package_version(void) { - return PACKAGE_STRING; -} - -void -coap_set_show_pdu_output(int use_fprintf) { - use_fprintf_for_show_pdu = use_fprintf; -} - -coap_log_t -coap_get_log_level(void) { - return maxlog; -} - -void -coap_set_log_level(coap_log_t level) { - maxlog = level; -} - -/* this array has the same order as the type log_t */ -static const char *loglevels[] = { - "EMRG", "ALRT", "CRIT", "ERR ", "WARN", "NOTE", "INFO", "DEBG" -}; - -#ifdef HAVE_TIME_H - -COAP_STATIC_INLINE size_t -print_timestamp(char *s, size_t len, coap_tick_t t) { - struct tm *tmp; - time_t now = coap_ticks_to_rt(t); - tmp = localtime(&now); - return strftime(s, len, "%b %d %H:%M:%S", tmp); -} - -#else /* alternative implementation: just print the timestamp */ - -COAP_STATIC_INLINE size_t -print_timestamp(char *s, size_t len, coap_tick_t t) { -#ifdef HAVE_SNPRINTF - return snprintf(s, len, "%u.%03u", - (unsigned int)coap_ticks_to_rt(t), - (unsigned int)(t % COAP_TICKS_PER_SECOND)); -#else /* HAVE_SNPRINTF */ - /* @todo do manual conversion of timestamp */ - return 0; -#endif /* HAVE_SNPRINTF */ -} - -#endif /* HAVE_TIME_H */ - -#ifndef HAVE_STRNLEN -/** - * A length-safe strlen() fake. - * - * @param s The string to count characters != 0. - * @param maxlen The maximum length of @p s. - * - * @return The length of @p s. - */ -static inline size_t -strnlen(const char *s, size_t maxlen) { - size_t n = 0; - while(*s++ && n < maxlen) - ++n; - return n; -} -#endif /* HAVE_STRNLEN */ - -static size_t -print_readable( const uint8_t *data, size_t len, - unsigned char *result, size_t buflen, int encode_always ) { - const uint8_t hex[] = "0123456789ABCDEF"; - size_t cnt = 0; - assert(data || len == 0); - - if (buflen == 0) { /* there is nothing we can do here but return */ - return 0; - } - - while (len) { - if (!encode_always && isprint(*data)) { - if (cnt+1 < buflen) { /* keep one byte for terminating zero */ - *result++ = *data; - ++cnt; - } else { - break; - } - } else { - if (cnt+4 < buflen) { /* keep one byte for terminating zero */ - *result++ = '\\'; - *result++ = 'x'; - *result++ = hex[(*data & 0xf0) >> 4]; - *result++ = hex[*data & 0x0f]; - cnt += 4; - } else - break; - } - - ++data; --len; - } - - *result = '\0'; /* add a terminating zero */ - return cnt; -} - -#ifndef min -#define min(a,b) ((a) < (b) ? (a) : (b)) -#endif - -size_t -coap_print_addr(const struct coap_address_t *addr, unsigned char *buf, size_t len) { -#if defined( HAVE_ARPA_INET_H ) || defined( HAVE_WS2TCPIP_H ) - const void *addrptr = NULL; - in_port_t port; - unsigned char *p = buf; - size_t need_buf; - - switch (addr->addr.sa.sa_family) { - case AF_INET: - addrptr = &addr->addr.sin.sin_addr; - port = ntohs(addr->addr.sin.sin_port); - need_buf = INET_ADDRSTRLEN; - break; - case AF_INET6: - if (len < 7) /* do not proceed if buffer is even too short for [::]:0 */ - return 0; - - *p++ = '['; - - addrptr = &addr->addr.sin6.sin6_addr; - port = ntohs(addr->addr.sin6.sin6_port); - need_buf = INET6_ADDRSTRLEN; - - break; - default: - memcpy(buf, "(unknown address type)", min(22, len)); - return min(22, len); - } - - /* Cast needed for Windows, since it doesn't have the correct API signature. */ - if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p, - min(len, need_buf)) == 0) { - perror("coap_print_addr"); - return 0; - } - - p += strnlen((char *)p, len); - - if (addr->addr.sa.sa_family == AF_INET6) { - if (p < buf + len) { - *p++ = ']'; - } else - return 0; - } - - p += snprintf((char *)p, buf + len - p + 1, ":%d", port); - - return buf + len - p; -#else /* HAVE_ARPA_INET_H */ -# if WITH_CONTIKI - unsigned char *p = buf; - uint8_t i; -# if NETSTACK_CONF_WITH_IPV6 - const uint8_t hex[] = "0123456789ABCDEF"; - - if (len < 41) - return 0; - - *p++ = '['; - - for (i=0; i < 16; i += 2) { - if (i) { - *p++ = ':'; - } - *p++ = hex[(addr->addr.u8[i] & 0xf0) >> 4]; - *p++ = hex[(addr->addr.u8[i] & 0x0f)]; - *p++ = hex[(addr->addr.u8[i+1] & 0xf0) >> 4]; - *p++ = hex[(addr->addr.u8[i+1] & 0x0f)]; - } - *p++ = ']'; -# else /* WITH_UIP6 */ -# warning "IPv4 network addresses will not be included in debug output" - - if (len < 21) - return 0; -# endif /* WITH_UIP6 */ - if (buf + len - p < 6) - return 0; - -#ifdef HAVE_SNPRINTF - p += snprintf((char *)p, buf + len - p + 1, ":%d", uip_htons(addr->port)); -#else /* HAVE_SNPRINTF */ - /* @todo manual conversion of port number */ -#endif /* HAVE_SNPRINTF */ - - return p - buf; -# else /* WITH_CONTIKI */ - /* TODO: output addresses manually */ -# warning "inet_ntop() not available, network addresses will not be included in debug output" -# endif /* WITH_CONTIKI */ - return 0; -#endif -} - -#ifdef WITH_CONTIKI -# define fprintf(fd, ...) PRINTF(__VA_ARGS__) -# define fflush(...) - -# ifdef HAVE_VPRINTF -# define vfprintf(fd, ...) vprintf(__VA_ARGS__) -# else /* HAVE_VPRINTF */ -# define vfprintf(fd, ...) PRINTF(__VA_ARGS__) -# endif /* HAVE_VPRINTF */ -#endif /* WITH_CONTIKI */ - -/** Returns a textual description of the message type @p t. */ -static const char * -msg_type_string(uint16_t t) { - static const char *types[] = { "CON", "NON", "ACK", "RST", "???" }; - - return types[min(t, sizeof(types)/sizeof(char *) - 1)]; -} - -/** Returns a textual description of the method or response code. */ -static const char * -msg_code_string(uint16_t c) { - static const char *methods[] = { "0.00", "GET", "POST", "PUT", "DELETE", - "FETCH", "PATCH", "iPATCH" }; - static const char *signals[] = { "7.00", "CSM", "Ping", "Pong", "Release", - "Abort" }; - static char buf[5]; - - if (c < sizeof(methods)/sizeof(const char *)) { - return methods[c]; - } else if (c >= 224 && c - 224 < (int)(sizeof(signals)/sizeof(const char *))) { - return signals[c-224]; - } else { - snprintf(buf, sizeof(buf), "%u.%02u", (c >> 5) & 0x7, c & 0x1f); - return buf; - } -} - -/** Returns a textual description of the option name. */ -static const char * -msg_option_string(uint8_t code, uint16_t option_type) { - struct option_desc_t { - uint16_t type; - const char *name; - }; - - static struct option_desc_t options[] = { - { COAP_OPTION_IF_MATCH, "If-Match" }, - { COAP_OPTION_URI_HOST, "Uri-Host" }, - { COAP_OPTION_ETAG, "ETag" }, - { COAP_OPTION_IF_NONE_MATCH, "If-None-Match" }, - { COAP_OPTION_OBSERVE, "Observe" }, - { COAP_OPTION_URI_PORT, "Uri-Port" }, - { COAP_OPTION_LOCATION_PATH, "Location-Path" }, - { COAP_OPTION_URI_PATH, "Uri-Path" }, - { COAP_OPTION_CONTENT_FORMAT, "Content-Format" }, - { COAP_OPTION_MAXAGE, "Max-Age" }, - { COAP_OPTION_URI_QUERY, "Uri-Query" }, - { COAP_OPTION_ACCEPT, "Accept" }, - { COAP_OPTION_LOCATION_QUERY, "Location-Query" }, - { COAP_OPTION_BLOCK2, "Block2" }, - { COAP_OPTION_BLOCK1, "Block1" }, - { COAP_OPTION_PROXY_URI, "Proxy-Uri" }, - { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" }, - { COAP_OPTION_SIZE1, "Size1" }, - { COAP_OPTION_SIZE2, "Size2" }, - { COAP_OPTION_NORESPONSE, "No-Response" } - }; - - static struct option_desc_t options_csm[] = { - { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" }, - { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer" } - }; - - static struct option_desc_t options_pingpong[] = { - { COAP_SIGNALING_OPTION_CUSTODY, "Custody" } - }; - - static struct option_desc_t options_release[] = { - { COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address" }, - { COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off" } - }; - - static struct option_desc_t options_abort[] = { - { COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option" } - }; - - static char buf[6]; - size_t i; - - if (code == COAP_SIGNALING_CSM) { - for (i = 0; i < sizeof(options_csm)/sizeof(struct option_desc_t); i++) { - if (option_type == options_csm[i].type) { - return options_csm[i].name; - } - } - } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) { - for (i = 0; i < sizeof(options_pingpong)/sizeof(struct option_desc_t); i++) { - if (option_type == options_pingpong[i].type) { - return options_pingpong[i].name; - } - } - } else if (code == COAP_SIGNALING_RELEASE) { - for (i = 0; i < sizeof(options_release)/sizeof(struct option_desc_t); i++) { - if (option_type == options_release[i].type) { - return options_release[i].name; - } - } - } else if (code == COAP_SIGNALING_ABORT) { - for (i = 0; i < sizeof(options_abort)/sizeof(struct option_desc_t); i++) { - if (option_type == options_abort[i].type) { - return options_abort[i].name; - } - } - } else { - /* search option_type in list of known options */ - for (i = 0; i < sizeof(options)/sizeof(struct option_desc_t); i++) { - if (option_type == options[i].type) { - return options[i].name; - } - } - } - /* unknown option type, just print to buf */ - snprintf(buf, sizeof(buf), "%u", option_type); - return buf; -} - -static unsigned int -print_content_format(unsigned int format_type, - unsigned char *result, unsigned int buflen) { - struct desc_t { - unsigned int type; - const char *name; - }; - - static struct desc_t formats[] = { - { COAP_MEDIATYPE_TEXT_PLAIN, "text/plain" }, - { COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format" }, - { COAP_MEDIATYPE_APPLICATION_XML, "application/xml" }, - { COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream" }, - { COAP_MEDIATYPE_APPLICATION_EXI, "application/exi" }, - { COAP_MEDIATYPE_APPLICATION_JSON, "application/json" }, - { COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor" }, - { COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\"" }, - { COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key" }, - { COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set" }, - { COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json" }, - { COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json" }, - { COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor" }, - { COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor" }, - { COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi" }, - { COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi" }, - { COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml" }, - { COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml" }, - { 75, "application/dcaf+cbor" } - }; - - size_t i; - - /* search format_type in list of known content formats */ - for (i = 0; i < sizeof(formats)/sizeof(struct desc_t); i++) { - if (format_type == formats[i].type) { - return snprintf((char *)result, buflen, "%s", formats[i].name); - } - } - - /* unknown content format, just print numeric value to buf */ - return snprintf((char *)result, buflen, "%d", format_type); -} - -/** - * Returns 1 if the given @p content_format is either unknown or known - * to carry binary data. The return value @c 0 hence indicates - * printable data which is also assumed if @p content_format is @c 01. - */ -COAP_STATIC_INLINE int -is_binary(int content_format) { - return !(content_format == -1 || - content_format == COAP_MEDIATYPE_TEXT_PLAIN || - content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT || - content_format == COAP_MEDIATYPE_APPLICATION_XML || - content_format == COAP_MEDIATYPE_APPLICATION_JSON); -} - -#define COAP_DO_SHOW_OUTPUT_LINE \ - do { \ - if (use_fprintf_for_show_pdu) { \ - fprintf(COAP_DEBUG_FD, "%s", outbuf); \ - } \ - else { \ - coap_log(level, "%s", outbuf); \ - } \ - } while (0) - -void -coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { -#if COAP_CONSTRAINED_STACK - static coap_mutex_t static_show_pdu_mutex = COAP_MUTEX_INITIALIZER; - static unsigned char buf[1024]; /* need some space for output creation */ - static char outbuf[COAP_DEBUG_BUF_SIZE]; -#else /* ! COAP_CONSTRAINED_STACK */ - unsigned char buf[1024]; /* need some space for output creation */ - char outbuf[COAP_DEBUG_BUF_SIZE]; -#endif /* ! COAP_CONSTRAINED_STACK */ - size_t buf_len = 0; /* takes the number of bytes written to buf */ - int encode = 0, have_options = 0, i; - coap_opt_iterator_t opt_iter; - coap_opt_t *option; - int content_format = -1; - size_t data_len; - unsigned char *data; - int outbuflen = 0; - - /* Save time if not needed */ - if (level > coap_get_log_level()) - return; - -#if COAP_CONSTRAINED_STACK - coap_mutex_lock(&static_show_pdu_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - - snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {", - COAP_DEFAULT_VERSION, msg_type_string(pdu->type), - msg_code_string(pdu->code), pdu->tid); - - for (i = 0; i < pdu->token_length; i++) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - "%02x", pdu->token[i]); - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "}"); - - /* show options, if any */ - coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " ["); - while ((option = coap_option_next(&opt_iter))) { - if (!have_options) { - have_options = 1; - } else { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ","); - } - - if (pdu->code == COAP_SIGNALING_CSM) switch(opt_iter.type) { - case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: - buf_len = snprintf((char *)buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } else if (pdu->code == COAP_SIGNALING_PING - || pdu->code == COAP_SIGNALING_PONG) { - buf_len = 0; - } else if (pdu->code == COAP_SIGNALING_RELEASE) switch(opt_iter.type) { - case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), 0); - break; - case COAP_SIGNALING_OPTION_HOLD_OFF: - buf_len = snprintf((char *)buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } else if (pdu->code == COAP_SIGNALING_ABORT) switch(opt_iter.type) { - case COAP_SIGNALING_OPTION_BAD_CSM_OPTION: - buf_len = snprintf((char *)buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } else switch (opt_iter.type) { - case COAP_OPTION_CONTENT_FORMAT: - content_format = (int)coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option)); - - buf_len = print_content_format(content_format, buf, sizeof(buf)); - break; - - case COAP_OPTION_BLOCK1: - case COAP_OPTION_BLOCK2: - /* split block option into number/more/size where more is the - * letter M if set, the _ otherwise */ - buf_len = snprintf((char *)buf, sizeof(buf), "%u/%c/%u", - coap_opt_block_num(option), /* block number */ - COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ - (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ - - break; - - case COAP_OPTION_URI_PORT: - case COAP_OPTION_MAXAGE: - case COAP_OPTION_OBSERVE: - case COAP_OPTION_SIZE1: - case COAP_OPTION_SIZE2: - /* show values as unsigned decimal value */ - buf_len = snprintf((char *)buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - - default: - /* generic output function for all other option types */ - if (opt_iter.type == COAP_OPTION_URI_PATH || - opt_iter.type == COAP_OPTION_PROXY_URI || - opt_iter.type == COAP_OPTION_URI_HOST || - opt_iter.type == COAP_OPTION_LOCATION_PATH || - opt_iter.type == COAP_OPTION_LOCATION_QUERY || - opt_iter.type == COAP_OPTION_URI_QUERY) { - encode = 0; - } else { - encode = 1; - } - - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), encode); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - " %s:%.*s", msg_option_string(pdu->code, opt_iter.type), - (int)buf_len, buf); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " ]"); - - if (coap_get_data(pdu, &data_len, &data)) { - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, " :: "); - - if (is_binary(content_format)) { - int keep_data_len = data_len; - uint8_t *keep_data = data; - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - "binary data length %zu\n", data_len); - COAP_DO_SHOW_OUTPUT_LINE; - /* - * Output hex dump of binary data as a continuous entry - */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - "%02x", *data++); - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>"); - data_len = keep_data_len; - data = keep_data; - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n"); - COAP_DO_SHOW_OUTPUT_LINE; - /* - * Output ascii readable (if possible), immediately under the - * hex value of the character output above to help binary debugging - */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, - "%c ", isprint (*data) ? *data : '.'); - data++; - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, ">>"); - } else { - if (print_readable(data, data_len, buf, sizeof(buf), 0)) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "'%s'", buf); - } - } - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf)-outbuflen, "\n"); - COAP_DO_SHOW_OUTPUT_LINE; - -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&static_show_pdu_mutex); -#endif /* COAP_CONSTRAINED_STACK */ -} - -void coap_show_tls_version(coap_log_t level) -{ - char buffer[64]; - coap_string_tls_version(buffer, sizeof(buffer)); - coap_log(level, "%s\n", buffer); -} - -char *coap_string_tls_version(char *buffer, size_t bufsize) -{ - coap_tls_version_t *tls_version = coap_get_tls_library_version(); - char beta[8]; - char sub[2]; - char b_beta[8]; - char b_sub[2]; - - switch (tls_version->type) { - case COAP_TLS_LIBRARY_NOTLS: - snprintf(buffer, bufsize, "TLS Library: None"); - break; - case COAP_TLS_LIBRARY_TINYDTLS: - snprintf(buffer, bufsize, "TLS Library: TinyDTLS - runtime %lu.%lu.%lu, " - "libcoap built for %lu.%lu.%lu", - (unsigned long)(tls_version->version >> 16), - (unsigned long)((tls_version->version >> 8) & 0xff), - (unsigned long)(tls_version->version & 0xff), - (unsigned long)(tls_version->built_version >> 16), - (unsigned long)((tls_version->built_version >> 8) & 0xff), - (unsigned long)(tls_version->built_version & 0xff)); - break; - case COAP_TLS_LIBRARY_OPENSSL: - switch (tls_version->version &0xf) { - case 0: - strcpy(beta, "-dev"); - break; - case 0xf: - strcpy(beta, ""); - break; - default: - strcpy(beta, "-beta"); - beta[5] = (tls_version->version &0xf) + '0'; - beta[6] = '\000'; - break; - } - sub[0] = ((tls_version->version >> 4) & 0xff) ? - ((tls_version->version >> 4) & 0xff) + 'a' -1 : '\000'; - sub[1] = '\000'; - switch (tls_version->built_version &0xf) { - case 0: - strcpy(b_beta, "-dev"); - break; - case 0xf: - strcpy(b_beta, ""); - break; - default: - strcpy(b_beta, "-beta"); - b_beta[5] = (tls_version->built_version &0xf) + '0'; - b_beta[6] = '\000'; - break; - } - b_sub[0] = ((tls_version->built_version >> 4) & 0xff) ? - ((tls_version->built_version >> 4) & 0xff) + 'a' -1 : '\000'; - b_sub[1] = '\000'; - snprintf(buffer, bufsize, "TLS Library: OpenSSL - runtime " - "%lu.%lu.%lu%s%s, libcoap built for %lu.%lu.%lu%s%s", - (unsigned long)(tls_version->version >> 28), - (unsigned long)((tls_version->version >> 20) & 0xff), - (unsigned long)((tls_version->version >> 12) & 0xff), sub, beta, - (unsigned long)(tls_version->built_version >> 28), - (unsigned long)((tls_version->built_version >> 20) & 0xff), - (unsigned long)((tls_version->built_version >> 12) & 0xff), - b_sub, b_beta); - break; - case COAP_TLS_LIBRARY_GNUTLS: - snprintf(buffer, bufsize, "TLS Library: GnuTLS - runtime %lu.%lu.%lu, " - "libcoap built for %lu.%lu.%lu", - (unsigned long)(tls_version->version >> 16), - (unsigned long)((tls_version->version >> 8) & 0xff), - (unsigned long)(tls_version->version & 0xff), - (unsigned long)(tls_version->built_version >> 16), - (unsigned long)((tls_version->built_version >> 8) & 0xff), - (unsigned long)(tls_version->built_version & 0xff)); - break; - case COAP_TLS_LIBRARY_MBEDTLS: - snprintf(buffer, bufsize, "TLS Library: MbedTLS - runtime %lu.%lu.%lu, " - "libcoap built for %lu.%lu.%lu", - (unsigned long)(tls_version->version >> 24), - (unsigned long)((tls_version->version >> 16) & 0xff), - (unsigned long)((tls_version->version >> 8) & 0xff), - (unsigned long)(tls_version->built_version >> 24), - (unsigned long)((tls_version->built_version >> 16) & 0xff), - (unsigned long)((tls_version->built_version >> 8) & 0xff)); - break; - default: - snprintf(buffer, bufsize, "Library type %d unknown", tls_version->type); - break; - } - return buffer; -} - -static coap_log_handler_t log_handler = NULL; - -void coap_set_log_handler(coap_log_handler_t handler) { - log_handler = handler; -} - -void -coap_log_impl(coap_log_t level, const char *format, ...) { - - if (maxlog < level) - return; - - if (log_handler) { -#if COAP_CONSTRAINED_STACK - static coap_mutex_t static_log_mutex = COAP_MUTEX_INITIALIZER; - static char message[COAP_DEBUG_BUF_SIZE]; -#else /* ! COAP_CONSTRAINED_STACK */ - char message[COAP_DEBUG_BUF_SIZE]; -#endif /* ! COAP_CONSTRAINED_STACK */ - va_list ap; - va_start(ap, format); -#if COAP_CONSTRAINED_STACK - coap_mutex_lock(&static_log_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - - vsnprintf( message, sizeof(message), format, ap); - va_end(ap); - log_handler(level, message); -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&static_log_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - } else { - char timebuf[32]; - coap_tick_t now; - va_list ap; - FILE *log_fd; - - log_fd = level <= LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD; - - coap_ticks(&now); - if (print_timestamp(timebuf,sizeof(timebuf), now)) - fprintf(log_fd, "%s ", timebuf); - - if (level <= LOG_DEBUG) - fprintf(log_fd, "%s ", loglevels[level]); - - va_start(ap, format); - vfprintf(log_fd, format, ap); - va_end(ap); - fflush(log_fd); - } -} - -static struct packet_num_interval { - int start; - int end; -} packet_loss_intervals[10]; -static int num_packet_loss_intervals = 0; -static int packet_loss_level = 0; -static int send_packet_count = 0; - -int coap_debug_set_packet_loss(const char *loss_level) { - const char *p = loss_level; - char *end = NULL; - int n = (int)strtol(p, &end, 10), i = 0; - if (end == p || n < 0) - return 0; - if (*end == '%') { - if (n > 100) - n = 100; - packet_loss_level = n * 65536 / 100; - coap_log(LOG_DEBUG, "packet loss level set to %d%%\n", n); - } else { - if (n <= 0) - return 0; - while (i < 10) { - packet_loss_intervals[i].start = n; - if (*end == '-') { - p = end + 1; - n = (int)strtol(p, &end, 10); - if (end == p || n <= 0) - return 0; - } - packet_loss_intervals[i++].end = n; - if (*end == 0) - break; - if (*end != ',') - return 0; - p = end + 1; - n = (int)strtol(p, &end, 10); - if (end == p || n <= 0) - return 0; - } - if (i == 10) - return 0; - num_packet_loss_intervals = i; - } - send_packet_count = 0; - return 1; -} - -int coap_debug_send_packet(void) { - ++send_packet_count; - if (num_packet_loss_intervals > 0) { - int i; - for (i = 0; i < num_packet_loss_intervals; i++) { - if (send_packet_count >= packet_loss_intervals[i].start - && send_packet_count <= packet_loss_intervals[i].end) - return 0; - } - } - if ( packet_loss_level > 0 ) { - uint16_t r = 0; - prng( (uint8_t*)&r, 2 ); - if ( r < packet_loss_level ) - return 0; - } - return 1; -} diff --git a/components/coap/port/coap_mbedtls.c b/components/coap/port/coap_mbedtls.c deleted file mode 100644 index ef016a125d..0000000000 --- a/components/coap/port/coap_mbedtls.c +++ /dev/null @@ -1,1869 +0,0 @@ -/* -* coap_mbedtls.c -- mbedTLS Datagram Transport Layer Support for libcoap -* -* Copyright (C) 2019 Jon Shallow -* 2019 Jitin George -* -* This file is part of the CoAP library libcoap. Please see README for terms -* of use. -*/ - -/* - * Naming used to prevent confusion between coap sessions, mbedtls sessions etc. - * when reading the code. - * - * c_context A coap_context_t * - * c_session A coap_session_t * - * m_context A coap_mbedtls_context_t * (held in c_context->dtls_context) - * m_env A coap_mbedtls_env_t * (held in c_session->tls) - */ - -#include "coap_config.h" - -#ifdef HAVE_MBEDTLS - -/* - * Once PS #335 has been merged in, then code following a rebase needs to be - * updated removing sections that are "#ifndef PSK2_PR", and then remove all - * references to PSK2_PR. - */ -#undef PSK2_PR - -#include "libcoap.h" -#include "coap_dtls.h" -#include "net.h" -#include "mem.h" -#include "coap_debug.h" -#include "prng.h" -#include "coap_mutex.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(ESPIDF_VERSION) && defined(CONFIG_MBEDTLS_DEBUG) -#include -#endif /* ESPIDF_VERSION && CONFIG_MBEDTLS_DEBUG */ -#include -#include - -#define mbedtls_malloc(a) malloc(a) -#define mbedtls_realloc(a,b) realloc(a,b) -#define mbedtls_strdup(a) strdup(a) - -#ifdef __GNUC__ -#define UNUSED __attribute__((unused)) -#else /* __GNUC__ */ -#define UNUSED -#endif /* __GNUC__ */ - -#define IS_PSK (1 << 0) -#define IS_PKI (1 << 1) -#define IS_CLIENT (1 << 6) -#define IS_SERVER (1 << 7) - -typedef struct coap_ssl_t { - const uint8_t *pdu; - unsigned pdu_len; - unsigned peekmode; - coap_tick_t timeout; -} coap_ssl_t; - -/* - * This structure encapsulates the mbedTLS session object. - * It handles both TLS and DTLS. - * c_session->tls points to this. - */ -typedef struct coap_mbedtls_env_t { - mbedtls_ssl_context ssl; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_config conf; - mbedtls_timing_delay_context timer; - mbedtls_x509_crt cacert; - mbedtls_x509_crt public_cert; - mbedtls_pk_context private_key; - mbedtls_ssl_cookie_ctx cookie_ctx; - /* If not set, need to do do_mbedtls_handshake */ - int established; - int seen_client_hello; - coap_ssl_t coap_ssl_data; -} coap_mbedtls_env_t; - -typedef struct pki_sni_entry { - char *sni; - coap_dtls_key_t pki_key; - mbedtls_x509_crt cacert; - mbedtls_x509_crt public_cert; - mbedtls_pk_context private_key; -} pki_sni_entry; - -#ifdef PSK2_PR -typedef struct psk_sni_entry { - coap_string_t sni; - coap_dtls_spsk_info_t psk_info; -} psk_sni_entry; -#endif /* PSK2_PR */ - -typedef struct coap_mbedtls_context_t { - coap_dtls_pki_t setup_data; - size_t pki_sni_count; - pki_sni_entry *pki_sni_entry_list; -#ifdef PSK2_PR - size_t psk_sni_count; - psk_sni_entry *psk_sni_entry_list; -#endif /* PSK2_PR */ - char *root_ca_file; - char *root_ca_path; - int psk_pki_enabled; -} coap_mbedtls_context_t; - -static int coap_dgram_read(void *ctx, unsigned char *out, size_t outl) -{ - ssize_t ret = 0; - coap_session_t *c_session = (struct coap_session_t *)ctx; - coap_ssl_t *data = &((coap_mbedtls_env_t *)c_session->tls)->coap_ssl_data; - - if (!c_session->tls) { - errno = EAGAIN; - return MBEDTLS_ERR_SSL_WANT_READ; - } - - if (out != NULL) { - if (data != NULL && data->pdu_len > 0) { - if (outl < data->pdu_len) { - memcpy(out, data->pdu, outl); - ret = outl; - data->pdu += outl; - data->pdu_len -= outl; - } - else { - memcpy(out, data->pdu, data->pdu_len); - ret = data->pdu_len; - if (!data->peekmode) { - data->pdu_len = 0; - data->pdu = NULL; - } - } - } - else { - ret = MBEDTLS_ERR_SSL_WANT_READ; - errno = EAGAIN; - return ret; - } - } - return ret; -} - -/* - * return +ve data amount - * 0 no more - * -1 error (error in errno) - */ -/* callback function given to mbedtls for sending data over socket */ -static int -coap_dgram_write(void *ctx, const unsigned char *send_buffer, - size_t send_buffer_length) -{ - ssize_t result = -1; - coap_session_t *c_session = (struct coap_session_t *)ctx; - - if (c_session) { - result = coap_session_send(c_session, send_buffer, send_buffer_length); - if (result != (int)send_buffer_length) { - coap_log(LOG_WARNING, "coap_network_send failed (%zd != %zd)\n", - result, send_buffer_length); - result = 0; - } - } else { - result = 0; - } - return result; -} - -#if !defined(ESPIDF_VERSION) || (defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) && defined(CONFIG_MBEDTLS_TLS_SERVER)) -static char* -get_ip_addr(const struct coap_address_t *addr) -{ - const void *addrptr = NULL; - size_t buf_len; - - if (!addr) { - return NULL; - } - switch (addr->addr.sa.sa_family) { - case AF_INET: - addrptr = &addr->addr.sin.sin_addr; - buf_len = INET_ADDRSTRLEN; - break; - case AF_INET6: - addrptr = &addr->addr.sin6.sin6_addr; - buf_len = INET6_ADDRSTRLEN; - break; - default: - return NULL; - } - char *str = (char *)mbedtls_calloc(1, buf_len); - if (!str) { - coap_log(LOG_ERR, "Memory allocation failed\n"); - return NULL; - } - if (inet_ntop(addr->addr.sa.sa_family, addrptr, str, - buf_len) == 0) { - perror("coap_print_addr"); - return 0; - } - return str; -} -#endif /* !ESPIDF_VERSION || (CONFIG_MBEDTLS_SSL_PROTO_DTLS && CONFIG_MBEDTLS_TLS_SERVER) */ - -#if !defined(ESPIDF_VERSION) || (defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) && defined(CONFIG_MBEDTLS_PSK_MODES) && defined(CONFIG_MBEDTLS_TLS_SERVER)) -/* - * Server side PSK callback - */ -static int psk_server_callback(void *p_info, mbedtls_ssl_context *ssl, - const unsigned char *name, size_t name_len ) -{ - coap_session_t *c_session = - (coap_session_t *)p_info; - uint8_t buf[128]; - size_t psk_len; -#ifdef PSK2_PR - coap_dtls_spsk_t *setup_data; -#endif /* PSK2_PR */ - coap_mbedtls_env_t *m_env; - - coap_log(LOG_DEBUG, "got psk_identity: '%.*s'\n", - (int)name_len, name); - - if (c_session == NULL || c_session->context == NULL || - c_session->context->get_server_psk == NULL) { - return -1; - } - m_env = (coap_mbedtls_env_t *)c_session->tls; -#ifdef PSK2_PR - setup_data = &c_session->context->spsk_setup_data; - - if (setup_data->validate_id_call_back) { - coap_bin_const_t lidentity; - lidentity.length = name_len; - lidentity.s = (const uint8_t*)name; - const coap_bin_const_t *psk_key = - setup_data->validate_id_call_back(&lidentity, - c_session, - setup_data->id_call_back_arg); - - if (psk_key == NULL) - return -1; - mbedtls_ssl_set_hs_psk(ssl, psk_key->s, psk_key->length); - coap_session_refresh_psk_key(c_session, psk_key); - m_env->seen_client_hello = 1; - return 0; - } -#endif /* PSK2_PR */ - - psk_len = c_session->context->get_server_psk(c_session, - (const uint8_t*)name, - name_len, - (uint8_t*)buf, sizeof(buf)); - m_env->seen_client_hello = 1; - mbedtls_ssl_set_hs_psk(ssl, buf, psk_len); - return 0; -} -#endif /* !ESPIDF_VERSION || (CONFIG_MBEDTLS_SSL_PROTO_DTLS && CONFIG_MBEDTLS_PSK_MODES && CONFIG_MBEDTLS_TLS_SERVER) */ - -static char* -get_san_or_cn_from_cert(mbedtls_x509_crt *crt) -{ - if (crt) { -#if COAP_CONSTRAINED_STACK - static coap_mutex_t a_static_mutex = COAP_MUTEX_INITIALIZER; - static char buf[1024]; -#else /* ! COAP_CONSTRAINED_STACK */ - char buf[1024]; -#endif /* ! COAP_CONSTRAINED_STACK */ - char *cn; - char *cp; - char *tcp; - int n; - -#if COAP_CONSTRAINED_STACK - coap_mutex_lock(&a_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - - mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt ); - - /* Look first to see if Subject Alt Name is defined */ - cp = strstr(buf, "subject alt name"); - if (cp) { - cp = strchr(cp, ':'); - if (cp) { - cp++; - while (*cp == ' ') cp++; - tcp = strchr(cp, '\n'); - if (tcp) - *tcp = '\000'; - /* Take only the first entry */ - tcp = strchr(cp, ','); - if (tcp) - *tcp = '\000'; - /* Return the Subject Alt Name */ -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&a_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - return mbedtls_strdup(cp); - } - } - - /* Pull CN= out of subject name */ - cp = strstr(buf, "subject name"); - if (cp) { - cp = strchr(cp, ':'); - if (cp) { - cp++; - while (*cp == ' ') cp++; - tcp = strchr(cp, '\n'); - if (tcp) - *tcp = '\000'; - - /* Need to emulate strcasestr() here. Looking for CN= */ - n = strlen(cp) - 3; - cn = cp; - while (n > 0) { - if (((cn[0] == 'C') || (cn[0] == 'c')) && - ((cn[1] == 'N') || (cn[1] == 'n')) && - (cn[2] == '=')) { - cn += 3; - break; - } - cn++; - n--; - } - if (n > 0) { - tcp = strchr(cn, ','); - if (tcp) - *tcp = '\000'; -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&a_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - return mbedtls_strdup(cn); - } - } - } -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&a_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - } - return NULL; -} - -/* - * return 0 All OK - * -ve Error Code - */ -static int -cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, - int depth, uint32_t *flags) -{ - coap_session_t *c_session = (coap_session_t*)data; - coap_mbedtls_context_t *m_context = - (coap_mbedtls_context_t *)c_session->context->dtls_context; - coap_dtls_pki_t *setup_data = &m_context->setup_data; - char *cn = NULL; - - if (*flags == 0) - return 0; - - if (!setup_data->verify_peer_cert) { - /* Nothing is being checked */ - *flags = 0; - return 0; - } - - cn = get_san_or_cn_from_cert(crt); - - if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) { - if (setup_data->allow_expired_certs) { - *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate has expired", cn ? cn : "?", depth); - } - } - if (*flags & MBEDTLS_X509_BADCERT_FUTURE) { - if (setup_data->allow_expired_certs) { - *flags &= ~MBEDTLS_X509_BADCERT_FUTURE; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate has a future date", cn ? cn : "?", depth); - } - } - if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) { - if (setup_data->allow_bad_md_hash) { - *flags &= ~MBEDTLS_X509_BADCERT_BAD_MD; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate has a bad MD hash", cn ? cn : "?", depth); - } - } - if (*flags & MBEDTLS_X509_BADCERT_BAD_KEY) { - if (setup_data->allow_short_rsa_length) { - *flags &= ~MBEDTLS_X509_BADCERT_BAD_KEY; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate has a short RSA length", cn ? cn : "?", depth); - } - } - if (*flags & MBEDTLS_X509_BADCRL_EXPIRED) { - if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { - *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate's CRL has expired", cn ? cn : "?", depth); - } - else if (!setup_data->check_cert_revocation) { - *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; - } - } - if (*flags & MBEDTLS_X509_BADCRL_FUTURE) { - if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { - *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; - coap_log(LOG_WARNING, - " %s: %s: overridden: '%s' depth %d\n", - coap_session_str(c_session), - "The certificate's CRL has a future date", cn ? cn : "?", depth); - } - else if (!setup_data->check_cert_revocation) { - *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; - } - } - - if (*flags & MBEDTLS_X509_BADCERT_CN_MISMATCH) { - *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; - } - if (setup_data->validate_cn_call_back) { - if (!setup_data->validate_cn_call_back(cn, - crt->raw.p, - crt->raw.len, - c_session, - depth, - *flags == 0, - setup_data->cn_call_back_arg)) { - *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; - } - } - if (*flags != 0) { - char buf[128]; - char *tcp; - - mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); - tcp = strchr(buf, '\n'); - while (tcp) { - *tcp = '\000'; - coap_log(LOG_WARNING, - " %s: %s: issue 0x%x: '%s' depth %d\n", - coap_session_str(c_session), - buf, *flags, cn ? cn : "?", depth); - tcp = strchr(tcp+1, '\n'); - } - } - - if (cn) - mbedtls_free(cn); - - return 0; -} - -static int -setup_pki_credentials(mbedtls_x509_crt *cacert, - mbedtls_x509_crt *public_cert, - mbedtls_pk_context *private_key, - coap_mbedtls_env_t *m_env, - coap_mbedtls_context_t *m_context, - coap_session_t *c_session, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role) -{ - int ret; - - switch (setup_data->pki_key.key_type) { - case COAP_PKI_KEY_PEM: - if (setup_data->pki_key.key.pem.public_cert && - setup_data->pki_key.key.pem.public_cert[0] && - setup_data->pki_key.key.pem.private_key && - setup_data->pki_key.key.pem.private_key[0]) { - - mbedtls_x509_crt_init(public_cert); - mbedtls_pk_init(private_key); - - ret = mbedtls_x509_crt_parse_file(public_cert, - setup_data->pki_key.key.pem.public_cert); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse_file returned -0x%x\n\n", - -ret); - return ret; - } - - ret = mbedtls_pk_parse_keyfile(private_key, - setup_data->pki_key.key.pem.private_key, NULL); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); - return ret; - } - - ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); - return ret; - } - } - else if (role == COAP_DTLS_ROLE_SERVER) { - coap_log(LOG_ERR, - "***setup_pki: (D)TLS: No %s Certificate + Private " - "Key defined\n", - role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); - return -1; - } - - if (setup_data->pki_key.key.pem.ca_file && - setup_data->pki_key.key.pem.ca_file[0]) { - mbedtls_x509_crt_init(cacert); - ret = mbedtls_x509_crt_parse_file(cacert, - setup_data->pki_key.key.pem.ca_file); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); - } - break; - case COAP_PKI_KEY_PEM_BUF: - if (setup_data->pki_key.key.pem_buf.public_cert && - setup_data->pki_key.key.pem_buf.public_cert_len && - setup_data->pki_key.key.pem_buf.private_key && - setup_data->pki_key.key.pem_buf.private_key_len > 0) { - mbedtls_x509_crt_init(public_cert); - mbedtls_pk_init(private_key); - ret = mbedtls_x509_crt_parse(public_cert, - (const unsigned char *)setup_data->pki_key.key.pem_buf.public_cert, - setup_data->pki_key.key.pem_buf.public_cert_len); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - - ret = mbedtls_pk_parse_key(private_key, - (const unsigned char *)setup_data->pki_key.key.pem_buf.private_key, - setup_data->pki_key.key.pem_buf.private_key_len, NULL, 0); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); - return ret; - } - - ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); - return ret; - } - } else if (role == COAP_DTLS_ROLE_SERVER) { - coap_log(LOG_ERR, - "***setup_pki: (D)TLS: No %s Certificate + Private " - "Key defined\n", - role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); - return -1; - } - - if (setup_data->pki_key.key.pem_buf.ca_cert && - setup_data->pki_key.key.pem_buf.ca_cert_len > 0) { - mbedtls_x509_crt_init(cacert); - ret = mbedtls_x509_crt_parse(cacert, - (const unsigned char *)setup_data->pki_key.key.pem_buf.ca_cert, - setup_data->pki_key.key.pem_buf.ca_cert_len); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); - } - break; - case COAP_PKI_KEY_ASN1: - if (setup_data->pki_key.key.asn1.public_cert && - setup_data->pki_key.key.asn1.public_cert_len && - setup_data->pki_key.key.asn1.private_key && - setup_data->pki_key.key.asn1.private_key_len > 0) { - - mbedtls_x509_crt_init(public_cert); - mbedtls_pk_init(private_key); - ret = mbedtls_x509_crt_parse(public_cert, - (const unsigned char *)setup_data->pki_key.key.asn1.public_cert, - setup_data->pki_key.key.asn1.public_cert_len); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - - ret = mbedtls_pk_parse_key(private_key, - (const unsigned char *)setup_data->pki_key.key.asn1.private_key, - setup_data->pki_key.key.asn1.private_key_len, NULL, 0); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_pk_parse_keyfile returned -0x%x\n\n", -ret); - return ret; - } - - ret = mbedtls_ssl_conf_own_cert(&m_env->conf, public_cert, private_key); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_ssl_conf_own_cert returned -0x%x\n\n", -ret); - return ret; - } - } else if (role == COAP_DTLS_ROLE_SERVER) { - coap_log(LOG_ERR, - "***setup_pki: (D)TLS: No %s Certificate + Private " - "Key defined\n", - role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); - return -1; - } - - if (setup_data->pki_key.key.asn1.ca_cert && - setup_data->pki_key.key.asn1.ca_cert_len > 0) { - mbedtls_x509_crt_init(cacert); - ret = mbedtls_x509_crt_parse(cacert, - (const unsigned char *)setup_data->pki_key.key.asn1.ca_cert, - setup_data->pki_key.key.asn1.ca_cert_len); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); - } - break; - default: - coap_log(LOG_ERR, - "***setup_pki: (D)TLS: Unknown key type %d\n", - setup_data->pki_key.key_type); - return -1; - } - - if (m_context->root_ca_file) { - ret = mbedtls_x509_crt_parse_file(cacert, m_context->root_ca_file); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); - } - if (m_context->root_ca_path) { - ret = mbedtls_x509_crt_parse_file(cacert, m_context->root_ca_path); - if (ret < 0) { - coap_log(LOG_ERR, "mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); - return ret; - } - mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); - } - - /* - * Verify Peer. - * Need to do all checking, even if setup_data->verify_peer_cert is not set - */ - mbedtls_ssl_conf_verify(&m_env->conf, - cert_verify_callback_mbedtls, c_session); - - return 0; -} - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_TLS_SERVER) -/* - * PKI SNI callback. - */ -static int -pki_sni_callback(void *p_info, mbedtls_ssl_context *ssl, - const unsigned char *uname, size_t name_len) -{ - unsigned int i; - coap_dtls_pki_t sni_setup_data; - coap_session_t *c_session = (coap_session_t *)p_info; - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - coap_mbedtls_context_t *m_context = - (coap_mbedtls_context_t *)c_session->context->dtls_context; - int ret = 0; - - /* Is this a cached entry? */ - for (i = 0; i < m_context->pki_sni_count; i++) { - if (name_len == strlen(m_context->pki_sni_entry_list[i].sni) && - memcmp(uname, m_context->pki_sni_entry_list[i].sni, name_len) == 0) { - break; - } - } - if (i == m_context->pki_sni_count) { - /* - * New PKI SNI request - */ - char *name; - coap_dtls_key_t *new_entry; - - name = mbedtls_malloc(name_len+1); - if (name == NULL) { - ret = -1; - goto end; - } - - memcpy(name, uname, name_len); - name[name_len] = '\000'; - new_entry = - m_context->setup_data.validate_sni_call_back(name, - m_context->setup_data.sni_call_back_arg); - if (!new_entry) { - ret = -1; - mbedtls_free(name); - goto end; - } - - m_context->pki_sni_entry_list = - mbedtls_realloc(m_context->pki_sni_entry_list, - (i+1)*sizeof(pki_sni_entry)); - if (m_context->pki_sni_entry_list == NULL) { - ret = -1; - mbedtls_free(name); - goto end; - } - - m_context->pki_sni_entry_list[i].sni = name; - m_context->pki_sni_entry_list[i].pki_key = *new_entry; - sni_setup_data = m_context->setup_data; - sni_setup_data.pki_key = *new_entry; - if ((ret = setup_pki_credentials(&m_context->pki_sni_entry_list[i].cacert, - &m_context->pki_sni_entry_list[i].public_cert, - &m_context->pki_sni_entry_list[i].private_key, - m_env, - m_context, - c_session, - &sni_setup_data, COAP_DTLS_ROLE_SERVER)) < 0) { - ret = -1; - mbedtls_free(name); - goto end; - } - m_context->pki_sni_count++; - } - -end: - if (ret != -1) { - mbedtls_ssl_set_hs_ca_chain(ssl, &m_context->pki_sni_entry_list[i].cacert, - NULL); - return mbedtls_ssl_set_hs_own_cert(ssl, - &m_context->pki_sni_entry_list[i].public_cert, - &m_context->pki_sni_entry_list[i].private_key); - } - return ret; -} -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_TLS_SERVER */ - -#ifdef PSK2_PR -/* - * PSK SNI callback. - */ -static int -psk_sni_callback(void *p_info, mbedtls_ssl_context *ssl, - const unsigned char *uname, size_t name_len) -{ - unsigned int i; - coap_dtls_spsk_t sni_setup_data; - coap_session_t *c_session = (coap_session_t *)p_info; - coap_mbedtls_context_t *m_context = - (coap_mbedtls_context_t *)c_session->context->dtls_context; - int ret = 0; - - /* Is this a cached entry? */ - for (i = 0; i < m_context->psk_sni_count; i++) { - if (name_len == m_context->psk_sni_entry_list[i].sni.length && - memcmp(uname, m_context->psk_sni_entry_list[i].sni.s, name_len) == 0) { - break; - } - } - if (i == m_context->psk_sni_count) { - /* - * New PSK SNI request - */ - coap_str_const_t lsni; - uint8_t *name; - const coap_dtls_spsk_info_t *new_entry; - - name = mbedtls_malloc(name_len+1); - if (name == NULL) { - ret = -1; - goto end; - } - - memcpy(name, uname, name_len); - name[name_len] = '\000'; - - lsni.s = name; - lsni.length = name_len; - new_entry = - c_session->context->spsk_setup_data.validate_sni_call_back(&lsni, - c_session, - c_session->context->spsk_setup_data.sni_call_back_arg); - if (!new_entry) { - ret = -1; - mbedtls_free(name); - goto end; - } - - m_context->psk_sni_entry_list = - mbedtls_realloc(m_context->psk_sni_entry_list, - (i+1)*sizeof(psk_sni_entry)); - - if (m_context->psk_sni_entry_list == NULL) { - ret = -1; - mbedtls_free(name); - goto end; - } - - m_context->psk_sni_entry_list[i].sni.s = name; - m_context->psk_sni_entry_list[i].sni.length = name_len; - m_context->psk_sni_entry_list[i].psk_info = *new_entry; - sni_setup_data = c_session->context->spsk_setup_data; - sni_setup_data.psk_info = *new_entry; - m_context->psk_sni_count++; - } - -end: - if (ret != -1) { - coap_session_refresh_psk_hint(c_session, - &m_context->psk_sni_entry_list[i].psk_info.hint); - coap_session_refresh_psk_key(c_session, - &m_context->psk_sni_entry_list[i].psk_info.key); - return mbedtls_ssl_set_hs_psk(ssl, - m_context->psk_sni_entry_list[i].psk_info.key.s, - m_context->psk_sni_entry_list[i].psk_info.key.length); - } - return ret; -} -#endif /* PSK2_PR */ - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_TLS_SERVER) - -static int setup_server_ssl_session(coap_session_t *c_session, - coap_mbedtls_env_t *m_env) -{ - coap_mbedtls_context_t *m_context = - (coap_mbedtls_context_t *)c_session->context->dtls_context; - int ret = 0; - m_context->psk_pki_enabled |= IS_SERVER; - - mbedtls_ssl_cookie_init(&m_env->cookie_ctx); - if ((ret = mbedtls_ssl_config_defaults(&m_env->conf, - MBEDTLS_SSL_IS_SERVER, - c_session->proto == COAP_PROTO_DTLS ? - MBEDTLS_SSL_TRANSPORT_DATAGRAM : - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { - coap_log(LOG_ERR, "mbedtls_ssl_config_defaults returned -0x%x\n", -ret); - goto fail; - } - - mbedtls_ssl_conf_rng(&m_env->conf, mbedtls_ctr_drbg_random, &m_env->ctr_drbg); - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - mbedtls_ssl_conf_handshake_timeout(&m_env->conf, 1000, 60000); - - if (m_context->psk_pki_enabled & IS_PSK) { -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) - mbedtls_ssl_conf_psk_cb(&m_env->conf, psk_server_callback, c_session); -#ifdef PSK2_PR - if (c_session->context->spsk_setup_data.validate_sni_call_back) { - mbedtls_ssl_conf_sni(&m_env->conf, psk_sni_callback, c_session); - } -#endif /* PSK2_PR */ -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ - } -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ - - if (m_context->psk_pki_enabled & IS_PKI) { - ret = setup_pki_credentials(&m_env->cacert, &m_env->public_cert, - &m_env->private_key, m_env, m_context, - c_session, &m_context->setup_data, - COAP_DTLS_ROLE_SERVER); - if (ret < 0) { - coap_log(LOG_ERR, "PKI setup failed\n"); - return ret; - } - if (m_context->setup_data.validate_sni_call_back) { - mbedtls_ssl_conf_sni(&m_env->conf, pki_sni_callback, c_session); - } - } - - if ((ret = mbedtls_ssl_cookie_setup(&m_env->cookie_ctx, - mbedtls_ctr_drbg_random, - &m_env->ctr_drbg)) != 0) { - coap_log(LOG_ERR, "mbedtls_ssl_cookie_setup: returned -0x%x\n", -ret); - goto fail; - } - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - mbedtls_ssl_conf_dtls_cookies(&m_env->conf, mbedtls_ssl_cookie_write, - mbedtls_ssl_cookie_check, - &m_env->cookie_ctx ); - mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ -fail: - return ret; -} -#endif /* !defined(ESPIDF_VERSION) || CONFIG_MBEDTLS_TLS_SERVER) */ - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) -#define MAX_CIPHERS 100 -static int psk_ciphers[MAX_CIPHERS]; -static int pki_ciphers[MAX_CIPHERS]; -static int processed_ciphers = 0; - -static void -set_ciphersuites(mbedtls_ssl_config *conf, int is_psk) -{ - if (!processed_ciphers) { - const int *list = mbedtls_ssl_list_ciphersuites(); - int *psk_list = psk_ciphers; - int *pki_list = pki_ciphers; - - while (*list) { - const mbedtls_ssl_ciphersuite_t *cur = - mbedtls_ssl_ciphersuite_from_id(*list); - - if (cur) { - if (mbedtls_ssl_ciphersuite_uses_psk(cur)) { - if (&psk_ciphers[MAX_CIPHERS] - psk_list > 1) { - *psk_list = *list; - psk_list++; - } - else { - static int done = 0; - - if (!done) { - done = 1; - coap_log(LOG_ERR, "psk_ciphers[MAX_CIPHERS] insufficient\n"); - } - } - } - else { - if (&pki_ciphers[MAX_CIPHERS] - pki_list > 1) { - *pki_list = *list; - pki_list++; - } - else { - static int done = 0; - - if (!done) { - done = 1; - coap_log(LOG_ERR, "pki_ciphers[MAX_CIPHERS] insufficient\n"); - } - } - } - } - list++; - } - /* zero terminate */ - *psk_list = 0; - *pki_list = 0; - processed_ciphers = 1; - } - mbedtls_ssl_conf_ciphersuites(conf, is_psk ? psk_ciphers : pki_ciphers); -} -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ - -static int setup_client_ssl_session(coap_session_t *c_session, - coap_mbedtls_env_t *m_env) -{ - int ret; - - coap_mbedtls_context_t *m_context = - (coap_mbedtls_context_t *)c_session->context->dtls_context; - - m_context->psk_pki_enabled |= IS_CLIENT; - - if ((ret = mbedtls_ssl_config_defaults(&m_env->conf, - MBEDTLS_SSL_IS_CLIENT, - c_session->proto == COAP_PROTO_DTLS ? - MBEDTLS_SSL_TRANSPORT_DATAGRAM : - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { - coap_log(LOG_ERR, "mbedtls_ssl_config_defaults returned -0x%x", -ret); - goto fail; - } - -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - mbedtls_ssl_conf_handshake_timeout(&m_env->conf, 1000, 60000); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ - - mbedtls_ssl_conf_authmode(&m_env->conf, MBEDTLS_SSL_VERIFY_REQUIRED); - mbedtls_ssl_conf_rng(&m_env->conf, mbedtls_ctr_drbg_random, &m_env->ctr_drbg); - - if (m_context->psk_pki_enabled & IS_PSK) { -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) - uint8_t identity[64]; - size_t identity_len; - uint8_t psk_key[64]; - size_t psk_len; - size_t max_identity_len = sizeof(identity); - - coap_log(LOG_INFO, "Setting PSK key\n"); - psk_len = c_session->context->get_client_psk(c_session, - NULL, - 0, - identity, - &identity_len, - max_identity_len, - psk_key, - sizeof(psk_key)); - assert(identity_len < sizeof(identity)); - mbedtls_ssl_conf_psk(&m_env->conf, (const unsigned char *)psk_key, - psk_len, (const unsigned char *)identity, - identity_len); -#ifdef PSK2_PR - if (c_session->cpsk_setup_data.client_sni) { - mbedtls_ssl_set_hostname(&m_env->ssl, - c_session->cpsk_setup_data.client_sni); - } -#if 0 -/* Identity Hint currently not supported in MbedTLS */ - if (c_session->cpsk_setup_data.validate_ih_call_back) { - coap_log(LOG_DEBUG, - "CoAP Client restricted to (D)TLS1.2 with Identity Hint callback\n"); - mbedtls_ssl_conf_max_version(&m_env->conf, MBEDTLS_SSL_MAJOR_VERSION_3, - MBEDTLS_SSL_MINOR_VERSION_3); - } -#endif -#endif /* PSK2_PR */ - set_ciphersuites(&m_env->conf, 1); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ - } - else if ((m_context->psk_pki_enabled & IS_PKI) || - (m_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) { - /* - * If neither PSK or PKI have been set up, use PKI basics. - * This works providing COAP_PKI_KEY_PEM has a value of 0. - */ - if ((m_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) { - mbedtls_ssl_conf_authmode(&m_env->conf, MBEDTLS_SSL_VERIFY_OPTIONAL); - } - ret = setup_pki_credentials(&m_env->cacert, &m_env->public_cert, - &m_env->private_key, m_env, m_context, - c_session, &m_context->setup_data, - COAP_DTLS_ROLE_CLIENT); - if (ret < 0) { - coap_log(LOG_ERR, "PKI setup failed\n"); - return ret; - } -#if !defined(ESPIDF_VERSION) ||(defined(CONFIG_MBEDTLS_TLS_SERVER) && defined(CONFIG_MBEDTLS_SSL_ALPN)) - if (c_session->proto == COAP_PROTO_TLS) { - const char *alpn_list[2]; - - memset(alpn_list, 0, sizeof(alpn_list)); - alpn_list[0] = "coap"; - ret = mbedtls_ssl_conf_alpn_protocols(&m_env->conf, alpn_list); - if (ret != 0) { - coap_log(LOG_ERR, "ALPN setup failed %d)\n", ret); - } - } -#endif /* !ESPIDF_VERSION || (CONFIG_MBEDTLS_TLS_SERVER && CONFIG_MBEDTLS_SSL_ALPN) */ - if (m_context->setup_data.client_sni) { - mbedtls_ssl_set_hostname(&m_env->ssl, m_context->setup_data.client_sni); - } -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_PSK_MODES) - set_ciphersuites(&m_env->conf, 0); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_PSK_MODES */ - } - return 0; - -fail: - return ret; -} - -static void mbedtls_cleanup(coap_mbedtls_env_t *m_env) -{ - if (!m_env) { - return; - } - - mbedtls_x509_crt_free(&m_env->cacert); - mbedtls_x509_crt_free(&m_env->public_cert); - mbedtls_pk_free(&m_env->private_key); - mbedtls_entropy_free(&m_env->entropy); - mbedtls_ssl_config_free(&m_env->conf); - mbedtls_ctr_drbg_free(&m_env->ctr_drbg); - mbedtls_ssl_free(&m_env->ssl); - mbedtls_ssl_cookie_free(&m_env->cookie_ctx); -} - -static void -coap_dtls_free_mbedtls_env(coap_mbedtls_env_t *m_env) { - if (m_env) { - mbedtls_cleanup(m_env); - free(m_env); - } -} - -/* - * return -1 failure - * 0 not completed - * 1 established - */ -static int do_mbedtls_handshake(coap_session_t *c_session, - coap_mbedtls_env_t *m_env) { - int ret; - char buf[128]; - - ret = mbedtls_ssl_handshake(&m_env->ssl); - switch (ret) { - case 0: - m_env->established = 1; - coap_log(LOG_DEBUG, "* %s: MbedTLS established\n", - coap_session_str(c_session)); - ret = 1; - break; - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - errno = EAGAIN; - ret = 0; - break; - case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED: - coap_log(LOG_INFO, "hello verification requested\n"); - ret = -1; - mbedtls_ssl_session_reset(&m_env->ssl); - break; - case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: - c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; - ret = -1; - break; - default: - mbedtls_strerror(ret, buf, sizeof(buf)); - coap_log(LOG_WARNING, - "do_mbedtls_handshake: session establish " - "returned -0x%x: '%s'\n", - -ret, buf); - ret = -1; - break; - } - return ret; -} - -static void -mbedtls_debug_out(void *ctx UNUSED, int level, - const char *file, int line, const char *str) { - int log_level; - - switch (level) { - case 4: - case 3: - case 2: - log_level = LOG_DEBUG; - break; - case 1: - log_level = LOG_ERR; - break; - case 0: - default: - log_level = 0; - break; - } - coap_log(log_level, "%s:%04d: %s", file, line, str); -} - -static coap_mbedtls_env_t *coap_dtls_new_mbedtls_env(coap_session_t *c_session, - coap_dtls_role_t role) -{ - int ret = 0; - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - - if (m_env) - return m_env; - - m_env = (coap_mbedtls_env_t *)calloc(1, sizeof(coap_mbedtls_env_t)); - if (!m_env) { - return NULL; - } - - mbedtls_ssl_init(&m_env->ssl); - mbedtls_ctr_drbg_init(&m_env->ctr_drbg); - mbedtls_ssl_config_init(&m_env->conf); - mbedtls_entropy_init(&m_env->entropy); - -#if defined(ESPIDF_VERSION) && defined(CONFIG_MBEDTLS_DEBUG) - mbedtls_esp_enable_debug_log(&m_env->conf, CONFIG_MBEDTLS_DEBUG_LEVEL); -#endif /* ESPIDF_VERSION && CONFIG_MBEDTLS_DEBUG */ - if ((ret = mbedtls_ctr_drbg_seed(&m_env->ctr_drbg, - mbedtls_entropy_func, &m_env->entropy, NULL, 0)) != 0) { - coap_log(LOG_ERR, "mbedtls_ctr_drbg_seed returned -0x%x", -ret); - goto fail; - } - - if (role == COAP_DTLS_ROLE_CLIENT) { - if (setup_client_ssl_session(c_session, m_env) != 0) { - goto fail; - } -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_TLS_SERVER) - } else if (role == COAP_DTLS_ROLE_SERVER) { - if (setup_server_ssl_session(c_session, m_env) != 0) { - goto fail; - } -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_TLS_SERVER */ - } else { - goto fail; - } - - if ((ret = mbedtls_ssl_setup(&m_env->ssl, &m_env->conf)) != 0) { - goto fail; - } - mbedtls_ssl_set_bio(&m_env->ssl, c_session, coap_dgram_write, - coap_dgram_read, NULL); - mbedtls_ssl_set_timer_cb(&m_env->ssl, &m_env->timer, - mbedtls_timing_set_delay, - mbedtls_timing_get_delay); - - mbedtls_ssl_conf_dbg(&m_env->conf, mbedtls_debug_out, stdout); - return m_env; - -fail: - if (m_env) { - free(m_env); - } - return NULL; -} - -int coap_dtls_is_supported(void) { -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - return 1; -#else /* ESPIDF_VERSION && !CONFIG_MBEDTLS_SSL_PROTO_DTLS */ - coap_log(LOG_EMERG, - "libcoap not compiled for DTLS with MbedTLS" - " - update MbedTLS to include DTLS\n"); - return 0; -#endif /* ESPIDF_VERSION && !CONFIG_MBEDTLS_SSL_PROTO_DTLS */ -} - -int coap_tls_is_supported(void) -{ - return 0; -} - -void *coap_dtls_new_context(struct coap_context_t *c_context) -{ - coap_mbedtls_context_t *m_context; - (void)c_context; - - m_context = (coap_mbedtls_context_t *)calloc(1, sizeof(coap_mbedtls_context_t)); - if (m_context) { - memset(m_context, 0, sizeof(coap_mbedtls_context_t)); - } - return m_context; -} - -#ifndef PSK2_PR -int coap_dtls_context_set_psk(struct coap_context_t *c_context, - const char *identity_hint UNUSED, - coap_dtls_role_t role UNUSED) -{ - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); -#if defined(ESPIDF_VERSION) && (!defined(CONFIG_MBEDTLS_PSK_MODES) || !defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK)) - coap_log(LOG_EMERG, "coap_dtls_context_set_psk:" - " libcoap not compiled with MBEDTLS_PSK_MODES and MBEDTLS_KEY_EXCHANGE_PSK" - " - update mbedTLS to include psk mode configs\n"); - return 0; -#endif /* ESPIDF_VERSION && (!CONFIG_MBEDTLS_PSK_MODES || !CONFIG_MBEDTLS_KEY_EXCHANGE_PSK) */ - -#if defined(ESPIDF_VERSION) && !defined(CONFIG_MBEDTLS_TLS_SERVER) - coap_log(LOG_EMERG, "coap_dtls_context_set_psk:" - " libcoap not compiled for Server Mode for MbedTLS" - " - update MbedTLS to include Server Mode\n"); - return 0; -#endif /* ESPIDF_VERSION && !CONFIG_MBEDTLS_TLS_SERVER */ - m_context->psk_pki_enabled |= IS_PSK; - return 1; -} -#else /* PSK2_PR */ -/* - * return 0 failed - * 1 passed - */ -int -coap_dtls_context_set_spsk(coap_context_t *c_context, - coap_dtls_spsk_t *setup_data -) { - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); - -#if defined(ESPIDF_VERSION) && !defined(CONFIG_MBEDTLS_TLS_SERVER) - coap_log(LOG_EMERG, "coap_dtls_context_set_spsk:" - " libcoap not compiled for Server Mode for MbedTLS" - " - update MbedTLS to include Server Mode\n"); - return 0; -#endif /* ESPIDF_VERSION && !CONFIG_MBEDTLS_TLS_SERVER */ - if (!m_context || !setup_data) - return 0; - - m_context->psk_pki_enabled |= IS_PSK; - return 1; -} - -/* - * return 0 failed - * 1 passed - */ -int -coap_dtls_context_set_cpsk(coap_context_t *c_context, - coap_dtls_cpsk_t *setup_data -) { - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); - - if (!m_context || !setup_data) - return 0; - - if (setup_data->validate_ih_call_back) { - coap_log(LOG_WARNING, - "CoAP Client with MbedTLS does not support Identity Hint selection\n"); - } - m_context->psk_pki_enabled |= IS_PSK; - return 1; -} - -#endif /* PSK2_PR */ - -int coap_dtls_context_set_pki(struct coap_context_t *c_context, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role UNUSED) -{ -#if defined(ESPIDF_VERSION) && (!defined(CONFIG_MBEDTLS_PSK_MODES) || !defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK)) - coap_log(LOG_EMERG, "coap_dtls_context_set_pki:" - " libcoap not compiled with MBEDTLS_PSK_MODES and MBEDTLS_KEY_EXCHANGE_PSK" - " - update mbedTLS to include psk mode configs\n"); - return 0; -#endif /* ESPIDF_VERSION && (!CONFIG_MBEDTLS_PSK_MODES || !CONFIG_MBEDTLS_KEY_EXCHANGE_PSK) */ - - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); - - m_context->setup_data = *setup_data; - m_context->psk_pki_enabled |= IS_PKI; - return 1; -} - -int coap_dtls_context_set_pki_root_cas(struct coap_context_t *c_context, - const char *ca_file, - const char *ca_path) -{ - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); - - if (!m_context) { - coap_log(LOG_WARNING, - "coap_context_set_pki_root_cas: (D)TLS environment " - "not set up\n"); - return 0; - } - - if (ca_file == NULL && ca_path == NULL) { - coap_log(LOG_WARNING, - "coap_context_set_pki_root_cas: ca_file and/or ca_path " - "not defined\n"); - return 0; - } - if (m_context->root_ca_file) { - free(m_context->root_ca_file); - m_context->root_ca_file = NULL; - } - - if (ca_file) { - m_context->root_ca_file = mbedtls_strdup(ca_file); - } - - if (m_context->root_ca_path) { - free(m_context->root_ca_path); - m_context->root_ca_path = NULL; - } - - if (ca_path) { - m_context->root_ca_path = mbedtls_strdup(ca_path); - } - return 1; -} - -int coap_dtls_context_check_keys_enabled(struct coap_context_t *c_context) -{ - coap_mbedtls_context_t *m_context = - ((coap_mbedtls_context_t *)c_context->dtls_context); - return m_context->psk_pki_enabled ? 1 : 0; -} - -void coap_dtls_free_context(void *dtls_context) -{ - coap_mbedtls_context_t *m_context = (coap_mbedtls_context_t *)dtls_context; - unsigned int i; - - for (i = 0; i < m_context->pki_sni_count; i++) { - mbedtls_free(m_context->pki_sni_entry_list[i].sni); - - mbedtls_x509_crt_free(&m_context->pki_sni_entry_list[i].public_cert); - - mbedtls_pk_free(&m_context->pki_sni_entry_list[i].private_key); - - mbedtls_x509_crt_free(&m_context->pki_sni_entry_list[i].cacert); - } -#ifdef PSK2_PR - for (i = 0; i < m_context->psk_sni_count; i++) { - mbedtls_free(m_context->psk_sni_entry_list[i].sni.s); - } - if (m_context->psk_sni_entry_list) - mbedtls_free(m_context->pki_sni_entry_list); - -#endif /* PSK2_PR */ - - free(m_context); -} - -void *coap_dtls_new_client_session(coap_session_t *c_session) -{ -#if defined(ESPIDF_VERSION) && !defined(CONFIG_MBEDTLS_TLS_CLIENT) - (void)c_session; - coap_log(LOG_EMERG, "coap_dtls_new_client_session:" - " libcoap not compiled for Client Mode for MbedTLS" - " - update MbedTLS to include Client Mode\n"); - return NULL; -#else /* !ESPIDF_VERSION || CONFIG_MBEDTLS_TLS_CLIENT */ - coap_mbedtls_env_t *m_env = coap_dtls_new_mbedtls_env(c_session, - COAP_DTLS_ROLE_CLIENT); - int ret; - - if (m_env) { - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == -1) { - coap_dtls_free_mbedtls_env(m_env); - return NULL; - } - } - return m_env; -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_TLS_CLIENT */ -} - -void *coap_dtls_new_server_session(coap_session_t *c_session) -{ - coap_mbedtls_env_t *m_env = - (coap_mbedtls_env_t *)c_session->tls; - if (m_env) { - m_env->seen_client_hello = 1; -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ - } - return m_env; -} - -void coap_dtls_free_session(coap_session_t *c_session) -{ - if (c_session && c_session->context) { - coap_dtls_free_mbedtls_env(c_session->tls); - c_session->tls = NULL; - } - return; -} - -void coap_dtls_session_update_mtu(coap_session_t *c_session) -{ -#if !defined(ESPIDF_VERSION) || defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) - coap_mbedtls_env_t *m_env = - (coap_mbedtls_env_t *)c_session->tls; - if (m_env) { - mbedtls_ssl_set_mtu(&m_env->ssl, c_session->mtu); - } -#endif /* !ESPIDF_VERSION || CONFIG_MBEDTLS_SSL_PROTO_DTLS */ -} - -int coap_dtls_send(coap_session_t *c_session, - const uint8_t *data, - size_t data_len) -{ - int ret; - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - char buf[128]; - - assert(m_env != NULL); - - if (!m_env) { - return -1; - } - c_session->dtls_event = -1; - if (m_env->established) { - ret = mbedtls_ssl_write(&m_env->ssl, (const unsigned char*) data, data_len); - if (ret <= 0) { - switch (ret) { - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - ret = 0; - break; - case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: - c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; - ret = -1; - break; - default: - mbedtls_strerror(ret, buf, sizeof(buf)); - coap_log(LOG_WARNING, - "coap_dtls_send: " - "returned -0x%x: '%s'\n", - -ret, buf); - ret = -1; - break; - } - if (ret == -1) { - coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n"); - } - } - } else { - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == 1) { - /* Just connected, so send the data */ - return coap_dtls_send(c_session, data, data_len); - } - ret = -1; - } - - if (c_session->dtls_event >= 0) { - coap_handle_event(c_session->context, c_session->dtls_event, c_session); - if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR || - c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) { - coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); - ret = -1; - } - } - return ret; -} - -int coap_dtls_is_context_timeout(void) -{ - return 0; -} - -coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED) -{ - return 0; -} - -coap_tick_t coap_dtls_get_timeout(coap_session_t *c_session, coap_tick_t now) -{ - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - int ret = mbedtls_timing_get_delay(&m_env->timer); - - switch (ret) { - case 0: - case 1: - /* int_ms has timed out, but not fin_ms */ - return now + 1; - case 2: - /* fin_ms has timed out - time for a retry */ - return now; - default: - break; - } - - return 0; -} - -void coap_dtls_handle_timeout(coap_session_t *c_session) -{ - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - - assert(m_env != NULL); - if (((c_session->state == COAP_SESSION_STATE_HANDSHAKE) && - (++c_session->dtls_timeout_count > c_session->max_retransmit)) || - (do_mbedtls_handshake(c_session, m_env) < 0)) { - /* Too many retries */ - coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); - } - return; -} - -int coap_dtls_receive(coap_session_t *c_session, - const uint8_t *data, - size_t data_len) -{ - int ret = 1; - - c_session->dtls_event = -1; - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - assert(m_env != NULL); - - coap_ssl_t *ssl_data = &m_env->coap_ssl_data; - if (ssl_data->pdu_len) { - coap_log(LOG_INFO, "** %s: Previous data not read %u bytes\n", - coap_session_str(c_session), ssl_data->pdu_len); - } - ssl_data->pdu = data; - ssl_data->pdu_len = (unsigned)data_len; - - if (m_env->established) { -#if COAP_CONSTRAINED_STACK - static coap_mutex_t b_static_mutex = COAP_MUTEX_INITIALIZER; - static uint8_t pdu[COAP_RXBUFFER_SIZE]; -#else /* ! COAP_CONSTRAINED_STACK */ - uint8_t pdu[COAP_RXBUFFER_SIZE]; -#endif /* ! COAP_CONSTRAINED_STACK */ - -#if COAP_CONSTRAINED_STACK - coap_mutex_lock(&b_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - - if (c_session->state == COAP_SESSION_STATE_HANDSHAKE) { - coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, - c_session); - coap_session_connected(c_session); - } - - ret = mbedtls_ssl_read(&m_env->ssl, pdu, (int)sizeof(pdu)); - if (ret > 0) { - ret = coap_handle_dgram(c_session->context, c_session, pdu, (size_t)ret); -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&b_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - return ret; - } - else if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; - } - else if (ret != MBEDTLS_ERR_SSL_WANT_READ) { - char buf[128]; - - mbedtls_strerror(ret, buf, sizeof(buf)); - coap_log(LOG_WARNING, - "coap_dtls_receive: " - "returned -0x%x: '%s' (length %zd)\n", - -ret, buf, data_len); - } -#if COAP_CONSTRAINED_STACK - coap_mutex_unlock(&b_static_mutex); -#endif /* COAP_CONSTRAINED_STACK */ - ret = -1; - } - else { - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == 1) { - /* Just connected, so send the data */ - coap_session_connected(c_session); - } else { - if (ssl_data->pdu_len) { - /* Do the handshake again incase of internal timeout */ - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == 1) { - /* Just connected, so send the data */ - coap_session_connected(c_session); - } else { - ret = -1; - } - } - ret = -1; - } - } - if (c_session->dtls_event >= 0) { - coap_handle_event(c_session->context, c_session->dtls_event, c_session); - if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR || - c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) { - coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); - ret = -1; - } - } - return ret; -} - -int coap_dtls_hello(coap_session_t *c_session, - const uint8_t *data, - size_t data_len) -{ -#if defined(ESPIDF_VERSION) && (!defined(CONFIG_MBEDTLS_SSL_PROTO_DTLS) || !defined(CONFIG_MBEDTLS_TLS_SERVER)) - (void)c_session; - (void)data; - (void)data_len; - coap_log(LOG_EMERG, "coap_dtls_hello:" - " libcoap not compiled for DTLS or Server Mode for MbedTLS" - " - update MbedTLS to include DTLS and Server Mode\n"); - return -1; -#else /* !ESPIDF_VERSION) || (CONFIG_MBEDTLS_SSL_PROTO_DTLS && CONFIG_MBEDTLS_TLS_SERVER) */ - coap_mbedtls_env_t *m_env = (coap_mbedtls_env_t *)c_session->tls; - coap_ssl_t *ssl_data = m_env ? &m_env->coap_ssl_data : NULL; - int ret; - - if (m_env) { - char *str = get_ip_addr(&c_session->remote_addr); - if (!str) { - return -1; - } - if((ret = mbedtls_ssl_set_client_transport_id(&m_env->ssl, - (unsigned char *)str, strlen(str))) != 0) { - coap_log(LOG_ERR, - "mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n", - -ret); - free(str); - return -1; - } - free(str); - } - - if (!m_env) { - m_env = coap_dtls_new_mbedtls_env(c_session, COAP_DTLS_ROLE_SERVER); - if (m_env) { - c_session->tls = m_env; - ssl_data = &m_env->coap_ssl_data; - ssl_data->pdu = data; - ssl_data->pdu_len = (unsigned)data_len; - char *str = get_ip_addr(&c_session->remote_addr); - if (!str) { - return -1; - } - if((ret = mbedtls_ssl_set_client_transport_id(&m_env->ssl, - (unsigned char *)str, strlen(str)) ) != 0) { - coap_log(LOG_ERR, - "mbedtls_ssl_set_client_transport_id() returned -0x%x\n", - -ret); - free(str); - return -1; - } - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == 0 || m_env->seen_client_hello) { - m_env->seen_client_hello = 0; - free(str); - return 1; - } - free(str); - } - return 0; - } - - ssl_data->pdu = data; - ssl_data->pdu_len = (unsigned)data_len; - ret = do_mbedtls_handshake(c_session, m_env); - if (ret == 0 || m_env->seen_client_hello) { - /* The test for seen_client_hello gives the ability to setup a new - c_session to continue the do_mbedtls_handshake past the client hello - and safely allow updating of the m_env and separately - letting a new session cleanly start up. - */ - m_env->seen_client_hello = 0; - return 1; - } - return 0; -#endif /* !ESPIDF_VERSION) || (CONFIG_MBEDTLS_SSL_PROTO_DTLS && CONFIG_MBEDTLS_TLS_SERVER) */ -} - -unsigned int coap_dtls_get_overhead(coap_session_t *c_session UNUSED) -{ - return 13 + 8 + 8; -} - -void *coap_tls_new_client_session(coap_session_t *c_session UNUSED, int *connected UNUSED) -{ - return NULL; -} - -void *coap_tls_new_server_session(coap_session_t *c_session UNUSED, int *connected UNUSED) -{ - return NULL; -} - -void coap_tls_free_session( coap_session_t *c_session UNUSED) -{ - return; -} - -ssize_t coap_tls_write(coap_session_t *c_session UNUSED, - const uint8_t *data UNUSED, - size_t data_len UNUSED - ) -{ - return 0; -} - -ssize_t coap_tls_read(coap_session_t *c_session UNUSED, - uint8_t *data UNUSED, - size_t data_len UNUSED - ) -{ - return 0; -} - -void coap_dtls_startup(void) -{ - return; -} - -static int keep_log_level = 0; - -void coap_dtls_set_log_level(int level) -{ -#if !defined(ESPIDF_VERSION) - int use_level; - /* - * MbedTLS debug levels filter - * 0 No debug - * 1 Error - * 2 State change - * 3 Informational - * 4 Verbose - */ - - if (level <= LOG_ERR) { - use_level = 1; - } - else { - use_level = (level >= LOG_DEBUG) ? level - LOG_DEBUG + 2 : 0; - } - mbedtls_debug_set_threshold(use_level); -#endif /* !ESPIDF_VERSION) */ - keep_log_level = level; - return; -} - -int coap_dtls_get_log_level(void) -{ - return keep_log_level; -} - -coap_tls_version_t * coap_get_tls_library_version(void) -{ - static coap_tls_version_t version; - version.version = mbedtls_version_get_number(); - version.built_version = MBEDTLS_VERSION_NUMBER; - version.type = COAP_TLS_LIBRARY_MBEDTLS; - return &version; -} - -#else /* !HAVE_MBEDTLS */ - -#ifdef __clang__ -/* Make compilers happy that do not like empty modules. As this function is - * never used, we ignore -Wunused-function at the end of compiling this file - */ -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -static inline void dummy(void) { -} - -#endif /* HAVE_MBEDTLS */ diff --git a/components/coap/port/coap_notls.c b/components/coap/port/coap_notls.c deleted file mode 100644 index aa2c27c688..0000000000 --- a/components/coap/port/coap_notls.c +++ /dev/null @@ -1,190 +0,0 @@ -/* -* coap_notls.c -- Stub Datagram Transport Layer Support for libcoap -* -* Copyright (C) 2016 Olaf Bergmann -* -* This file is part of the CoAP library libcoap. Please see README for terms -* of use. -*/ - -#include "coap_config.h" - -#if !defined(HAVE_LIBTINYDTLS) && !defined(HAVE_OPENSSL) && !defined(HAVE_LIBGNUTLS) && !defined(HAVE_MBEDTLS) - -#include "net.h" - -#ifdef __GNUC__ -#define UNUSED __attribute__((unused)) -#else /* __GNUC__ */ -#define UNUSED -#endif /* __GNUC__ */ - -int -coap_dtls_is_supported(void) { - return 0; -} - -int -coap_tls_is_supported(void) { - return 0; -} - -coap_tls_version_t * -coap_get_tls_library_version(void) { - static coap_tls_version_t version; - version.version = 0; - version.type = COAP_TLS_LIBRARY_NOTLS; - return &version; -} - -int -coap_dtls_context_set_pki(coap_context_t *ctx UNUSED, - coap_dtls_pki_t* setup_data UNUSED, - coap_dtls_role_t role UNUSED -) { - return 0; -} - -int -coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx UNUSED, - const char *ca_file UNUSED, - const char *ca_path UNUSED -) { - return 0; -} - -int -coap_dtls_context_set_psk(coap_context_t *ctx UNUSED, - const char *hint UNUSED, - coap_dtls_role_t role UNUSED -) { - return 0; -} - -int -coap_dtls_context_check_keys_enabled(coap_context_t *ctx UNUSED) -{ - return 0; -} - -static int dtls_log_level = 0; - -void coap_dtls_startup(void) { -} - -void -coap_dtls_set_log_level(int level) { - dtls_log_level = level; -} - -int -coap_dtls_get_log_level(void) { - return dtls_log_level; -} - -void * -coap_dtls_new_context(struct coap_context_t *coap_context UNUSED) { - return NULL; -} - -void -coap_dtls_free_context(void *handle UNUSED) { -} - -void *coap_dtls_new_server_session(coap_session_t *session UNUSED) { - return NULL; -} - -void *coap_dtls_new_client_session(coap_session_t *session UNUSED) { - return NULL; -} - -void coap_dtls_free_session(coap_session_t *coap_session UNUSED) { -} - -void coap_dtls_session_update_mtu(coap_session_t *session UNUSED) { -} - -int -coap_dtls_send(coap_session_t *session UNUSED, - const uint8_t *data UNUSED, - size_t data_len UNUSED -) { - return -1; -} - -int coap_dtls_is_context_timeout(void) { - return 1; -} - -coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED) { - return 0; -} - -coap_tick_t -coap_dtls_get_timeout(coap_session_t *session UNUSED, coap_tick_t now UNUSED) { - return 0; -} - -void coap_dtls_handle_timeout(coap_session_t *session UNUSED) { -} - -int -coap_dtls_receive(coap_session_t *session UNUSED, - const uint8_t *data UNUSED, - size_t data_len UNUSED -) { - return -1; -} - -int -coap_dtls_hello(coap_session_t *session UNUSED, - const uint8_t *data UNUSED, - size_t data_len UNUSED -) { - return 0; -} - -unsigned int coap_dtls_get_overhead(coap_session_t *session UNUSED) { - return 0; -} - -void *coap_tls_new_client_session(coap_session_t *session UNUSED, int *connected UNUSED) { - return NULL; -} - -void *coap_tls_new_server_session(coap_session_t *session UNUSED, int *connected UNUSED) { - return NULL; -} - -void coap_tls_free_session(coap_session_t *coap_session UNUSED) { -} - -ssize_t coap_tls_write(coap_session_t *session UNUSED, - const uint8_t *data UNUSED, - size_t data_len UNUSED -) { - return -1; -} - -ssize_t coap_tls_read(coap_session_t *session UNUSED, - uint8_t *data UNUSED, - size_t data_len UNUSED -) { - return -1; -} - -#undef UNUSED - -#else /* !HAVE_LIBTINYDTLS && !HAVE_OPENSSL && !HAVE_LIBGNUTLS */ - -#ifdef __clang__ -/* Make compilers happy that do not like empty modules. As this function is - * never used, we ignore -Wunused-function at the end of compiling this file - */ -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -static inline void dummy(void) { -} - -#endif /* !HAVE_LIBTINYDTLS && !HAVE_OPENSSL && !HAVE_LIBGNUTLS */ diff --git a/components/coap/port/include/coap/coap_dtls.h b/components/coap/port/include/coap/coap_dtls.h deleted file mode 100644 index 2dd0e88d2e..0000000000 --- a/components/coap/port/include/coap/coap_dtls.h +++ /dev/null @@ -1,631 +0,0 @@ -/* - * coap_dtls.h -- (Datagram) Transport Layer Support for libcoap - * - * Copyright (C) 2016 Olaf Bergmann - * Copyright (C) 2017 Jean-Claude Michelou - * - * This file is part of the CoAP library libcoap. Please see README for terms - * of use. - */ - -#ifndef COAP_DTLS_H_ -#define COAP_DTLS_H_ - -#include "coap_time.h" -#include "str.h" - -struct coap_context_t; -struct coap_session_t; -struct coap_dtls_pki_t; - -/** - * @defgroup dtls DTLS Support - * API functions for interfacing with DTLS libraries. - * @{ - */ - -/** - * Check whether DTLS is available. - * - * @return @c 1 if support for DTLS is enabled, or @c 0 otherwise. - */ -int coap_dtls_is_supported(void); - -/** - * Check whether TLS is available. - * - * @return @c 1 if support for TLS is enabled, or @c 0 otherwise. - */ -int coap_tls_is_supported(void); - -typedef enum coap_tls_library_t { - COAP_TLS_LIBRARY_NOTLS = 0, /**< No DTLS library */ - COAP_TLS_LIBRARY_TINYDTLS, /**< Using TinyDTLS library */ - COAP_TLS_LIBRARY_OPENSSL, /**< Using OpenSSL library */ - COAP_TLS_LIBRARY_GNUTLS, /**< Using GnuTLS library */ - COAP_TLS_LIBRARY_MBEDTLS, /**< Using MbedTLS library */ -} coap_tls_library_t; - -/** - * The structure used for returning the underlying (D)TLS library - * information. - */ -typedef struct coap_tls_version_t { - uint64_t version; /**< (D)TLS runtime Library Version */ - coap_tls_library_t type; /**< Library type. One of COAP_TLS_LIBRARY_* */ - uint64_t built_version; /**< (D)TLS Built against Library Version */ -} coap_tls_version_t; - -/** - * Determine the type and version of the underlying (D)TLS library. - * - * @return The version and type of library libcoap was compiled against. - */ -coap_tls_version_t *coap_get_tls_library_version(void); - -/** - * Additional Security setup handler that can be set up by - * coap_context_set_pki(). - * Invoked when libcoap has done the validation checks at the TLS level, - * but the application needs to do some additional checks/changes/updates. - * - * @param tls_session The security session definition - e.g. SSL * for OpenSSL. - * NULL if server call-back. - * This will be dependent on the underlying TLS library - - * see coap_get_tls_library_version() - * @param setup_data A structure containing setup data originally passed into - * coap_context_set_pki() or coap_new_client_session_pki(). - * - * @return @c 1 if successful, else @c 0. - */ -typedef int (*coap_dtls_security_setup_t)(void* tls_session, - struct coap_dtls_pki_t *setup_data); - -/** - * CN Validation call-back that can be set up by coap_context_set_pki(). - * Invoked when libcoap has done the validation checks at the TLS level, - * but the application needs to check that the CN is allowed. - * CN is the SubjectAltName in the cert, if not present, then the leftmost - * Common Name (CN) component of the subject name. - * - * @param cn The determined CN from the certificate - * @param asn1_public_cert The ASN.1 DER encoded X.509 certificate - * @param asn1_length The ASN.1 length - * @param coap_session The CoAP session associated with the certificate update - * @param depth Depth in cert chain. If 0, then client cert, else a CA - * @param validated TLS layer can find no issues if 1 - * @param arg The same as was passed into coap_context_set_pki() - * in setup_data->cn_call_back_arg - * - * @return @c 1 if accepted, else @c 0 if to be rejected. - */ -typedef int (*coap_dtls_cn_callback_t)(const char *cn, - const uint8_t *asn1_public_cert, - size_t asn1_length, - struct coap_session_t *coap_session, - unsigned depth, - int validated, - void *arg); - -/** - * The enum used for determining the provided PKI ASN.1 (DER) Private Key - * formats. - */ -typedef enum coap_asn1_privatekey_type_t { - COAP_ASN1_PKEY_NONE, /**< NONE */ - COAP_ASN1_PKEY_RSA, /**< RSA type */ - COAP_ASN1_PKEY_RSA2, /**< RSA2 type */ - COAP_ASN1_PKEY_DSA, /**< DSA type */ - COAP_ASN1_PKEY_DSA1, /**< DSA1 type */ - COAP_ASN1_PKEY_DSA2, /**< DSA2 type */ - COAP_ASN1_PKEY_DSA3, /**< DSA3 type */ - COAP_ASN1_PKEY_DSA4, /**< DSA4 type */ - COAP_ASN1_PKEY_DH, /**< DH type */ - COAP_ASN1_PKEY_DHX, /**< DHX type */ - COAP_ASN1_PKEY_EC, /**< EC type */ - COAP_ASN1_PKEY_HMAC, /**< HMAC type */ - COAP_ASN1_PKEY_CMAC, /**< CMAC type */ - COAP_ASN1_PKEY_TLS1_PRF, /**< TLS1_PRF type */ - COAP_ASN1_PKEY_HKDF /**< HKDF type */ -} coap_asn1_privatekey_type_t; - -/** - * The enum used for determining the PKI key formats. - */ -typedef enum coap_pki_key_t { - COAP_PKI_KEY_PEM = 0, /**< The PKI key type is PEM file */ - COAP_PKI_KEY_ASN1, /**< The PKI key type is ASN.1 (DER) */ - COAP_PKI_KEY_PEM_BUF, /**< The PKI key type is PEM buffer */ -} coap_pki_key_t; - -/** - * The structure that holds the PKI PEM definitions. - */ -typedef struct coap_pki_key_pem_t { - const char *ca_file; /**< File location of Common CA in PEM format */ - const char *public_cert; /**< File location of Public Cert in PEM format */ - const char *private_key; /**< File location of Private Key in PEM format */ -} coap_pki_key_pem_t; - -/** - * The structure that holds the PKI PEM buffer definitions. - */ -typedef struct coap_pki_key_pem_buf_t { - const uint8_t *ca_cert; /**< PEM buffer Common CA Cert */ - const uint8_t *public_cert; /**< PEM buffer Public Cert */ - const uint8_t *private_key; /**< PEM buffer Private Key */ - size_t ca_cert_len; /**< PEM buffer CA Cert length */ - size_t public_cert_len; /**< PEM buffer Public Cert length */ - size_t private_key_len; /**< PEM buffer Private Key length */ -} coap_pki_key_pem_buf_t; - -/** - * The structure that holds the PKI ASN.1 (DER) definitions. - */ -typedef struct coap_pki_key_asn1_t { - const uint8_t *ca_cert; /**< ASN1 (DER) Common CA Cert */ - const uint8_t *public_cert; /**< ASN1 (DER) Public Cert */ - const uint8_t *private_key; /**< ASN1 (DER) Private Key */ - size_t ca_cert_len; /**< ASN1 CA Cert length */ - size_t public_cert_len; /**< ASN1 Public Cert length */ - size_t private_key_len; /**< ASN1 Private Key length */ - coap_asn1_privatekey_type_t private_key_type; /**< Private Key Type */ -} coap_pki_key_asn1_t; - -/** - * The structure that holds the PKI key information. - */ -typedef struct coap_dtls_key_t { - coap_pki_key_t key_type; /**< key format type */ - union { - coap_pki_key_pem_t pem; /**< for PEM file keys */ - coap_pki_key_pem_buf_t pem_buf; /**< for PEM memory keys */ - coap_pki_key_asn1_t asn1; /**< for ASN.1 (DER) file keys */ - } key; -} coap_dtls_key_t; - -/** - * Server Name Indication (SNI) Validation call-back that can be set up by - * coap_context_set_pki(). - * Invoked if the SNI is not previously seen and prior to sending a certificate - * set back to the client so that the appropriate certificate set can be used - * based on the requesting SNI. - * - * @param sni The requested SNI - * @param arg The same as was passed into coap_context_set_pki() - * in setup_data->sni_call_back_arg - * - * @return New set of certificates to use, or @c NULL if SNI is to be rejected. - */ -typedef coap_dtls_key_t *(*coap_dtls_sni_callback_t)(const char *sni, - void* arg); - - -#define COAP_DTLS_PKI_SETUP_VERSION 1 /**< Latest PKI setup version */ - -/** - * The structure used for defining the PKI setup data to be used. - */ -typedef struct coap_dtls_pki_t { - uint8_t version; /** Set to 1 to support this version of the struct */ - - /* Options to enable different TLS functionality in libcoap */ - uint8_t verify_peer_cert; /**< 1 if peer cert is to be verified */ - uint8_t require_peer_cert; /**< 1 if peer cert is required */ - uint8_t allow_self_signed; /**< 1 if self signed certs are allowed */ - uint8_t allow_expired_certs; /**< 1 if expired certs are allowed */ - uint8_t cert_chain_validation; /**< 1 if to check cert_chain_verify_depth */ - uint8_t cert_chain_verify_depth; /**< recommended depth is 3 */ - uint8_t check_cert_revocation; /**< 1 if revocation checks wanted */ - uint8_t allow_no_crl; /**< 1 ignore if CRL not there */ - uint8_t allow_expired_crl; /**< 1 if expired crl is allowed */ - uint8_t allow_bad_md_hash; /**< 1 if expired certs are allowed */ - uint8_t allow_short_rsa_length; /**< 1 if expired certs are allowed */ - uint8_t reserved[4]; /**< Reserved - must be set to 0 for - future compatibility */ - /* Size of 4 chosen to align to next - * parameter, so if newly defined option - * it can use one of the reserverd slot so - * no need to change - * COAP_DTLS_PKI_SETUP_VERSION and just - * decrement the reserved[] count. - */ - - /** CN check call-back function. - * If not NULL, is called when the TLS connection has passed the configured - * TLS options above for the application to verify if the CN is valid. - */ - coap_dtls_cn_callback_t validate_cn_call_back; - void *cn_call_back_arg; /**< Passed in to the CN call-back function */ - - /** SNI check call-back function. - * If not @p NULL, called if the SNI is not previously seen and prior to - * sending a certificate set back to the client so that the appropriate - * certificate set can be used based on the requesting SNI. - */ - coap_dtls_sni_callback_t validate_sni_call_back; - void *sni_call_back_arg; /**< Passed in to the sni call-back function */ - - /** Additional Security call-back handler that is invoked when libcoap has - * done the standerd, defined validation checks at the TLS level, - * If not @p NULL, called from within the TLS Client Hello connection - * setup. - */ - coap_dtls_security_setup_t additional_tls_setup_call_back; - - char* client_sni; /**< If not NULL, SNI to use in client TLS setup. - Owned by the client app and must remain valid - during the call to coap_new_client_session_pki() */ - - coap_dtls_key_t pki_key; /**< PKI key definition */ -} coap_dtls_pki_t; - -/** @} */ - -/** - * @defgroup dtls_internal DTLS Support (Internal) - * Internal API functions for interfacing with DTLS libraries. - * @{ - */ - -/** - * Creates a new DTLS context for the given @p coap_context. This function - * returns a pointer to a new DTLS context object or @c NULL on error. - * - * Internal function. - * - * @param coap_context The CoAP context where the DTLS object shall be used. - * - * @return A DTLS context object or @c NULL on error. - */ -void * -coap_dtls_new_context(struct coap_context_t *coap_context); - -typedef enum coap_dtls_role_t { - COAP_DTLS_ROLE_CLIENT, /**< Internal function invoked for client */ - COAP_DTLS_ROLE_SERVER /**< Internal function invoked for server */ -} coap_dtls_role_t; - -/** - * Set the DTLS context's default PSK information. - * This does the PSK specifics following coap_dtls_new_context(). - * If @p COAP_DTLS_ROLE_SERVER, then identity hint will also get set. - * If @p COAP_DTLS_ROLE_SERVER, then the information will get put into the - * TLS library's context (from which sessions are derived). - * If @p COAP_DTLS_ROLE_CLIENT, then the information will get put into the - * TLS library's session. - * - * Internal function. - * - * @param coap_context The CoAP context. - * @param identity_hint The default PSK server identity hint sent to a client. - * Required parameter. If @p NULL, will be set to "". - * Empty string is a valid hint. - * This parameter is ignored if COAP_DTLS_ROLE_CLIENT - * @param role One of @p COAP_DTLS_ROLE_CLIENT or @p COAP_DTLS_ROLE_SERVER - * - * @return @c 1 if successful, else @c 0. - */ - -int -coap_dtls_context_set_psk(struct coap_context_t *coap_context, - const char *identity_hint, - coap_dtls_role_t role); - -/** - * Set the DTLS context's default server PKI information. - * This does the PKI specifics following coap_dtls_new_context(). - * If @p COAP_DTLS_ROLE_SERVER, then the information will get put into the - * TLS library's context (from which sessions are derived). - * If @p COAP_DTLS_ROLE_CLIENT, then the information will get put into the - * TLS library's session. - * - * Internal function. - * - * @param coap_context The CoAP context. - * @param setup_data Setup information defining how PKI is to be setup. - * Required parameter. If @p NULL, PKI will not be - * set up. - * @param role One of @p COAP_DTLS_ROLE_CLIENT or @p COAP_DTLS_ROLE_SERVER - * - * @return @c 1 if successful, else @c 0. - */ - -int -coap_dtls_context_set_pki(struct coap_context_t *coap_context, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role); - -/** - * Set the dtls context's default Root CA information for a client or server. - * - * Internal function. - * - * @param coap_context The current coap_context_t object. - * @param ca_file If not @p NULL, is the full path name of a PEM encoded - * file containing all the Root CAs to be used. - * @param ca_dir If not @p NULL, points to a directory containing PEM - * encoded files containing all the Root CAs to be used. - * - * @return @c 1 if successful, else @c 0. - */ - -int -coap_dtls_context_set_pki_root_cas(struct coap_context_t *coap_context, - const char *ca_file, - const char *ca_dir); - -/** - * Check whether one of the coap_dtls_context_set_{psk|pki}() functions have - * been called. - * - * Internal function. - * - * @param coap_context The current coap_context_t object. - * - * @return @c 1 if coap_dtls_context_set_{psk|pki}() called, else @c 0. - */ - -int coap_dtls_context_check_keys_enabled(struct coap_context_t *coap_context); - -/** - * Releases the storage allocated for @p dtls_context. - * - * Internal function. - * - * @param dtls_context The DTLS context as returned by coap_dtls_new_context(). - */ -void coap_dtls_free_context(void *dtls_context); - -/** - * Create a new client-side session. This should send a HELLO to the server. - * - * Internal function. - * - * @param coap_session The CoAP session. - * - * @return Opaque handle to underlying TLS library object containing security - * parameters for the session. -*/ -void *coap_dtls_new_client_session(struct coap_session_t *coap_session); - -/** - * Create a new DTLS server-side session. - * Called after coap_dtls_hello() has returned @c 1, signalling that a validated - * HELLO was received from a client. - * This should send a HELLO to the server. - * - * Internal function. - * - * @param coap_session The CoAP session. - * - * @return Opaque handle to underlying TLS library object containing security - * parameters for the DTLS session. - */ -void *coap_dtls_new_server_session(struct coap_session_t *coap_session); - -/** - * Terminates the DTLS session (may send an ALERT if necessary) then frees the - * underlying TLS library object containing security parameters for the session. - * - * Internal function. - * - * @param coap_session The CoAP session. - */ -void coap_dtls_free_session(struct coap_session_t *coap_session); - -/** - * Notify of a change in the CoAP session's MTU, for example after - * a PMTU update. - * - * Internal function. - * - * @param coap_session The CoAP session. - */ -void coap_dtls_session_update_mtu(struct coap_session_t *coap_session); - -/** - * Send data to a DTLS peer. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param data pointer to data. - * @param data_len Number of bytes to send. - * - * @return @c 0 if this would be blocking, @c -1 if there is an error or the - * number of cleartext bytes sent. - */ -int coap_dtls_send(struct coap_session_t *coap_session, - const uint8_t *data, - size_t data_len); - -/** - * Check if timeout is handled per CoAP session or per CoAP context. - * - * Internal function. - * - * @return @c 1 of timeout and retransmit is per context, @c 0 if it is - * per session. - */ -int coap_dtls_is_context_timeout(void); - -/** - * Do all pending retransmits and get next timeout - * - * Internal function. - * - * @param dtls_context The DTLS context. - * - * @return @c 0 if no event is pending or date of the next retransmit. - */ -coap_tick_t coap_dtls_get_context_timeout(void *dtls_context); - -/** - * Get next timeout for this session. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param now The current time in ticks. - * - * @return @c 0 If no event is pending or ticks time of the next retransmit. - */ -coap_tick_t coap_dtls_get_timeout(struct coap_session_t *coap_session, - coap_tick_t now); - -/** - * Handle a DTLS timeout expiration. - * - * Internal function. - * - * @param coap_session The CoAP session. - */ -void coap_dtls_handle_timeout(struct coap_session_t *coap_session); - -/** - * Handling incoming data from a DTLS peer. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param data Encrypted datagram. - * @param data_len Encrypted datagram size. - * - * @return Result of coap_handle_dgram on the decrypted CoAP PDU - * or @c -1 for error. - */ -int coap_dtls_receive(struct coap_session_t *coap_session, - const uint8_t *data, - size_t data_len); - -/** - * Handling client HELLO messages from a new candiate peer. - * Note that session->tls is empty. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param data Encrypted datagram. - * @param data_len Encrypted datagram size. - * - * @return @c 0 if a cookie verification message has been sent, @c 1 if the - * HELLO contains a valid cookie and a server session should be created, - * @c -1 if the message is invalid. - */ -int coap_dtls_hello(struct coap_session_t *coap_session, - const uint8_t *data, - size_t data_len); - -/** - * Get DTLS overhead over cleartext PDUs. - * - * Internal function. - * - * @param coap_session The CoAP session. - * - * @return Maximum number of bytes added by DTLS layer. - */ -unsigned int coap_dtls_get_overhead(struct coap_session_t *coap_session); - -/** - * Create a new TLS client-side session. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param connected Updated with whether the connection is connected yet or not. - * @c 0 is not connected, @c 1 is connected. - * - * @return Opaque handle to underlying TLS library object containing security - * parameters for the session. -*/ -void *coap_tls_new_client_session(struct coap_session_t *coap_session, int *connected); - -/** - * Create a TLS new server-side session. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param connected Updated with whether the connection is connected yet or not. - * @c 0 is not connected, @c 1 is connected. - * - * @return Opaque handle to underlying TLS library object containing security - * parameters for the session. - */ -void *coap_tls_new_server_session(struct coap_session_t *coap_session, int *connected); - -/** - * Terminates the TLS session (may send an ALERT if necessary) then frees the - * underlying TLS library object containing security parameters for the session. - * - * Internal function. - * - * @param coap_session The CoAP session. - */ -void coap_tls_free_session( struct coap_session_t *coap_session ); - -/** - * Send data to a TLS peer, with implicit flush. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param data Pointer to data. - * @param data_len Number of bytes to send. - * - * @return @c 0 if this should be retried, @c -1 if there is an error - * or the number of cleartext bytes sent. - */ -ssize_t coap_tls_write(struct coap_session_t *coap_session, - const uint8_t *data, - size_t data_len - ); - -/** - * Read some data from a TLS peer. - * - * Internal function. - * - * @param coap_session The CoAP session. - * @param data Pointer to data. - * @param data_len Maximum number of bytes to read. - * - * @return @c 0 if this should be retried, @c -1 if there is an error - * or the number of cleartext bytes read. - */ -ssize_t coap_tls_read(struct coap_session_t *coap_session, - uint8_t *data, - size_t data_len - ); - -/** - * Initialize the underlying (D)TLS Library layer. - * - * Internal function. - * - */ -void coap_dtls_startup(void); - -/** @} */ - -/** - * @ingroup logging - * Sets the (D)TLS logging level to the specified @p level. - * Note: coap_log_level() will influence output if at a specified level. - * - * @param level The logging level to use - LOG_* - */ -void coap_dtls_set_log_level(int level); - -/** - * @ingroup logging - * Get the current (D)TLS logging. - * - * @return The current log level (one of LOG_*). - */ -int coap_dtls_get_log_level(void); - - -#endif /* COAP_DTLS_H */ diff --git a/components/coap/port/include/coap/coap.h b/components/coap/port/include/coap3/coap.h similarity index 53% rename from components/coap/port/include/coap/coap.h rename to components/coap/port/include/coap3/coap.h index f048ca8571..c69d2734b3 100644 --- a/components/coap/port/include/coap/coap.h +++ b/components/coap/port/include/coap3/coap.h @@ -21,27 +21,28 @@ extern "C" { #endif -#include "libcoap.h" +#include "coap3/libcoap.h" -#include "address.h" -#include "async.h" -#include "bits.h" -#include "block.h" -#include "coap_dtls.h" -#include "coap_event.h" -#include "coap_io.h" -#include "coap_time.h" -#include "coap_debug.h" -#include "encode.h" -#include "mem.h" -#include "net.h" -#include "option.h" -#include "pdu.h" -#include "prng.h" -#include "resource.h" -#include "str.h" -#include "subscribe.h" -#include "uri.h" +#include "coap3/coap_forward_decls.h" +#include "coap3/address.h" +#include "coap3/async.h" +#include "coap3/block.h" +#include "coap3/coap_cache.h" +#include "coap3/coap_dtls.h" +#include "coap3/coap_event.h" +#include "coap3/coap_io.h" +#include "coap3/coap_time.h" +#include "coap3/coap_debug.h" +#include "coap3/encode.h" +#include "coap3/mem.h" +#include "coap3/net.h" +#include "coap3/option.h" +#include "coap3/pdu.h" +#include "coap3/coap_prng.h" +#include "coap3/resource.h" +#include "coap3/str.h" +#include "coap3/subscribe.h" +#include "coap3/uri.h" #ifdef __cplusplus } diff --git a/components/coap/port/include/coap_config_posix.h b/components/coap/port/include/coap_config_posix.h index 3f6b7c605f..b24335ad15 100644 --- a/components/coap/port/include/coap_config_posix.h +++ b/components/coap/port/include/coap_config_posix.h @@ -30,6 +30,7 @@ #define HAVE_NETDB_H #define HAVE_NETINET_IN_H #define HAVE_STRUCT_CMSGHDR +#define COAP_DISABLE_TCP 0 #define ipi_spec_dst ipi_addr struct in6_pktinfo { @@ -53,8 +54,6 @@ struct in6_pktinfo { #define COAP_CONSTRAINED_STACK 1 #define ESPIDF_VERSION -#define _POSIX_TIMERS 1 - #define gai_strerror(x) "gai_strerror() not supported" #endif /* WITH_POSIX */ diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 6fddca7f61..cde2b05cd2 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -6,3 +6,7 @@ idf_component_register(SRCS "commands.c" INCLUDE_DIRS "." REQUIRES vfs PRIV_REQUIRES driver) + +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + set_source_files_properties(argtable3/argtable3.c PROPERTIES COMPILE_FLAGS -Wno-clobbered) +endif() diff --git a/components/console/argtable3/argtable3.c b/components/console/argtable3/argtable3.c index ca9149277c..0c3cb729fd 100644 --- a/components/console/argtable3/argtable3.c +++ b/components/console/argtable3/argtable3.c @@ -30,8 +30,6 @@ #include "argtable3.h" -#pragma GCC diagnostic ignored "-Wclobbered" - /******************************************************************************* * This file is part of the argtable3 library. * diff --git a/components/console/component.mk b/components/console/component.mk index 0a6d4945e3..703e86d80d 100644 --- a/components/console/component.mk +++ b/components/console/component.mk @@ -1,2 +1,4 @@ COMPONENT_ADD_INCLUDEDIRS := . COMPONENT_SRCDIRS := linenoise argtable3 . + +argtable3/argtable3.o: CFLAGS += -Wno-clobbered diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 28b1efee72..093e321823 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -67,6 +67,7 @@ if(IDF_TARGET STREQUAL "esp32c3") list(APPEND srcs "gdma.c" "spi_slave_hd.c" "adc_common.c" + "dedic_gpio.c" "usb_serial_jtag.c" "esp32c3/adc.c" "esp32c3/adc2_init_cal.c" @@ -77,6 +78,7 @@ if(IDF_TARGET STREQUAL "esp32h2") list(APPEND srcs "gdma.c" "spi_slave_hd.c" "adc_common.c" + "dedic_gpio.c" "esp32h2/adc.c" "esp32h2/adc2_init_cal.c" "esp32h2/rtc_tempsensor.c") diff --git a/components/driver/Kconfig b/components/driver/Kconfig index aedad08772..6ca95be2ca 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -21,6 +21,20 @@ menu "Driver configurations" endmenu # ADC Configuration + menu "MCPWM configuration" + + config MCPWM_ISR_IN_IRAM + bool "Place MCPWM ISR function into IRAM" + default n + help + If this option is not selected, the MCPWM interrupt will be deferred when the Cache + is in a disabled state (e.g. Flash write/erase operation). + + Note that if this option is selected, all user registered ISR callbacks should never + try to use cache as well. (with IRAM_ATTR) + + endmenu # MCPWM Configuration + menu "SPI configuration" config SPI_MASTER_IN_IRAM diff --git a/components/driver/adc_common.c b/components/driver/adc_common.c index a231b8134a..8cc8e434df 100644 --- a/components/driver/adc_common.c +++ b/components/driver/adc_common.c @@ -18,16 +18,18 @@ #include "driver/gpio.h" #include "driver/adc.h" #include "adc1_private.h" - #include "hal/adc_types.h" #include "hal/adc_hal.h" +#include "hal/adc_hal_conf.h" #if SOC_DAC_SUPPORTED #include "driver/dac.h" #include "hal/dac_hal.h" #endif -#include "hal/adc_hal_conf.h" +#if CONFIG_IDF_TARGET_ESP32S3 +#include "esp_efuse_rtc_calib.h" +#endif #define ADC_CHECK_RET(fun_ret) ({ \ if (fun_ret != ESP_OK) { \ @@ -123,20 +125,10 @@ static esp_pm_lock_handle_t s_adc2_arbiter_lock; #endif //CONFIG_PM_ENABLE #endif // !CONFIG_IDF_TARGET_ESP32 + /*--------------------------------------------------------------- ADC Common ---------------------------------------------------------------*/ - -#if CONFIG_IDF_TARGET_ESP32S2 -static uint32_t get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t chan) -{ - adc_atten_t atten = adc_hal_get_atten(adc_n, chan); - - extern uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten, bool no_cal); - return adc_get_calibration_offset(adc_n, chan, atten, false); -} -#endif - // ADC Power // This gets incremented when adc_power_acquire() is called, and decremented when @@ -226,7 +218,61 @@ esp_err_t adc2_pad_get_io_num(adc2_channel_t channel, gpio_num_t *gpio_num) return ESP_OK; } -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + +//------------------------------------------------------------RTC Single Read----------------------------------------------// +#if SOC_ADC_RTC_CTRL_SUPPORTED + +/*--------------------------------------------------------------- + ADC Calibration +---------------------------------------------------------------*/ +#if CONFIG_IDF_TARGET_ESP32S3 +/** + * Temporarily put this function here. These are about ADC calibration and will be moved driver/adc.c in !14278. + * Reason for putting calibration functions in driver/adc.c: + * adc_common.c is far too confusing. Before a refactor is applied to adc_common.c, will put definite code in driver/adc.c + */ + +static uint16_t s_adc_cali_param[ADC_UNIT_MAX][ADC_ATTEN_MAX] = {}; + +uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten) +{ + if (s_adc_cali_param[adc_n][atten]) { + //These value won't exceed UINT16_MAX + return s_adc_cali_param[adc_n][atten]; + } + + //Get the calibration version + int version = esp_efuse_rtc_calib_get_ver(); + + uint32_t init_code = 0; + if (version == 1) { + init_code = esp_efuse_rtc_calib_get_init_code(version, adc_n, atten); + } else { + ESP_LOGV(ADC_TAG, "Calibration eFuse is not configured, use self-calibration for ICode"); + adc_power_acquire(); + RTC_ENTER_CRITICAL(); + const bool internal_gnd = true; + init_code = adc_hal_self_calibration(adc_n, channel, atten, internal_gnd); + RTC_EXIT_CRITICAL(); + adc_power_release(); + } + s_adc_cali_param[adc_n][atten] = init_code; + return s_adc_cali_param[adc_n][atten]; +} +#elif CONFIG_IDF_TARGET_ESP32S2 +//Temporarily extern this from s2/adc.c. Will be modified in !14278. +extern uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten); +#endif //CONFIG_IDF_TARGET_ESP32S3 + +#if SOC_ADC_CALIBRATION_V1_SUPPORTED +static uint32_t get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t chan) +{ + adc_atten_t atten = adc_hal_get_atten(adc_n, chan); + + return adc_get_calibration_offset(adc_n, chan, atten); +} +#endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED + esp_err_t adc_set_clk_div(uint8_t clk_div) { DIGI_CONTROLLER_ENTER(); @@ -349,7 +395,7 @@ esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten) adc_hal_set_atten(ADC_NUM_1, channel, atten); SARADC1_EXIT(); -#if SOC_ADC_HW_CALIBRATION_V1 +#if SOC_ADC_CALIBRATION_V1_SUPPORTED adc_hal_calibration_init(ADC_NUM_1); #endif @@ -427,11 +473,11 @@ int adc1_get_raw(adc1_channel_t channel) ADC_CHANNEL_CHECK(ADC_NUM_1, channel); adc1_rtc_mode_acquire(); -#if CONFIG_IDF_TARGET_ESP32S2 +#if SOC_ADC_CALIBRATION_V1_SUPPORTED // Get calibration value before going into critical section uint32_t cal_val = get_calibration_offset(ADC_NUM_1, channel); adc_hal_set_calibration_param(ADC_NUM_1, cal_val); -#endif +#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED SARADC1_ENTER(); #ifdef CONFIG_IDF_TARGET_ESP32 @@ -521,7 +567,7 @@ esp_err_t adc2_config_channel_atten(adc2_channel_t channel, adc_atten_t atten) SARADC2_RELEASE(); -#if SOC_ADC_HW_CALIBRATION_V1 +#if SOC_ADC_CALIBRATION_V1_SUPPORTED adc_hal_calibration_init(ADC_NUM_2); #endif @@ -578,11 +624,11 @@ esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width_bit, int * ADC_CHECK(width_bit == ADC_WIDTH_MAX - 1, "WIDTH ERR: see `adc_bits_width_t` for supported bit width", ESP_ERR_INVALID_ARG); #endif -#if CONFIG_IDF_TARGET_ESP32S2 +#if SOC_ADC_CALIBRATION_V1_SUPPORTED // Get calibration value before going into critical section uint32_t cal_val = get_calibration_offset(ADC_NUM_2, channel); adc_hal_set_calibration_param(ADC_NUM_2, cal_val); -#endif +#endif //SOC_ADC_CALIBRATION_V1_SUPPORTED if ( SARADC2_TRY_ACQUIRE() == -1 ) { //try the lock, return if failed (wifi using). @@ -679,4 +725,4 @@ esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio) return ESP_OK; } -#endif //CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#endif //SOC_ADC_RTC_CTRL_SUPPORTED diff --git a/components/driver/dac_common.c b/components/driver/dac_common.c index 79172eb984..791926b0fc 100644 --- a/components/driver/dac_common.c +++ b/components/driver/dac_common.c @@ -8,6 +8,7 @@ #include #include "esp_log.h" #include "esp_err.h" +#include "esp_check.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" @@ -18,21 +19,14 @@ extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished. -static const char *DAC_TAG = "DAC"; - -#define DAC_CHECK(a, str, ret_val) ({ \ - if (!(a)) { \ - ESP_LOGE(DAC_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret_val); \ - } \ -}) +static const char *TAG = "DAC"; /*--------------------------------------------------------------- DAC ---------------------------------------------------------------*/ esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); *gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[channel]; @@ -41,7 +35,7 @@ esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num) static esp_err_t dac_rtc_pad_init(dac_channel_t channel) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); gpio_num_t gpio_num = 0; dac_pad_get_io_num(channel, &gpio_num); @@ -55,7 +49,7 @@ static esp_err_t dac_rtc_pad_init(dac_channel_t channel) esp_err_t dac_output_enable(dac_channel_t channel) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); dac_rtc_pad_init(channel); portENTER_CRITICAL(&rtc_spinlock); @@ -68,7 +62,7 @@ esp_err_t dac_output_enable(dac_channel_t channel) esp_err_t dac_output_disable(dac_channel_t channel) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); portENTER_CRITICAL(&rtc_spinlock); dac_hal_power_down(channel); @@ -79,7 +73,7 @@ esp_err_t dac_output_disable(dac_channel_t channel) esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); portENTER_CRITICAL(&rtc_spinlock); dac_hal_update_output_value(channel, dac_value); @@ -90,7 +84,7 @@ esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value) esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value) { - DAC_CHECK(channel < DAC_CHANNEL_MAX, "DAC channel error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error"); portENTER_CRITICAL(&rtc_spinlock); dac_hal_update_output_value(channel, dac_value); @@ -119,7 +113,7 @@ esp_err_t dac_cw_generator_disable(void) esp_err_t dac_cw_generator_config(dac_cw_config_t *cw) { - if (!cw) return ESP_ERR_INVALID_ARG; + ESP_RETURN_ON_FALSE(cw, ESP_ERR_INVALID_ARG, TAG, "invalid clock configuration"); portENTER_CRITICAL(&rtc_spinlock); dac_hal_cw_generator_config(cw); diff --git a/components/driver/dedic_gpio.c b/components/driver/dedic_gpio.c index 98109f51bd..46daf61ae6 100644 --- a/components/driver/dedic_gpio.c +++ b/components/driver/dedic_gpio.c @@ -63,8 +63,8 @@ struct dedic_gpio_bundle_t { uint32_t in_mask; // mask of input channels in the bank uint32_t out_offset; // offset in the bank (seen from output channel) uint32_t in_offset; // offset in the bank (seen from input channel) - size_t nr_gpio; // number of GPIOs in the gpio_array - int gpio_array[]; // array of GPIO numbers (configured by user) + size_t nr_gpio; // number of GPIOs in the gpio_array + int gpio_array[]; // array of GPIO numbers (configured by user) }; static esp_err_t dedic_gpio_build_platform(uint32_t core_id) @@ -80,8 +80,10 @@ static esp_err_t dedic_gpio_build_platform(uint32_t core_id) s_platform[core_id]->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; #if SOC_DEDIC_GPIO_ALLOW_REG_ACCESS s_platform[core_id]->dev = &DEDIC_GPIO; -#endif +#endif // SOC_DEDIC_GPIO_ALLOW_REG_ACCESS +#if !SOC_DEDIC_PERIPH_AUTO_ENABLE periph_module_enable(dedic_gpio_periph_signals.module); // enable APB clock to peripheral +#endif // !SOC_DEDIC_PERIPH_AUTO_ENABLE } } _lock_release(&s_platform_mutexlock[core_id]); @@ -102,7 +104,9 @@ static void dedic_gpio_break_platform(uint32_t core_id) if (s_platform[core_id]) { free(s_platform[core_id]); s_platform[core_id] = NULL; +#if !SOC_DEDIC_PERIPH_AUTO_ENABLE periph_module_disable(dedic_gpio_periph_signals.module); // disable module if no GPIO channel is being used +#endif // !SOC_DEDIC_PERIPH_AUTO_ENABLE } _lock_release(&s_platform_mutexlock[core_id]); } @@ -263,6 +267,9 @@ esp_err_t dedic_gpio_new_bundle(const dedic_gpio_bundle_config_t *config, dedic_ gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_array[i]], PIN_FUNC_GPIO); esp_rom_gpio_connect_out_signal(config->gpio_array[i], dedic_gpio_periph_signals.cores[core_id].out_sig_per_channel[out_offset + i], config->flags.out_invert, false); } +#if !SOC_DEDIC_GPIO_OUT_AUTO_ENABLE + cpu_ll_enable_dedic_gpio_output(s_platform[core_id]->out_occupied_mask); +#endif // !SOC_DEDIC_GPIO_OUT_AUTO_ENABLE } // it's safe to initialize bundle members without locks here @@ -340,14 +347,14 @@ err: void dedic_gpio_bundle_write(dedic_gpio_bundle_handle_t bundle, uint32_t mask, uint32_t value) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) cpu_ll_write_dedic_gpio_mask(bundle->out_mask & (mask << bundle->out_offset), value << bundle->out_offset); } uint32_t dedic_gpio_bundle_read_out(dedic_gpio_bundle_handle_t bundle) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) uint32_t value = cpu_ll_read_dedic_gpio_out(); return (value & bundle->out_mask) >> (bundle->out_offset); @@ -355,7 +362,7 @@ uint32_t dedic_gpio_bundle_read_out(dedic_gpio_bundle_handle_t bundle) uint32_t dedic_gpio_bundle_read_in(dedic_gpio_bundle_handle_t bundle) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) uint32_t value = cpu_ll_read_dedic_gpio_in(); return (value & bundle->in_mask) >> (bundle->in_offset); diff --git a/components/driver/esp32c3/adc.c b/components/driver/esp32c3/adc.c index 91dff0b5c5..585a139550 100644 --- a/components/driver/esp32c3/adc.c +++ b/components/driver/esp32c3/adc.c @@ -448,11 +448,11 @@ esp_err_t adc_vref_to_gpio(adc_unit_t adc_unit, gpio_num_t gpio) if (adc_unit & ADC_UNIT_1) { ADC_ENTER_CRITICAL(); adc_hal_vref_output(ADC_NUM_1, channel, true); - ADC_EXIT_CRITICAL() + ADC_EXIT_CRITICAL(); } else if (adc_unit & ADC_UNIT_2) { ADC_ENTER_CRITICAL(); adc_hal_vref_output(ADC_NUM_2, channel, true); - ADC_EXIT_CRITICAL() + ADC_EXIT_CRITICAL(); } ret = adc_digi_gpio_init(ADC_NUM_2, BIT(channel)); diff --git a/components/driver/esp32s2/adc.c b/components/driver/esp32s2/adc.c index 7609eb5869..a68fd46a32 100644 --- a/components/driver/esp32s2/adc.c +++ b/components/driver/esp32s2/adc.c @@ -437,7 +437,7 @@ static uint16_t s_adc_cali_param[ADC_NUM_MAX][ADC_ATTEN_MAX] = { {0}, {0} }; // 1. Semaphore when reading efuse // 2. Spinlock when actually doing ADC calibration //This function shoudn't be called inside critical section or ISR -uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten, bool no_cal) +uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten) { #ifdef CONFIG_IDF_ENV_FPGA return 0; @@ -448,10 +448,6 @@ uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, a return (uint32_t)s_adc_cali_param[adc_n][atten]; } - if (no_cal) { - return 0; //indicating failure - } - uint32_t dout = 0; // check if we can fetch the values from eFuse. int version = esp_efuse_rtc_table_read_calib_version(); @@ -474,7 +470,7 @@ uint32_t adc_get_calibration_offset(adc_ll_num_t adc_n, adc_channel_t channel, a esp_err_t adc_cal_offset(adc_ll_num_t adc_n, adc_channel_t channel, adc_atten_t atten) { adc_hal_calibration_init(adc_n); - uint32_t cal_val = adc_get_calibration_offset(adc_n, channel, atten, false); + uint32_t cal_val = adc_get_calibration_offset(adc_n, channel, atten); ADC_ENTER_CRITICAL(); adc_hal_set_calibration_param(adc_n, cal_val); ADC_EXIT_CRITICAL(); diff --git a/components/driver/esp32s2/dac.c b/components/driver/esp32s2/dac.c index 7a1d0d40c5..16f8742a90 100644 --- a/components/driver/esp32s2/dac.c +++ b/components/driver/esp32s2/dac.c @@ -8,6 +8,7 @@ #include #include "esp_log.h" #include "esp_err.h" +#include "esp_check.h" #include "esp_pm.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -17,14 +18,7 @@ #include "soc/dac_periph.h" #include "hal/dac_hal.h" -static const char *DAC_TAG = "DAC"; - -#define DAC_CHECK(a, str, ret_val) ({ \ - if (!(a)) { \ - ESP_LOGE(DAC_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret_val); \ - } \ -}) +static const char *TAG = "DAC"; extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished. #define DAC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock) @@ -64,11 +58,11 @@ esp_err_t dac_digi_deinit(void) esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) { - DAC_CHECK(cfg->mode < DAC_CONV_MAX, "DAC mode error", ESP_ERR_INVALID_ARG); - DAC_CHECK(cfg->interval > 0 && cfg->interval < 4096, "DAC interval error", ESP_ERR_INVALID_ARG); - DAC_CHECK(cfg->dig_clk.div_num < 256, "DAC clk div_num error", ESP_ERR_INVALID_ARG); - DAC_CHECK(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, "DAC clk div_b error", ESP_ERR_INVALID_ARG); - DAC_CHECK(cfg->dig_clk.div_a < 64, "DAC clk div_a error", ESP_ERR_INVALID_ARG); + ESP_RETURN_ON_FALSE(cfg->mode < DAC_CONV_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC mode error"); + ESP_RETURN_ON_FALSE(cfg->interval > 0 && cfg->interval < 4096, ESP_ERR_INVALID_ARG, TAG, "DAC interval error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_num < 256, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_num error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_b error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_a < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_a error"); #ifdef CONFIG_PM_ENABLE esp_err_t err; if (s_dac_digi_lock == NULL) { @@ -79,7 +73,7 @@ esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) } if (err != ESP_OK) { s_dac_digi_lock = NULL; - ESP_LOGE(DAC_TAG, "DAC-DMA pm lock error"); + ESP_LOGE(TAG, "DAC-DMA pm lock error"); return err; } } @@ -95,7 +89,7 @@ esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) esp_err_t dac_digi_start(void) { #ifdef CONFIG_PM_ENABLE - DAC_CHECK((s_dac_digi_lock), "Should start after call `dac_digi_controller_config`", ESP_FAIL); + ESP_RETURN_ON_FALSE(s_dac_digi_lock, ESP_FAIL, TAG, "Should start after call `dac_digi_controller_config`"); esp_pm_lock_acquire(s_dac_digi_lock); #endif DAC_ENTER_CRITICAL(); diff --git a/components/driver/gpio.c b/components/driver/gpio.c index f749c77a34..db27ab45ab 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -18,15 +18,12 @@ #include "soc/soc_caps.h" #include "soc/gpio_periph.h" #include "esp_log.h" +#include "esp_check.h" #include "hal/gpio_hal.h" #include "esp_rom_gpio.h" static const char *GPIO_TAG = "gpio"; -#define GPIO_CHECK(a, str, ret_val) \ - if (!(a)) { \ - ESP_LOGE(GPIO_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ - return (ret_val); \ - } +#define GPIO_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE(a, ret_val, GPIO_TAG, "%s", str) #define GPIO_ISR_CORE_ID_UNINIT (3) diff --git a/components/driver/i2c.c b/components/driver/i2c.c index 1ac074a299..438d1efc12 100644 --- a/components/driver/i2c.c +++ b/components/driver/i2c.c @@ -539,6 +539,7 @@ esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, ESP_RETURN_ON_FALSE(rx_trans_mode < I2C_DATA_MODE_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_TRANS_MODE_ERR_STR); I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_data_mode(&(i2c_context[i2c_num].hal), tx_trans_mode, rx_trans_mode); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -690,13 +691,13 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf) i2c_hal_set_sda_timing(&(i2c_context[i2c_num].hal), I2C_SLAVE_SDA_SAMPLE_DEFAULT, I2C_SLAVE_SDA_HOLD_DEFAULT); i2c_hal_set_tout(&(i2c_context[i2c_num].hal), I2C_SLAVE_TIMEOUT_DEFAULT); i2c_hal_enable_slave_rx_it(&(i2c_context[i2c_num].hal)); - i2c_hal_update_config(&(i2c_context[i2c_num].hal)); } else { i2c_hal_master_init(&(i2c_context[i2c_num].hal), i2c_num); //Default, we enable hardware filter i2c_hal_set_filter(&(i2c_context[i2c_num].hal), I2C_FILTER_CYC_NUM_DEF); i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, src_clk); } + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -709,6 +710,7 @@ esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_scl_timing(&(i2c_context[i2c_num].hal), high_period, low_period); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -728,6 +730,7 @@ esp_err_t i2c_filter_enable(i2c_port_t i2c_num, uint8_t cyc_num) ESP_RETURN_ON_FALSE(p_i2c_obj[i2c_num] != NULL, ESP_FAIL, I2C_TAG, I2C_DRIVER_ERR_STR); I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_filter(&(i2c_context[i2c_num].hal), cyc_num); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -737,6 +740,7 @@ esp_err_t i2c_filter_disable(i2c_port_t i2c_num) ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_NUM_ERROR_STR); I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_filter(&(i2c_context[i2c_num].hal), 0); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -749,6 +753,7 @@ esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_start_timing(&(i2c_context[i2c_num].hal), setup_time, hold_time); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -770,6 +775,7 @@ esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_stop_timing(&(i2c_context[i2c_num].hal), setup_time, hold_time); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } @@ -791,6 +797,7 @@ esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time I2C_ENTER_CRITICAL(&(i2c_context[i2c_num].spinlock)); i2c_hal_set_sda_timing(&(i2c_context[i2c_num].hal), sample_time, hold_time); + i2c_hal_update_config(&(i2c_context[i2c_num].hal)); I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock)); return ESP_OK; } diff --git a/components/driver/i2s.c b/components/driver/i2s.c index f2085bc5ee..0eb50e4738 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -18,10 +18,13 @@ #include "driver/i2s.h" #include "hal/gpio_hal.h" #include "hal/i2s_hal.h" -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC #include "driver/dac.h" +#endif // SOC_I2S_SUPPORTS_DAC + +#if SOC_I2S_SUPPORTS_ADC #include "adc1_private.h" -#endif +#endif // SOC_I2S_SUPPORTS_ADC #if SOC_GDMA_SUPPORTED #include "esp_private/gdma.h" @@ -47,8 +50,8 @@ static const char *TAG = "I2S"; #define I2S_EXIT_CRITICAL_ISR(i2s_num) portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) #define I2S_ENTER_CRITICAL(i2s_num) portENTER_CRITICAL(&i2s_spinlock[i2s_num]) #define I2S_EXIT_CRITICAL(i2s_num) portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) -#define I2S_FULL_DUPLEX_SLAVE_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_SLAVE) -#define I2S_FULL_DUPLEX_MASTER_MODE_MASK (I2S_MODE_TX | I2S_MODE_RX | I2S_MODE_MASTER) + +#define I2S_DMA_BUFFER_MAX_SIZE 4092 #if !SOC_GDMA_SUPPORTED #define I2S_INTR_IN_SUC_EOF BIT(9) @@ -65,8 +68,8 @@ static const char *TAG = "I2S"; typedef struct { char **buf; int buf_size; - int rw_pos; - void *curr_ptr; + volatile int rw_pos; + volatile void *curr_ptr; SemaphoreHandle_t mux; xQueueHandle queue; lldesc_t **desc; @@ -82,6 +85,7 @@ typedef struct { QueueHandle_t i2s_queue; /*!< I2S queue handler*/ int dma_buf_count; /*!< DMA buffer count, number of buffer*/ int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + uint32_t last_buf_size; /*!< DMA last buffer size */ i2s_dma_t *tx; /*!< DMA Tx buffer*/ i2s_dma_t *rx; /*!< DMA Rx buffer*/ #if SOC_GDMA_SUPPORTED @@ -90,16 +94,10 @@ typedef struct { #else i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ #endif - int channel_num; /*!< Number of channels*/ - int bytes_per_sample; /*!< Bytes per sample*/ - int bits_per_sample; /*!< Bits per sample*/ - i2s_comm_format_t communication_format; /*!bck_io_num == -1 || GPIO_IS_VALID_GPIO(pin->bck_io_num)), ESP_ERR_INVALID_ARG, TAG, "bck_io_num invalid"); ESP_RETURN_ON_FALSE((pin->ws_io_num == -1 || GPIO_IS_VALID_GPIO(pin->ws_io_num)), @@ -197,45 +299,51 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) ESP_RETURN_ON_FALSE((pin->data_in_num == -1 || GPIO_IS_VALID_GPIO(pin->data_in_num)), ESP_ERR_INVALID_ARG, TAG, "data_in_num invalid"); - if (p_i2s[i2s_num]->mode & I2S_MODE_SLAVE) { - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].tx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].tx_bck_sig, 0); + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_SLAVE) { + /* For "tx + rx + slave" or "rx + slave" mode, we should select RX signal index for ws and bck */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_rx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_rx_bck_sig, 0); + /* For "tx + slave" mode, we should select TX signal index for ws and bck */ } else { - gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].rx_ws_sig, 0); - gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].rx_bck_sig, 0); + gpio_matrix_in_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].s_tx_ws_sig, 0); + gpio_matrix_in_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].s_tx_bck_sig, 0); } } else { + /* mclk only available in master mode */ ESP_RETURN_ON_ERROR(i2s_check_set_mclk(i2s_num, pin->mck_io_num), TAG, "mclk config failed"); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].tx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].tx_bck_sig, 0, 0); + /* For "tx + rx + master" or "tx + master" mode, we should select TX signal index for ws and bck */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_tx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_tx_bck_sig, 0, 0); + /* For "rx + master" mode, we should select RX signal index for ws and bck */ } else { - gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].rx_ws_sig, 0, 0); - gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].rx_bck_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->ws_io_num, i2s_periph_signal[i2s_num].m_rx_ws_sig, 0, 0); + gpio_matrix_out_check_and_set(pin->bck_io_num, i2s_periph_signal[i2s_num].m_rx_bck_sig, 0, 0); } } + /* Set data input/ouput GPIO */ gpio_matrix_out_check_and_set(pin->data_out_num, i2s_periph_signal[i2s_num].data_out_sig, 0, 0); gpio_matrix_in_check_and_set(pin->data_in_num, i2s_periph_signal[i2s_num].data_in_sig, 0); return ESP_OK; } - -/************************************************************** - * I2S DMA operation * - * - i2s_dma_rx_callback * - * - i2s_dma_tx_callback * - * - i2s_intr_handler_default * - * - i2s_tx_reset * - * - i2s_rx_reset * - * - i2s_tx_start * - * - i2s_rx_start * - * - i2s_tx_stop * - * - i2s_rx_stop * - **************************************************************/ - +/*------------------------------------------------------------- + I2S DMA operation + -------------------------------------------------------------*/ #if SOC_GDMA_SUPPORTED +/** + * @brief GDMA rx callback function + * @note This function is called by GDMA default ISR handler + * + * @param dma_chan GDMA channel handler + * @param event_data GDMA rx event data + * @param user_data GDMA user data + * @return + * - true need yield + * - false no need + */ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -262,6 +370,16 @@ static bool IRAM_ATTR i2s_dma_rx_callback(gdma_channel_handle_t dma_chan, gdma_e return high_priority_task_awoken; } +/** + * @brief GDMA tx callback function + * @note This function is called by GDMA default ISR handler + * + * @param dma_chan GDMA channel handler + * @param event_data GDMA tx event data + * @param user_data GDMA user data + * @return + * - whether need yield + */ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) { i2s_obj_t *p_i2s = (i2s_obj_t *) user_data; @@ -291,6 +409,13 @@ static bool IRAM_ATTR i2s_dma_tx_callback(gdma_channel_handle_t dma_chan, gdma_e } #else + +/** + * @brief I2S defalut interrupt handler + * @note This function is triggered by I2S dedicated DMA interrupt + * + * @param arg Argument transport to ISR, here is the pointer to I2S object + */ static void IRAM_ATTR i2s_intr_handler_default(void *arg) { i2s_obj_t *p_i2s = (i2s_obj_t *) arg; @@ -360,40 +485,103 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) } #endif + +/** + * @brief I2S DMA interrupt initialization + * @note I2S will use GDMA if chip supports, and the interrupt is triggered by GDMA. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK I2S DMA interrupt initialize success + * - ESP_ERR_NOT_FOUND GDMA channel not found + * - ESP_ERR_INVALID_ARG Invalid arguments + * - ESP_ERR_INVALID_STATE GDMA state error + */ +static esp_err_t i2s_dma_intr_init(i2s_port_t i2s_num, int intr_flag) +{ +#if SOC_GDMA_SUPPORTED + /* Set GDMA trigger module */ + gdma_trigger_t trig = {.periph = GDMA_TRIG_PERIPH_I2S}; + + switch (i2s_num) { +#if SOC_I2S_NUM > 1 + case I2S_NUM_1: + trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S1; + break; +#endif + default: + trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0; + break; + } + + /* Set GDMA config */ + gdma_channel_alloc_config_t dma_cfg = {}; + if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; + /* Register a new GDMA tx channel */ + ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->tx_dma_chan), TAG, "Register tx dma channel error"); + ESP_RETURN_ON_ERROR(gdma_connect(p_i2s[i2s_num]->tx_dma_chan, trig), TAG, "Connect tx dma channel error"); + gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback}; + /* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */ + gdma_register_tx_event_callbacks(p_i2s[i2s_num]->tx_dma_chan, &cb, p_i2s[i2s_num]); + } + if ( p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; + /* Register a new GDMA rx channel */ + ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->rx_dma_chan), TAG, "Register rx dma channel error"); + ESP_RETURN_ON_ERROR(gdma_connect(p_i2s[i2s_num]->rx_dma_chan, trig), TAG, "Connect rx dma channel error"); + gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback}; + /* Set callback function for GDMA, the interrupt is triggered by GDMA, then the GDMA ISR will call the callback function */ + gdma_register_rx_event_callbacks(p_i2s[i2s_num]->rx_dma_chan, &cb, p_i2s[i2s_num]); + } +#else + /* Initial I2S module interrupt */ + ESP_RETURN_ON_ERROR(esp_intr_alloc(i2s_periph_signal[i2s_num].irq, intr_flag, i2s_intr_handler_default, p_i2s[i2s_num], &p_i2s[i2s_num]->i2s_isr_handle), TAG, "Register I2S Interrupt error"); +#endif // SOC_GDMA_SUPPORTED + return ESP_OK; +} + +/** + * @brief I2S tx reset + * + * @param i2s_num I2S device number + */ static void i2s_tx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->tx->curr_ptr = NULL; p_i2s[i2s_num]->tx->rw_pos = 0; + i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED - // gdma_stop(p_i2s[i2s_num]->tx_dma_chan); - i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); gdma_reset(p_i2s[i2s_num]->tx_dma_chan); - i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); #else - // Reset I2S TX module first, and then, reset DMA and FIFO. - i2s_hal_reset_tx(&(p_i2s[i2s_num]->hal)); i2s_hal_reset_txdma(&(p_i2s[i2s_num]->hal)); - i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); #endif + i2s_hal_reset_tx_fifo(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S rx reset + * + * @param i2s_num I2S device number + */ static void i2s_rx_reset(i2s_port_t i2s_num) { p_i2s[i2s_num]->rx->curr_ptr = NULL; p_i2s[i2s_num]->rx->rw_pos = 0; + i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED - i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); gdma_reset(p_i2s[i2s_num]->rx_dma_chan); - i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); #else - - // Reset I2S RX module first, and then, reset DMA and FIFO. - i2s_hal_reset_rx(&(p_i2s[i2s_num]->hal)); i2s_hal_reset_rxdma(&(p_i2s[i2s_num]->hal)); - i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); #endif + i2s_hal_reset_rx_fifo(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S tx start + * + * @param i2s_num I2S device number + */ static void i2s_tx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED @@ -406,6 +594,11 @@ static void i2s_tx_start(i2s_port_t i2s_num) i2s_hal_start_tx(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S rx start + * + * @param i2s_num I2S device number + */ static void i2s_rx_start(i2s_port_t i2s_num) { #if SOC_GDMA_SUPPORTED @@ -418,193 +611,269 @@ static void i2s_rx_start(i2s_port_t i2s_num) i2s_hal_start_rx(&(p_i2s[i2s_num]->hal)); } +/** + * @brief I2S tx stop + * + * @param i2s_num I2S device number + */ static void i2s_tx_stop(i2s_port_t i2s_num) { + i2s_hal_stop_tx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->tx_dma_chan); #else i2s_hal_stop_tx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_stop_tx(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_tx_intr(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_tx_dma(&(p_i2s[i2s_num]->hal)); #endif } +/** + * @brief I2S rx stop + * + * @param i2s_num I2S device number + */ static void i2s_rx_stop(i2s_port_t i2s_num) { + i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); #if SOC_GDMA_SUPPORTED gdma_stop(p_i2s[i2s_num]->rx_dma_chan); #else i2s_hal_stop_rx_link(&(p_i2s[i2s_num]->hal)); - i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_rx_intr(&(p_i2s[i2s_num]->hal)); i2s_hal_disable_rx_dma(&(p_i2s[i2s_num]->hal)); #endif } -/************************************************************** - * I2S buffer operation * - * - i2s_alloc_dma_buffer * - * - i2s_destroy_dma_queue * - * - i2s_create_dma_queue * - * - i2s_zero_dma_buffer * - **************************************************************/ -static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, int data_bits, int ch) +/*------------------------------------------------------------- + I2S buffer operation + -------------------------------------------------------------*/ +/** + * @brief I2S get DMA buffer size + * + * @param i2s_num I2S device number + * @return + * - DMA buffer size + */ +static inline uint32_t i2s_get_buf_size(i2s_port_t i2s_num) { - if (p_i2s[i2s_num]->channel_num != ch) { - p_i2s[i2s_num]->channel_num = (ch == 2) ? 2 : 1; - } + /* Calculate bytes per sample, align to 16 bit */ + uint32_t bytes_per_sample = ((p_i2s[i2s_num]->hal_cfg.sample_bits + 15) / 16) * 2; + /* The DMA buffer limitation is 4092 bytes */ + uint32_t bytes_per_frame = bytes_per_sample * p_i2s[i2s_num]->hal_cfg.active_chan; + p_i2s[i2s_num]->dma_buf_len = (p_i2s[i2s_num]->dma_buf_len * bytes_per_frame > I2S_DMA_BUFFER_MAX_SIZE) ? + I2S_DMA_BUFFER_MAX_SIZE / bytes_per_frame : p_i2s[i2s_num]->dma_buf_len; + return p_i2s[i2s_num]->dma_buf_len * bytes_per_frame; +} - i2s_dma_t *save_tx = NULL, *save_rx = NULL; - - if (data_bits != p_i2s[i2s_num]->bits_per_sample) { - p_i2s[i2s_num]->bits_per_sample = data_bits; - - // Round bytes_per_sample up to next multiple of 16 bits - int halfwords_per_sample = (data_bits + 15) / 16; - p_i2s[i2s_num]->bytes_per_sample = halfwords_per_sample * 2; - - // Because limited of DMA buffer is 4092 bytes - if (p_i2s[i2s_num]->dma_buf_len * p_i2s[i2s_num]->bytes_per_sample * p_i2s[i2s_num]->channel_num > 4092) { - p_i2s[i2s_num]->dma_buf_len = 4092 / p_i2s[i2s_num]->bytes_per_sample / p_i2s[i2s_num]->channel_num; +/** + * @brief Delete DMA buffer and descriptor + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * @return + * - ESP_OK DMA buffer delete success + * - ESP_ERR_INVALID_ARG dma_obj is NULL + */ +static esp_err_t i2s_delete_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) +{ + ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); + /* Loop to destroy every descriptor and buffer */ + for (int cnt = 0; cnt < p_i2s[i2s_num]->dma_buf_count; cnt++) { + if (dma_obj->desc && dma_obj->desc[cnt]) { + free(dma_obj->desc[cnt]); + dma_obj->desc[cnt] = NULL; } - - // Re-create TX DMA buffer - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - save_tx = p_i2s[i2s_num]->tx; - //destroy old tx dma if exist - if (save_tx) { - i2s_destroy_dma_queue(i2s_num, save_tx); - } - p_i2s[i2s_num]->tx = i2s_create_dma_queue(i2s_num, p_i2s[i2s_num]->dma_buf_count, p_i2s[i2s_num]->dma_buf_len); - if (p_i2s[i2s_num]->tx == NULL) { - ESP_LOGE(TAG, "Failed to create tx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - } - // Re-create RX DMA buffer - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { - save_rx = p_i2s[i2s_num]->rx; - //destroy old rx dma if exist - if (save_rx) { - i2s_destroy_dma_queue(i2s_num, save_rx); - } - p_i2s[i2s_num]->rx = i2s_create_dma_queue(i2s_num, p_i2s[i2s_num]->dma_buf_count, p_i2s[i2s_num]->dma_buf_len); - if (p_i2s[i2s_num]->rx == NULL) { - ESP_LOGE(TAG, "Failed to create rx dma buffer"); - i2s_driver_uninstall(i2s_num); - return ESP_ERR_NO_MEM; - } - i2s_hal_set_rx_eof_num(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->dma_buf_len * p_i2s[i2s_num]->channel_num * p_i2s[i2s_num]->bytes_per_sample); + if (dma_obj->buf && dma_obj->buf[cnt]) { + free(dma_obj->buf[cnt]); + dma_obj->buf[cnt] = NULL; } } return ESP_OK; } -static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +/** + * @brief Allocate memory for DMA buffer and descriptor + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * @return + * - ESP_OK Allocate success + * - ESP_ERR_NO_MEM No memory for DMA buffer + */ +static esp_err_t i2s_alloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) { - int bux_idx; - if (p_i2s[i2s_num] == NULL) { - ESP_LOGE(TAG, "Not initialized yet"); - return ESP_ERR_INVALID_ARG; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, err, TAG, "I2S DMA object can't be NULL"); + + uint32_t buf_cnt = p_i2s[i2s_num]->dma_buf_count; + for (int cnt = 0; cnt < buf_cnt; cnt++) { + /* Allocate DMA buffer */ + dma_obj->buf[cnt] = (char *) heap_caps_calloc(dma_obj->buf_size, sizeof(char), MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(dma_obj->buf[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma buffer"); + /* Initialize DMA buffer to 0 */ + memset(dma_obj->buf[cnt], 0, dma_obj->buf_size); + ESP_LOGD(TAG, "Addr[%d] = %d", cnt, (int)dma_obj->buf[cnt]); + + /* Allocate DMA descpriptor */ + dma_obj->desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(dma_obj->desc[cnt], ESP_ERR_NO_MEM, err, TAG, "Error malloc dma description entry"); } - if (dma == NULL) { - ESP_LOGE(TAG, "dma is NULL"); - return ESP_ERR_INVALID_ARG; + /* DMA descriptor must be initialize after all descriptor has been created, otherwise they can't be linked together as a chain */ + for (int cnt = 0; cnt < buf_cnt; cnt++) { + /* Initialize DMA descriptor */ + dma_obj->desc[cnt]->owner = 1; + dma_obj->desc[cnt]->eof = 1; + dma_obj->desc[cnt]->sosf = 0; + dma_obj->desc[cnt]->length = dma_obj->buf_size; + dma_obj->desc[cnt]->size = dma_obj->buf_size; + dma_obj->desc[cnt]->buf = (uint8_t *) dma_obj->buf[cnt]; + dma_obj->desc[cnt]->offset = 0; + /* Link to the next descriptor */ + dma_obj->desc[cnt]->empty = (uint32_t)((cnt < (buf_cnt - 1)) ? (dma_obj->desc[cnt + 1]) : dma_obj->desc[0]); } - for (bux_idx = 0; bux_idx < p_i2s[i2s_num]->dma_buf_count; bux_idx++) { - if (dma->desc && dma->desc[bux_idx]) { - free(dma->desc[bux_idx]); - } - if (dma->buf && dma->buf[bux_idx]) { - free(dma->buf[bux_idx]); - } + ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_obj->buf_size, buf_cnt); + return ESP_OK; +err: + /* Delete DMA buffer if failed to allocate memory */ + i2s_delete_dma_buffer(i2s_num, dma_obj); + return ret; +} + +/** + * @brief Realloc I2S dma buffer + * + * @param i2s_num I2S device number + * @param dma_obj DMA object + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM No memory for I2S tx dma buffer + */ +static esp_err_t i2s_realloc_dma_buffer(i2s_port_t i2s_num, i2s_dma_t *dma_obj) +{ + ESP_RETURN_ON_FALSE(dma_obj, ESP_ERR_INVALID_ARG, TAG, "I2S DMA object can't be NULL"); + + /* Destroy old dma descriptor and buffer */ + i2s_delete_dma_buffer(i2s_num, dma_obj); + /* Alloc new dma descriptor and buffer */ + ESP_RETURN_ON_ERROR(i2s_alloc_dma_buffer(i2s_num, dma_obj), TAG, "Failed to allocate dma buffer"); + + return ESP_OK; +} + +/** + * @brief I2S destroy the whole DMA object + * + * @param i2s_num I2S device number + * @param dma Secondary pointer to the DMA object + * @return + * - ESP_OK I2S DMA buffer has been destroyed successfully + * - ESP_ERR_INVALID_ARG I2S driver has not installed yet + */ +static esp_err_t i2s_destroy_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) +{ + /* Check if DMA truely need destroy */ + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S not initialized yet"); + if (!(*dma)) { + return ESP_OK; } - if (dma->buf) { - free(dma->buf); + /* Destroy every descriptor and buffer */ + i2s_delete_dma_buffer(i2s_num, (*dma)); + /* Destroy descriptor pointer */ + if ((*dma)->desc) { + free((*dma)->desc); + (*dma)->desc = NULL; } - if (dma->desc) { - free(dma->desc); + /* Destroy buffer pointer */ + if ((*dma)->buf) { + free((*dma)->buf); + (*dma)->buf = NULL; } + /* Delete DMA mux */ + vSemaphoreDelete((*dma)->mux); + /* Delete DMA queue */ + vQueueDelete((*dma)->queue); + /* Free DMA structure */ + free(*dma); + *dma = NULL; ESP_LOGI(TAG, "DMA queue destroyed"); - vQueueDelete(dma->queue); - vSemaphoreDelete(dma->mux); - free(dma); return ESP_OK; } -static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +/** + * @brief Create I2S DMA object + * @note This function only create I2S DMA object but will not allocate memory + * for DMA descriptor and buffer, call 'i2s_alloc_dma_buffer' additionally to + * allocate DMA buffer + * + * @param i2s_num I2S device number + * @param dma The secondary pointer of DMA object + * @return + * - ESP_OK The pointer of DMA object + * - ESP_ERR_INVALID_ARG NULL pointer error or DMA object has been created + * - ESP_ERR_NO_MEM No memory for new DMA object + */ +static esp_err_t i2s_create_dma_object(i2s_port_t i2s_num, i2s_dma_t **dma) { - int bux_idx; - int sample_size = p_i2s[i2s_num]->bytes_per_sample * p_i2s[i2s_num]->channel_num; - i2s_dma_t *dma = (i2s_dma_t *) malloc(sizeof(i2s_dma_t)); - if (dma == NULL) { - ESP_LOGE(TAG, "Error malloc i2s_dma_t"); - return NULL; + ESP_RETURN_ON_FALSE(dma, ESP_ERR_INVALID_ARG, TAG, "DMA object secondary pointer is NULL"); + ESP_RETURN_ON_FALSE((*dma == NULL), ESP_ERR_INVALID_ARG, TAG, "DMA object has been created"); + uint32_t buf_cnt = p_i2s[i2s_num]->dma_buf_count; + /* Allocate new DMA structure */ + *dma = (i2s_dma_t *) malloc(sizeof(i2s_dma_t)); + ESP_RETURN_ON_FALSE(*dma, ESP_ERR_NO_MEM, TAG, "DMA object allocate failed"); + /* Allocate DMA buffer poiter */ + (*dma)->buf = (char **)heap_caps_calloc(buf_cnt, sizeof(char *), MALLOC_CAP_DMA); + if (!(*dma)->buf) { + goto err; } - memset(dma, 0, sizeof(i2s_dma_t)); - - dma->buf = (char **)malloc(sizeof(char *) * dma_buf_count); - if (dma->buf == NULL) { - ESP_LOGE(TAG, "Error malloc dma buffer pointer"); - free(dma); - return NULL; + /* Allocate secondary pointer of DMA descriptor chain */ + (*dma)->desc = (lldesc_t **)heap_caps_calloc(buf_cnt, sizeof(lldesc_t *), MALLOC_CAP_DMA); + if (!(*dma)->desc) { + goto err; } - memset(dma->buf, 0, sizeof(char *) * dma_buf_count); - - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->buf[bux_idx] = (char *) heap_caps_calloc(1, dma_buf_len * sample_size, MALLOC_CAP_DMA); - if (dma->buf[bux_idx] == NULL) { - ESP_LOGE(TAG, "Error malloc dma buffer"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - ESP_LOGD(TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + /* Create queue and mutex */ + (*dma)->queue = xQueueCreate(buf_cnt - 1, sizeof(char *)); + if (!(*dma)->queue) { + goto err; + } + (*dma)->mux = xSemaphoreCreateMutex(); + if (!(*dma)->mux) { + goto err; } - dma->desc = (lldesc_t **) malloc(sizeof(lldesc_t *) * dma_buf_count); - if (dma->desc == NULL) { - ESP_LOGE(TAG, "Error malloc dma description"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx] = (lldesc_t *) heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA); - if (dma->desc[bux_idx] == NULL) { - ESP_LOGE(TAG, "Error malloc dma description entry"); - i2s_destroy_dma_queue(i2s_num, dma); - return NULL; - } - } - for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { - dma->desc[bux_idx]->owner = 1; - dma->desc[bux_idx]->eof = 1; - dma->desc[bux_idx]->sosf = 0; - dma->desc[bux_idx]->length = dma_buf_len * sample_size; - dma->desc[bux_idx]->size = dma_buf_len * sample_size; - dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; - dma->desc[bux_idx]->offset = 0; - dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); - } - dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char *)); - dma->mux = xSemaphoreCreateMutex(); - dma->buf_size = dma_buf_len * sample_size; - ESP_LOGI(TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); - return dma; + return ESP_OK; +err: + ESP_LOGE(TAG, "I2S DMA object create failed, preparing to uninstall"); + /* Destroy DMA queue if failed to allocate memory */ + i2s_destroy_dma_object(i2s_num, dma); + return ESP_ERR_NO_MEM; } +/** + * @brief Zero the contents of the TX DMA buffer. + * @note Pushes zero-byte samples into the TX DMA buffer, until it is full. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + + /* Clear I2S RX DMA buffer */ if (p_i2s[i2s_num]->rx && p_i2s[i2s_num]->rx->buf != NULL && p_i2s[i2s_num]->rx->buf_size != 0) { for (int i = 0; i < p_i2s[i2s_num]->dma_buf_count; i++) { memset(p_i2s[i2s_num]->rx->buf[i], 0, p_i2s[i2s_num]->rx->buf_size); } } + + /* Clear I2S TX DMA buffer */ if (p_i2s[i2s_num]->tx && p_i2s[i2s_num]->tx->buf != NULL && p_i2s[i2s_num]->tx->buf_size != 0) { - int bytes_left = 0; - bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; + /* Finish to write all tx data */ + int bytes_left = (p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos) % 4; if (bytes_left) { size_t zero_bytes = 0, bytes_written; i2s_write(i2s_num, (void *)&zero_bytes, bytes_left, &bytes_written, portMAX_DELAY); @@ -616,23 +885,14 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) return ESP_OK; } -/************************************************************** - * I2S clock operation * - * - i2s_get_clk * - * - i2s_apll_get_fi2s * - * - i2s_apll_calculate_fi2s * - * - i2s_fbclk_cal * - **************************************************************/ - -float i2s_get_clk(i2s_port_t i2s_num) -{ - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - return (float)p_i2s[i2s_num]->sample_rate; -} - - +/*------------------------------------------------------------- + I2S clock operation + -------------------------------------------------------------*/ #if SOC_I2S_SUPPORTS_APLL -static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) +/** + * @brief Get APLL frequency + */ +static float i2s_apll_get_freq(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir) { int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; @@ -667,7 +927,7 @@ static float i2s_apll_get_fi2s(int bits_per_sample, int sdm0, int sdm1, int sdm2 * * The most accurate way to find the sdm0..2 and odir parameters is to loop through them all, * then apply the above formula, finding the closest frequency to the desired one. - * But 256*256*64*32 = 134.217.728 loops are too slow with ESP32 + * But 256*256*64*32 = 134,217,728 loops are too slow with ESP32 * 1. We will choose the parameters with the highest level of change, * With 350MHzmclk_multiple ? p_i2s[i2s_num]->mclk_multiple : I2S_MCLK_MULTIPLE_256; - uint32_t _bck_div = rate * multi / _fbck; - i2s_clock_src_t clk_src = I2S_CLK_D2CLK; - -//ADC mode only support on ESP32, -#if SOC_I2S_SUPPORTS_ADC_DAC - if ( p_i2s[i2s_num]->mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN)) { - _fbck = rate * I2S_LL_AD_BCK_FACTOR * 2; - _bck_div = I2S_LL_AD_BCK_FACTOR; - } -#endif // SOC_I2S_SUPPORTS_ADC_DAC - - if ( p_i2s[i2s_num]->mode & I2S_MODE_PDM) { -#if SOC_I2S_SUPPORTS_PDM_TX - if ( p_i2s[i2s_num]->mode & I2S_MODE_TX) { - int fp = i2s_hal_get_tx_pdm_fp(&(p_i2s[i2s_num]->hal)); - int fs = i2s_hal_get_tx_pdm_fs(&(p_i2s[i2s_num]->hal)); - _fbck = rate * I2S_LL_PDM_BCK_FACTOR * fp / fs; - } -#endif //SOC_I2S_SUPPORTS_PDM_TX -#if SOC_I2S_SUPPORTS_PDM_RX - if ( p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_pdm_dsr_t dsr; - i2s_hal_get_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), &dsr); - _fbck = rate * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); - } -#endif // SOC_I2S_SUPPORTS_PDM_RX - _bck_div = 8; - } - #if SOC_I2S_SUPPORTS_APLL - int sdm0 = 0; - int sdm1 = 0; - int sdm2 = 0; - int odir = 0; - //If APLL is specified, try to calculate in APLL - if (p_i2s[i2s_num]->use_apll && i2s_apll_calculate_fi2s(p_i2s[i2s_num]->fixed_mclk, channel_bit, &sdm0, &sdm1, &sdm2, &odir) == ESP_OK) { - _sclk = p_i2s[i2s_num]->fixed_mclk; - clk_src = I2S_CLK_APLL; - ESP_LOGD(TAG, "sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, odir); + if (use_apll) { + int sdm0 = 0; + int sdm1 = 0; + int sdm2 = 0; + int odir = 0; + if ((mclk / p_i2s[i2s_num]->hal_cfg.chan_bits / p_i2s[i2s_num]->hal_cfg.total_chan) < SOC_I2S_APLL_MIN_RATE) { + ESP_LOGE(TAG, "mclk is too small"); + return 0; + } + i2s_apll_calculate_fi2s(mclk, p_i2s[i2s_num]->hal_cfg.sample_bits, &sdm0, &sdm1, &sdm2, &odir); + ESP_LOGI(TAG, "APLL Enabled, coefficient: sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir); + rtc_clk_apll_enable(true, sdm0, sdm1, sdm2, odir); + /* Set I2S_APLL as I2S module clock source */ + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_APLL); + /* In APLL mode, there is no sclk but only mclk, so return 0 here to indicate APLL mode */ + return 0; } -#endif // SOC_I2S_SUPPORTS_APLL - if ((_fbck * _bck_div) > _sclk) { - ESP_LOGE(TAG, "sample rate is too large\r\n"); - return ESP_ERR_INVALID_ARG; + /* Set I2S_D2CLK (160M) as default I2S module clock source */ + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); + return I2S_LL_BASE_CLK; +#else + if (use_apll) { + ESP_LOGW(TAG, "APLL not supported on current chip, use I2S_CLK_D2CLK as default clock source"); } - i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), clk_src); - *sclk = _sclk; - *fbck = _fbck; - *bck_div = _bck_div; + /* Set I2S_D2CLK (160M) as I2S module clock source */ + i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK); + return I2S_LL_BASE_CLK; +#endif +} + +#if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC +/** + * @brief I2S calculate clock for built-in ADC/DAC mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & (I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_ARG, TAG, "current mode is not built-in ADC/DAC"); + + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_AD_BCK_FACTOR * 2; + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = I2S_LL_AD_BCK_FACTOR; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ + clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + /* Calculate bclk_div = mclk / bclk */ + clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + /* Get I2S system clock by config source clock */ + clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + +#if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief I2S calculate clock for PDM tx mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); + + int fp = i2s_hal_get_tx_pdm_fp(&(p_i2s[i2s_num]->hal)); + int fs = i2s_hal_get_tx_pdm_fs(&(p_i2s[i2s_num]->hal)); + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * fp / fs; + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = 8; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ + clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + /* Calculate bclk_div = mclk / bclk */ + clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + /* Get I2S system clock by config source clock */ + clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_PDM_TX + +#if SOC_I2S_SUPPORTS_PDM_RX +/** + * @brief I2S calculate clock for PDM rx mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM, ESP_ERR_INVALID_ARG, TAG, "current mode is not PDM"); + + i2s_pdm_dsr_t dsr; + i2s_hal_get_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), &dsr); + /* Set I2S bit clock */ + clk_cfg->bclk = p_i2s[i2s_num]->hal_cfg.sample_rate * I2S_LL_PDM_BCK_FACTOR * (dsr == I2S_PDM_DSR_16S ? 2 : 1); + /* Set I2S bit clock default division */ + clk_cfg->bclk_div = 8; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ + clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : clk_cfg->bclk * clk_cfg->bclk_div; + /* Calculate bclk_div = mclk / bclk */ + clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + /* Get I2S system clock by config source clock */ + clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + + return ESP_OK; +} +#endif // SOC_I2S_SUPPORTS_PDM_RX + +/** + * @brief I2S calculate clock for common mode (philip, MSB, PCM) + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Get clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + ESP_RETURN_ON_FALSE(clk_cfg, ESP_ERR_INVALID_ARG, TAG, "input clk_cfg is NULL"); + + uint32_t rate = p_i2s[i2s_num]->hal_cfg.sample_rate; + uint32_t chan_num = p_i2s[i2s_num]->hal_cfg.total_chan < 2 ? 2 : p_i2s[i2s_num]->hal_cfg.total_chan; + uint32_t chan_bit = p_i2s[i2s_num]->hal_cfg.chan_bits; + uint32_t multi; + /* Calculate multiple */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_MASTER || p_i2s[i2s_num]->use_apll) { + multi = p_i2s[i2s_num]->mclk_multiple ? p_i2s[i2s_num]->mclk_multiple : I2S_MCLK_MULTIPLE_256; + } else { + /* Only need to set the multiple of mclk to sample rate for MASTER mode, + * because BCK and WS clock are provided by the external codec in SLAVE mode. + * The multiple should be big enough to get a high module clock which could detect the edges of externel clock more accurately, + * otherwise the data we receive or send would get a large latency and go wrong due to the slow module clock. + * But on ESP32 and ESP32S2, due to the different clock work mode in hardware, + * their multiple should be set to an appropriate range according to the sample bits, + * and this particular multiple finally aims at guaranteeing the bclk_div not smaller than 8, + * if not, the I2S may can't send data or send wrong data. + * Here use 'SOC_I2S_SUPPORTS_TDM' to differentialize other chips with ESP32 and ESP32S2. + */ +#if SOC_I2S_SUPPORTS_TDM + multi = I2S_LL_BASE_CLK / rate; +#else + multi = 64 * chan_bit; +#endif + } + /* Set I2S bit clock */ + clk_cfg->bclk = rate * chan_num * chan_bit; + /* If fixed_mclk and use_apll are set, use fixed_mclk as mclk frequency, otherwise calculate by mclk = sample_rate * multiple */ + clk_cfg->mclk = (p_i2s[i2s_num]->use_apll && p_i2s[i2s_num]->fixed_mclk) ? + p_i2s[i2s_num]->fixed_mclk : (rate * multi); + /* Calculate bclk_div = mclk / bclk */ + clk_cfg->bclk_div = clk_cfg->mclk / clk_cfg->bclk; + /* Get I2S system clock by config source clock */ + clk_cfg->sclk = i2s_config_source_clock(i2s_num, p_i2s[i2s_num]->use_apll, clk_cfg->mclk); + /* Get I2S master clock rough division, later will calculate the fine division parameters in HAL */ + clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk; + + /* Check if the configuration is correct */ + ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + return ESP_OK; } -/************************************************************** - * I2S configuration * - * - i2s_get_active_chan_num * - * - i2s_set_dac_mode * - * - _i2s_adc_mode_recover * - * - i2s_set_adc_mode * - * - i2s_adc_enable * - * - i2s_adc_disable * - * - i2s_set_sample_rates * - * - i2s_pcm_config * - * - i2s_set_pdm_rx_down_sample * - * - i2s_set_pdm_tx_up_sample * - * - i2s_check_cfg_static * - * - i2s_param_config * - * - i2s_set_clk * - * - i2s_set_mode * - **************************************************************/ +/** + * @brief I2S calculate clocks according to the selected I2S mode + * + * @param i2s_num I2S device number + * @param clk_cfg Struct to restore clock confiuration + * @return + * - ESP_OK Claculate clock success + * - ESP_ERR_INVALID_ARG Invalid args + */ +static esp_err_t i2s_calculate_clock(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + /* Calculate clock for ADC mode */ +#if SOC_I2S_SUPPORTS_ADC + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { + ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "ADC clock calculate failed"); + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_ADC + /* Calculate clock for DAC mode */ +#if SOC_I2S_SUPPORTS_DAC + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_DAC_BUILT_IN) { + ESP_RETURN_ON_ERROR(i2s_calculate_adc_dac_clock(i2s_num, clk_cfg), TAG, "DAC clock calculate failed"); + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_DAC -static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) + /* Calculate clock for PDM mode */ +#if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM) { +#if SOC_I2S_SUPPORTS_PDM_TX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_tx_clock(i2s_num, clk_cfg), TAG, "PDM TX clock calculate failed"); + } +#endif // SOC_I2S_SUPPORTS_PDM_TX +#if SOC_I2S_SUPPORTS_PDM_RX + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + ESP_RETURN_ON_ERROR(i2s_calculate_pdm_rx_clock(i2s_num, clk_cfg), TAG, "PDM RX clock calculate failed"); + } +#endif // SOC_I2S_SUPPORTS_PDM_RX + return ESP_OK; + } +#endif // SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + + /* Calculate clock for common mode */ + ESP_RETURN_ON_ERROR(i2s_calculate_common_clock(i2s_num, clk_cfg), TAG, "Common clock calculate failed"); + return ESP_OK; +} + +/*------------------------------------------------------------- + I2S configuration + -------------------------------------------------------------*/ +#if SOC_I2S_SUPPORTS_TDM +/** + * @brief Get max actived channel number + * + * @param chan_mask I2S channel mask that indicates which channels are actived + * @return + * - Max actived channel number + */ +static uint32_t i2s_get_max_channel_num(i2s_channel_t chan_mask) +{ + uint32_t max_chan = 0; + uint32_t channel = chan_mask & 0xFFFF; + for (int i = 0; channel && i < 16; i++, channel >>= 1) { + if (channel & 0x01) { + max_chan = i + 1; + } + } + /* Can't be smaller than 2 */ + return max_chan < 2 ? 2 : max_chan; +} +#endif + +/** + * @brief Get active channel number according to channel format + * @note In 'I2S_CHANNEL_FMT_MULTIPLE' format, this function will check + * 'total_chan' and fix it if it is not correct. + * + * @param hal_cfg [input/output] I2S hal configuration structer + * @return + * - Active channel number + */ +static uint32_t i2s_get_active_channel_num(const i2s_hal_config_t *hal_cfg) { switch (hal_cfg->chan_fmt) { case I2S_CHANNEL_FMT_RIGHT_LEFT: //fall through @@ -843,18 +1312,12 @@ static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) #if SOC_I2S_SUPPORTS_TDM case I2S_CHANNEL_FMT_MULTIPLE: { uint32_t num = 0; - uint32_t max_chan = 0; - uint32_t chan_mask = hal_cfg->chan_mask; - + uint32_t chan_mask = hal_cfg->chan_mask & 0xFFFF; for (int i = 0; chan_mask && i < 16; i++, chan_mask >>= 1) { - if ((chan_mask & 0x01) == 1) { + if (chan_mask & 0x01) { num++; - max_chan = i + 1; } } - if (max_chan > hal_cfg->total_chan) { - hal_cfg->total_chan = max_chan; - } return num; } #endif @@ -863,7 +1326,15 @@ static uint32_t i2s_get_active_chan_num(i2s_hal_config_t *hal_cfg) } } -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC +/** + * @brief I2S set built-in DAC mode + * + * @param dac_mode DAC mode + * @return + * - ESP_OK Set DAC success + * - ESP_ERR_INVALID_ARG Wrong DAC mode + */ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) { ESP_RETURN_ON_FALSE((dac_mode < I2S_DAC_CHANNEL_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s dac mode error"); @@ -885,13 +1356,31 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode) } return ESP_OK; } +#endif // SOC_I2S_SUPPORTS_DAC +#if SOC_I2S_SUPPORTS_ADC +/** + * @brief ADC mode recover + * + * @return + * - ESP_OK ADC Recover success + * - ESP_ERR_INVALID_ARG ADC not initialized yet + */ static esp_err_t _i2s_adc_mode_recover(void) { ESP_RETURN_ON_FALSE(((_i2s_adc_unit != -1) && (_i2s_adc_channel != -1)), ESP_ERR_INVALID_ARG, TAG, "i2s ADC recover error, not initialized..."); return adc_i2s_mode_init(_i2s_adc_unit, _i2s_adc_channel); } +/** + * @brief I2S set adc mode + * + * @param adc_unit ADC unit number + * @param adc_channel ADC channel + * @return + * - ESP_OK ADC Recover success + * - ESP_ERR_INVALID_ARG ADC not initialized yet + */ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) { ESP_RETURN_ON_FALSE((adc_unit < ADC_UNIT_2), ESP_ERR_INVALID_ARG, TAG, "i2s ADC unit error, only support ADC1 for now"); @@ -901,23 +1390,41 @@ esp_err_t i2s_set_adc_mode(adc_unit_t adc_unit, adc1_channel_t adc_channel) return adc_i2s_mode_init(adc_unit, adc_channel); } +/** + * @brief I2S enable ADC mode + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Enable ADC success + * - ESP_ERR_INVALID_ARG Invalid argument + * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC + */ esp_err_t i2s_adc_enable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); adc1_dma_mode_acquire(); _i2s_adc_mode_recover(); i2s_rx_reset(i2s_num); - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } +/** + * @brief I2S disable ADC + * + * @param i2s_num I2S device number + * @return + * - ESP_OK I2S ADC mode successfully disabled + * - ESP_ERR_INVALID_ARG Invalid argument + * - ESP_ERR_INVALID_STATE Current I2S mode is not built-in ADC + */ esp_err_t i2s_adc_disable(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_STATE, TAG, "Not initialized yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN), ESP_ERR_INVALID_STATE, TAG, "i2s built-in adc not enabled"); i2s_hal_stop_rx(&(p_i2s[i2s_num]->hal)); adc1_lock_release(); @@ -925,25 +1432,47 @@ esp_err_t i2s_adc_disable(i2s_port_t i2s_num) } #endif - +/** + * @brief Set sample rate used for I2S RX and TX. + * @note The bit clock rate is determined by the sample rate and i2s_config_t configuration parameters (number of channels, bits_per_sample). + * `bit_clock = rate * (number of channels) * bits_per_sample` + * + * @param i2s_num I2S device number + * @param rate I2S sample rate (ex: 8000, 44100...) + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->bytes_per_sample > 0), ESP_ERR_INVALID_ARG, TAG, "bits_per_sample not set"); - return i2s_set_clk(i2s_num, rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.sample_bits > 0), ESP_ERR_INVALID_ARG, TAG, "sample bits not set"); + return i2s_set_clk(i2s_num, rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #if SOC_I2S_SUPPORTS_PCM +/** + * @brief Configure I2S a/u-law decompress or compress + * @note This function should be called after i2s driver installed + * Only take effect when the i2s 'communication_format' is set to 'I2S_COMM_FORMAT_STAND_PCM_SHORT' or 'I2S_COMM_FORMAT_STAND_PCM_LONG' + * + * @param i2s_num I2S_NUM_0 + * @param pcm_cfg Including mode selection and a/u-law decompress or compress configuration paramater + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->communication_format & I2S_COMM_FORMAT_STAND_PCM_SHORT), + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.comm_fmt & I2S_COMM_FORMAT_STAND_PCM_SHORT), ESP_ERR_INVALID_ARG, TAG, "i2s communication mode is not PCM mode"); i2s_stop(i2s_num); I2S_ENTER_CRITICAL(i2s_num); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_hal_tx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); - } else if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + } else if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_hal_rx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); } I2S_EXIT_CRITICAL(i2s_num); @@ -953,198 +1482,275 @@ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) #endif #if SOC_I2S_SUPPORTS_PDM_RX +/** + * @brief Set PDM mode down-sample rate + * In PDM RX mode, there would be 2 rounds of downsample process in hardware. + * In the first downsample process, the sampling number can be 16 or 8. + * In the second downsample process, the sampling number is fixed as 8. + * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly. + * @note After calling this function, it would call i2s_set_clk inside to update the clock frequency. + * Please call this function after I2S driver has been initialized. + * + * @param i2s_num I2S device number + * @param downsample i2s RX down sample rate for PDM mode. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsample) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); i2s_stop(i2s_num); i2s_hal_set_rx_pdm_dsr(&(p_i2s[i2s_num]->hal), downsample); // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, p_i2s[i2s_num]->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, p_i2s[i2s_num]->hal_cfg.sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #endif #if SOC_I2S_SUPPORTS_PDM_TX +/** + * @brief Set TX PDM mode up-sample rate + * @note If you have set PDM mode while calling 'i2s_driver_install', + * default PDM TX upsample parameters have already been set, + * no need to call this function again if you don't have to change the default configuration + * + * @param i2s_num I2S device number + * @param upsample_cfg Set I2S PDM up-sample rate configuration + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample_cfg_t *upsample_cfg) { ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_FAIL, TAG, "i2s has not installed yet"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "i2s mode is not PDM mode"); i2s_stop(i2s_num); i2s_hal_set_tx_pdm_fpfs(&(p_i2s[i2s_num]->hal), upsample_cfg->fp, upsample_cfg->fs); // i2s will start in 'i2s_set_clk' - return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->bits_per_sample, p_i2s[i2s_num]->channel_num); + return i2s_set_clk(i2s_num, upsample_cfg->sample_rate, p_i2s[i2s_num]->hal_cfg.sample_bits, p_i2s[i2s_num]->hal_cfg.active_chan); } #endif -static esp_err_t i2s_check_cfg_static(i2s_port_t i2s_num) +/** + * @brief I2S check the validity of configuration + * + * @param i2s_num I2S device number + * @param cfg I2S HAL configuration + * @return + * - ESP_OK I2S configuration is valid + * - ESP_ERR_INVALID_ARG I2S configuration is invalid + */ +static esp_err_t i2s_check_cfg_validity(i2s_port_t i2s_num, i2s_hal_config_t *cfg) { - i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; -#if SOC_I2S_SUPPORTS_ADC_DAC - //We only check if the I2S number is invalid when set to build in ADC and DAC mode. - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_ADC_BUILT_IN) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S ADC built-in only support on I2S0"); - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_DAC_BUILT_IN) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S DAC built-in only support on I2S0"); - return ESP_OK; -#endif - //We only check if the I2S number is invalid when set to PDM mode. - ESP_RETURN_ON_FALSE(!((cfg->mode & I2S_MODE_PDM) && (i2s_num != I2S_NUM_0)), ESP_ERR_INVALID_ARG, TAG, "I2S DAC PDM only support on I2S0"); - return ESP_OK; +#if SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX + /* Check PDM mode */ + if (cfg->mode & I2S_MODE_PDM) { + ESP_RETURN_ON_FALSE(i2s_num == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "I2S PDM mode only support on I2S0"); +#if !SOC_I2S_SUPPORTS_PDM_TX + ESP_RETURN_ON_FALSE(cfg->mode & I2S_MODE_TX, ESP_ERR_INVALID_ARG, TAG, "PDM does not support TX on this chip"); +#endif // SOC_I2S_SUPPORTS_PDM_TX +#if !SOC_I2S_SUPPORTS_PDM_RX + ESP_RETURN_ON_FALSE(cfg->mode & I2S_MODE_RX, ESP_ERR_INVALID_ARG, TAG, "PDM does not support RX on this chip"); +#endif // SOC_I2S_SUPPORTS_PDM_RX + } +#else + ESP_RETURN_ON_FALSE(!(cfg->mode & I2S_MODE_PDM), ESP_ERR_INVALID_ARG, TAG, "I2S PDM mode not supported on current chip"); +#endif // SOC_I2S_SUPPORTS_PDM_TX || SOC_I2S_SUPPORTS_PDM_RX +#if SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + /* Check built-in ADC/DAC mode */ + if (cfg->mode & (I2S_MODE_ADC_BUILT_IN | I2S_MODE_DAC_BUILT_IN)) { + ESP_RETURN_ON_FALSE(i2s_num == I2S_NUM_0, ESP_ERR_INVALID_ARG, TAG, "I2S built-in ADC/DAC only support on I2S0"); + } +#else + /* Check the transmit/receive mode */ + ESP_RETURN_ON_FALSE((cfg->mode & I2S_MODE_TX) || (cfg->mode & I2S_MODE_RX), ESP_ERR_INVALID_ARG, TAG, "I2S no TX/RX mode selected"); + /* Check communication format */ ESP_RETURN_ON_FALSE(cfg->comm_fmt && (cfg->comm_fmt < I2S_COMM_FORMAT_STAND_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid communication formats"); - ESP_RETURN_ON_FALSE(!((cfg->comm_fmt & I2S_COMM_FORMAT_STAND_MSB) && (cfg->comm_fmt & I2S_COMM_FORMAT_STAND_PCM_LONG)), ESP_ERR_INVALID_ARG, TAG, "multiple communication formats specified"); +#endif // SOC_I2S_SUPPORTS_ADC || SOC_I2S_SUPPORTS_DAC + return ESP_OK; } -static esp_err_t i2s_param_config(i2s_port_t i2s_num) +static void i2s_tx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + i2s_hal_tx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); + i2s_hal_set_tx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); +} + +static void i2s_rx_set_clk_and_channel(i2s_port_t i2s_num, i2s_hal_clock_cfg_t *clk_cfg) +{ + i2s_hal_rx_clock_config(&(p_i2s[i2s_num]->hal), clk_cfg); + i2s_hal_set_rx_sample_bit(&(p_i2s[i2s_num]->hal), p_i2s[i2s_num]->hal_cfg.chan_bits, p_i2s[i2s_num]->hal_cfg.sample_bits); + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); +} + +/** + * @brief Get clock set on particular port number. + * + * @param i2s_num I2S device number + * @return + * - sample rate + */ +float i2s_get_clk(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((i2s_check_cfg_static(i2s_num) == ESP_OK), ESP_ERR_INVALID_ARG, TAG, "param check error"); - - i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; - p_i2s[i2s_num]->communication_format = cfg->comm_fmt; -#if SOC_I2S_SUPPORTS_ADC_DAC - if ((cfg->mode & I2S_MODE_DAC_BUILT_IN) || (cfg->mode & I2S_MODE_ADC_BUILT_IN)) { - if (cfg->mode & I2S_MODE_DAC_BUILT_IN) { - i2s_hal_enable_builtin_dac(&(p_i2s[i2s_num]->hal)); - } - if (cfg->mode & I2S_MODE_ADC_BUILT_IN) { - //in ADC built-in mode, we need to call i2s_set_adc_mode to - //initialize the specific ADC channel. - //in the current stage, we only support ADC1 and single channel mode. - //In default data mode, the ADC data is in 12-bit resolution mode. - adc_power_acquire(); - i2s_hal_enable_builtin_adc(&(p_i2s[i2s_num]->hal)); - } - } else { - i2s_hal_disable_builtin_dac(&(p_i2s[i2s_num]->hal)); - i2s_hal_disable_builtin_adc(&(p_i2s[i2s_num]->hal)); -#endif - // configure I2S data port interface. - i2s_hal_config_param(&(p_i2s[i2s_num]->hal), cfg); -#if SOC_I2S_SUPPORTS_ADC_DAC - } -#endif - if ((p_i2s[i2s_num]->mode & I2S_MODE_RX) && (p_i2s[i2s_num]->mode & I2S_MODE_TX)) { - i2s_hal_enable_sig_loopback(&(p_i2s[i2s_num]->hal)); - if (p_i2s[i2s_num]->mode & I2S_MODE_MASTER) { - i2s_hal_enable_master_fd_mode(&(p_i2s[i2s_num]->hal)); - } else { - i2s_hal_enable_slave_fd_mode(&(p_i2s[i2s_num]->hal)); - } - } - return ESP_OK; + return (float)p_i2s[i2s_num]->hal_cfg.sample_rate; } +/** + * @brief Set clock & bit width used for I2S RX and TX. + * Similar to i2s_set_sample_rates(), but also sets bit width. + * + * 1. stop i2s + * 2. calculate mclk, bck, bck_factor + * 3. set clock configurations + * 4. realloc dma buffer if DMA buffer size changed + * 5. start i2s + * + * @param i2s_num I2S device number + * @param rate I2S sample rate (ex: 8000, 44100...) + * @param bits_cfg I2S bits configuration + * the low 16 bits is for data bits per sample in one channel (see 'i2s_bits_per_sample_t') + * the high 16 bits is for total bits in one channel (see 'i2s_bits_per_chan_t') + * high 16bits =0 means same as the bits per sample. + * @param ch I2S channel, (I2S_CHANNEL_MONO, I2S_CHANNEL_STEREO or specific channel in TDM mode) + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + */ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_channel_t ch) { + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num] != NULL), ESP_ERR_INVALID_ARG, TAG, "Not initialized yet"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_ARG, TAG, "I2S%d has not installed yet", i2s_num); i2s_hal_config_t *cfg = &p_i2s[i2s_num]->hal_cfg; - int data_bits = 0; - int chan_bits = 0; - int active_chan_num = 0; - int chan_num = 0; - - cfg->ch = ch; - cfg->sample_rate = rate; - cfg->bits_cfg.val = bits_cfg; - - cfg->bits_cfg.chan_bits = cfg->bits_cfg.chan_bits < cfg->bits_cfg.sample_bits ? - cfg->bits_cfg.sample_bits : cfg->bits_cfg.chan_bits; - chan_bits = cfg->bits_cfg.chan_bits; - data_bits = cfg->bits_cfg.sample_bits; - + /* If not the first time, update configuration */ + if (p_i2s[i2s_num]->last_buf_size) { + cfg->sample_rate = rate; + cfg->sample_bits = bits_cfg & 0xFFFF; + cfg->chan_bits = (bits_cfg >> 16) > cfg->sample_bits ? (bits_cfg >> 16) : cfg->sample_bits; #if SOC_I2S_SUPPORTS_TDM - cfg->chan_mask = ch & 0xFFFF; - active_chan_num = i2s_get_active_chan_num(cfg); - chan_num = cfg->total_chan; + cfg->chan_mask = ch; + cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; + cfg->active_chan = i2s_get_active_channel_num(cfg); + uint32_t max_channel = i2s_get_max_channel_num(cfg->chan_mask); + /* If total channel is smaller than max actived channel number then set it to the max active channel number */ + cfg->total_chan = p_i2s[i2s_num]->hal_cfg.total_chan < max_channel ? max_channel : p_i2s[i2s_num]->hal_cfg.total_chan; #else - active_chan_num = i2s_get_active_chan_num(cfg); - chan_num = ch == I2S_CHANNEL_MONO ? 2 : active_chan_num; + /* Default */ + cfg->chan_fmt = ch == I2S_CHANNEL_MONO ? I2S_CHANNEL_FMT_ONLY_RIGHT : cfg->chan_fmt; + cfg->active_chan = i2s_get_active_channel_num(cfg); + cfg->total_chan = 2; #endif - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - - if ((data_bits % 8 != 0) || (data_bits > I2S_BITS_PER_SAMPLE_32BIT)) { - ESP_LOGE(TAG, "Invalid bits per sample"); - return ESP_ERR_INVALID_ARG; - } - //Stop I2S - i2s_stop(i2s_num); - // wait all on-going writing finish - if ((p_i2s[i2s_num]->mode & I2S_MODE_TX) && p_i2s[i2s_num]->tx) { - xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); - } - if ((p_i2s[i2s_num]->mode & I2S_MODE_RX) && p_i2s[i2s_num]->rx) { - xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); - } - //malloc DMA buffer - if (i2s_alloc_dma_buffer(i2s_num, data_bits, active_chan_num) != ESP_OK ) { - return ESP_ERR_NO_MEM; - } - - uint32_t i2s_clk = 0; // I2S source clock - uint32_t i2s_bck = 0; // I2S back clock - uint32_t bck_div = 0; // I2S bck div - //calculate bck_div, f_bck and select source clock - if (i2s_fbclk_cal(i2s_num, rate, chan_num, chan_bits, &i2s_clk, &i2s_bck, &bck_div) != ESP_OK) { - return ESP_FAIL; - } - //configure i2s clock - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { - i2s_hal_tx_clock_config(&(p_i2s[i2s_num]->hal), i2s_clk, i2s_bck, bck_div); - i2s_hal_set_tx_sample_bit(&(p_i2s[i2s_num]->hal), chan_bits, data_bits); - // wait all writing on-going finish - if (p_i2s[i2s_num]->tx) { + if (cfg->mode & I2S_MODE_TX) { + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); xSemaphoreGive(p_i2s[i2s_num]->tx->mux); } - i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_hal_rx_clock_config(&(p_i2s[i2s_num]->hal), i2s_clk, i2s_bck, bck_div); - i2s_hal_set_rx_sample_bit(&(p_i2s[i2s_num]->hal), chan_bits, data_bits); - // wait all writing on-going finish - if (p_i2s[i2s_num]->rx) { + if (cfg->mode & I2S_MODE_RX) { + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), cfg); xSemaphoreGive(p_i2s[i2s_num]->rx->mux); } - i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); - } - // Reset message queue to avoid receiving unavailable values because the old dma queque has been destroyed - if (p_i2s[i2s_num]->tx) { - xQueueReset(p_i2s[i2s_num]->tx->queue); - } - if (p_i2s[i2s_num]->rx) { - xQueueReset(p_i2s[i2s_num]->rx->queue); } + uint32_t data_bits = cfg->sample_bits; - //I2S start + /* Check the validity of sample bits */ + ESP_RETURN_ON_FALSE((data_bits % 8 == 0), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + ESP_RETURN_ON_FALSE((data_bits <= I2S_BITS_PER_SAMPLE_32BIT), ESP_ERR_INVALID_ARG, TAG, "Invalid bits per sample"); + + /* Stop I2S */ + i2s_stop(i2s_num); + + i2s_hal_clock_cfg_t clk_cfg; + /* To get sclk, mclk, mclk_div bclk and bclk_div */ + i2s_calculate_clock(i2s_num, &clk_cfg); + + uint32_t buf_size = i2s_get_buf_size(i2s_num); + bool need_realloc = p_i2s[i2s_num]->last_buf_size != buf_size; + + /* TX mode clock reset */ + if (cfg->mode & I2S_MODE_TX) { + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->tx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); + /* Waiting for transmit finish */ + xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + i2s_tx_set_clk_and_channel(i2s_num, &clk_cfg); + /* If buffer size changed, the DMA buffer need realloc */ + if (need_realloc) { + p_i2s[i2s_num]->tx->buf_size = buf_size; + ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->tx); + } + /* If not the first time, update I2S tx channel style */ + if (p_i2s[i2s_num]->last_buf_size) { + i2s_hal_tx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + } + /* Reset the queue to avoid receive invalid data */ + xQueueReset(p_i2s[i2s_num]->tx->queue); + xSemaphoreGive(p_i2s[i2s_num]->tx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d tx DMA buffer malloc failed", i2s_num); + } + /* RX mode clock reset */ + if (cfg->mode & I2S_MODE_RX) { + ESP_RETURN_ON_FALSE(p_i2s[i2s_num]->rx, ESP_ERR_INVALID_ARG, TAG, "I2S TX DMA object has not initialized yet"); + /* Waiting for receive finish */ + xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + i2s_rx_set_clk_and_channel(i2s_num, &clk_cfg); + /* If buffer size changed, the DMA buffer need realloc */ + if (need_realloc) { + p_i2s[i2s_num]->rx->buf_size = buf_size; + ret = i2s_realloc_dma_buffer(i2s_num, p_i2s[i2s_num]->rx); + /* Reset the end-of-frame number */ + i2s_hal_set_rx_eof_num(&(p_i2s[i2s_num]->hal), buf_size); + } + /* If not the first time, update I2S rx channel style */ + if (p_i2s[i2s_num]->last_buf_size) { + i2s_hal_rx_set_channel_style(&(p_i2s[i2s_num]->hal), &(p_i2s[i2s_num]->hal_cfg)); + } + /* Reset the queue to avoid receiving invalid data */ + xQueueReset(p_i2s[i2s_num]->rx->queue); + xSemaphoreGive(p_i2s[i2s_num]->rx->mux); + ESP_RETURN_ON_ERROR(ret, TAG, "I2S%d rx DMA buffer malloc failed", i2s_num); + } + /* Update last buffer size */ + p_i2s[i2s_num]->last_buf_size = buf_size; + + /* I2S start */ i2s_start(i2s_num); - p_i2s[i2s_num]->sample_rate = rate; + return ESP_OK; } - - -/************************************************************** - * I2S driver operation * - * - i2s_start * - * - i2s_stop * - * - i2s_driver_install * - * - i2s_write * - * - i2s_write_expand * - * - i2s_read * - **************************************************************/ - +/*------------------------------------------------------------- + I2S driver operation + -------------------------------------------------------------*/ +/** + * @brief Start I2S driver + * @note It is not necessary to call this function after i2s_driver_install() (it is started automatically), however it is necessary to call it after i2s_stop(). + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_start(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); //start DMA link I2S_ENTER_CRITICAL(i2s_num); - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_tx_reset(i2s_num); i2s_tx_start(i2s_num); } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_rx_reset(i2s_num); i2s_rx_start(i2s_num); } @@ -1155,6 +1761,17 @@ esp_err_t i2s_start(i2s_port_t i2s_num) return ESP_OK; } +/** + * @brief Stop I2S driver + * @note There is no need to call i2s_stop() before calling i2s_driver_uninstall(). + * Disables I2S TX/RX, until i2s_start() is called. + * + * @param i2s_num I2S device number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_stop(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); @@ -1162,10 +1779,10 @@ esp_err_t i2s_stop(i2s_port_t i2s_num) #if !SOC_GDMA_SUPPORTED esp_intr_disable(p_i2s[i2s_num]->i2s_isr_handle); #endif - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { i2s_tx_stop(i2s_num); } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { i2s_rx_stop(i2s_num); } #if !SOC_GDMA_SUPPORTED @@ -1175,157 +1792,214 @@ esp_err_t i2s_stop(i2s_port_t i2s_num) return ESP_OK; } +/** + * @brief Initialize I2S driver configurations + * + * @param i2s_num I2S device number + * @param i2s_config I2S configurations - see i2s_config_t struct + * @return + * - ESP_OK I2S initialize success + * - ESP_ERR_INVALID_ARG No channel enabled in multiple channel format + */ +static esp_err_t i2s_driver_init(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "The pointer of I2S configuration structure is NULL"); + + /* I2S driver configuration assignment */ + p_i2s[i2s_num]->i2s_num = i2s_num; + p_i2s[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s[i2s_num]->last_buf_size = 0; + p_i2s[i2s_num]->use_apll = i2s_config->use_apll; + p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; + p_i2s[i2s_num]->mclk_multiple = i2s_config->mclk_multiple; + p_i2s[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; + + /* I2S HAL configuration assignment */ + p_i2s[i2s_num]->hal_cfg.mode = i2s_config->mode; + p_i2s[i2s_num]->hal_cfg.sample_rate = i2s_config->sample_rate; + p_i2s[i2s_num]->hal_cfg.comm_fmt = i2s_config->communication_format; + p_i2s[i2s_num]->hal_cfg.chan_fmt = i2s_config->channel_format; + p_i2s[i2s_num]->hal_cfg.sample_bits = i2s_config->bits_per_sample; + p_i2s[i2s_num]->hal_cfg.chan_bits = (uint32_t)i2s_config->bits_per_chan < (uint32_t)i2s_config->bits_per_sample ? + (uint32_t)i2s_config->bits_per_sample : (uint32_t)i2s_config->bits_per_chan; + +#if SOC_I2S_SUPPORTS_TDM + /* I2S HAL TDM configuration assignment */ + p_i2s[i2s_num]->hal_cfg.left_align = i2s_config->left_align; + p_i2s[i2s_num]->hal_cfg.big_edin = i2s_config->big_edin; + p_i2s[i2s_num]->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; + p_i2s[i2s_num]->hal_cfg.skip_msk = i2s_config->skip_msk; + + /* Set chan_mask according to channel format */ + switch (i2s_config->channel_format) { + case I2S_CHANNEL_FMT_RIGHT_LEFT: // fall through + case I2S_CHANNEL_FMT_ALL_RIGHT: // fall through + case I2S_CHANNEL_FMT_ALL_LEFT: + p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; + p_i2s[i2s_num]->hal_cfg.total_chan = 2; + break; + case I2S_CHANNEL_FMT_ONLY_RIGHT: // fall through + case I2S_CHANNEL_FMT_ONLY_LEFT: + p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0; + p_i2s[i2s_num]->hal_cfg.total_chan = 2; + break; + case I2S_CHANNEL_FMT_MULTIPLE: + ESP_RETURN_ON_FALSE(i2s_config->chan_mask, ESP_ERR_INVALID_ARG, TAG, "i2s all channel are disabled"); + p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->chan_mask; + /* Get the max actived channel number */ + uint32_t max_channel = i2s_get_max_channel_num(p_i2s[i2s_num]->hal_cfg.chan_mask); + /* If total channel is smaller than max actived channel number then set it to the max active channel number */ + p_i2s[i2s_num]->hal_cfg.total_chan = p_i2s[i2s_num]->hal_cfg.total_chan < max_channel ? max_channel : + p_i2s[i2s_num]->hal_cfg.total_chan; + break; + default: + ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "wrong i2s channel format, going to uninstall i2s"); + } + + /* Calculate actived channel number in channel mask */ + p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); + +#else + /* Calculate actived channel number in channel mask */ + p_i2s[i2s_num]->hal_cfg.active_chan = i2s_get_active_channel_num(&p_i2s[i2s_num]->hal_cfg); + /* Total channel number is equal to the actived channel number in non-TDM mode */ + p_i2s[i2s_num]->hal_cfg.total_chan = 2; +#endif + return ESP_OK; +} + +/** + * @brief Initialize I2S DMA object + * + * @param i2s_num I2S device number + * @return + * - ESP_OK DMA object initialize success + * - ESP_ERR_NO_MEM No memory for DMA object + */ +static esp_err_t i2s_dma_object_init(i2s_port_t i2s_num) +{ + uint32_t buf_size = i2s_get_buf_size(i2s_num); + /* Create DMA object */ + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_TX) { + ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->tx), TAG, "I2S TX DMA object create failed"); + p_i2s[i2s_num]->tx->buf_size = buf_size; + } + if (p_i2s[i2s_num]->hal_cfg.mode & I2S_MODE_RX) { + ESP_RETURN_ON_ERROR(i2s_create_dma_object(i2s_num, &p_i2s[i2s_num]->rx), TAG, "I2S RX DMA object create failed"); + p_i2s[i2s_num]->rx->buf_size = buf_size; + } + return ESP_OK; +} + +/** + * @brief Install and start I2S driver. + * @note This function must be called before any I2S driver read/write operations. + * + * + * @param i2s_num I2S device number + * @param i2s_config I2S configurations - see i2s_config_t struct + * @param queue_size I2S event queue size/depth. + * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Out of memory + * - ESP_ERR_INVALID_STATE Current I2S port is in use + */ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue) { - esp_err_t ret = ESP_FAIL; - i2s_obj_t *pre_alloc_i2s_obj = NULL; + esp_err_t ret = ESP_OK; + + /* Step 1: Check the validity of input parameters */ + /* Check the validity of i2s device number */ ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((i2s_config != NULL), ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not NULL"); + ESP_RETURN_ON_FALSE(i2s_config, ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not be NULL"); + /* Check the size of DMA buffer */ ESP_RETURN_ON_FALSE((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), ESP_ERR_INVALID_ARG, TAG, "I2S buffer count less than 128 and more than 2"); ESP_RETURN_ON_FALSE((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), ESP_ERR_INVALID_ARG, TAG, "I2S buffer length at most 1024 and more than 8"); - // alloc driver object and register to platform - pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); + /* Step 2: Allocate driver object and register to platform */ + i2s_obj_t *pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); ESP_RETURN_ON_FALSE(pre_alloc_i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver"); ret = i2s_priv_register_object(pre_alloc_i2s_obj, i2s_num); if (ret != ESP_OK) { free(pre_alloc_i2s_obj); ESP_LOGE(TAG, "register I2S object to platform failed"); - return ret; + return ESP_ERR_INVALID_STATE; } - // initialize HAL layer + + /* Step 3: Initialize I2S object, assign configarations */ + ESP_GOTO_ON_ERROR(i2s_driver_init(i2s_num, i2s_config), err, TAG, "I2S init failed"); + /* Check the validity of I2S configuration */ + ESP_GOTO_ON_ERROR(i2s_check_cfg_validity(i2s_num, &(pre_alloc_i2s_obj->hal_cfg)), err, TAG, "I2S configuration is invalid"); + + /* Get device instance */ i2s_hal_init(&(pre_alloc_i2s_obj->hal), i2s_num); - // Set I2S HAL configurations - pre_alloc_i2s_obj->hal_cfg.mode = i2s_config->mode; - pre_alloc_i2s_obj->hal_cfg.sample_rate = i2s_config->sample_rate; - pre_alloc_i2s_obj->hal_cfg.comm_fmt = i2s_config->communication_format; - pre_alloc_i2s_obj->hal_cfg.chan_fmt = i2s_config->channel_format; - pre_alloc_i2s_obj->hal_cfg.bits_cfg.sample_bits = i2s_config->bits_per_sample; - pre_alloc_i2s_obj->hal_cfg.bits_cfg.chan_bits = i2s_config->bits_per_chan; -#if SOC_I2S_SUPPORTS_TDM - int active_chan = 0; - switch (i2s_config->channel_format) { - case I2S_CHANNEL_FMT_RIGHT_LEFT: - case I2S_CHANNEL_FMT_ALL_RIGHT: - case I2S_CHANNEL_FMT_ALL_LEFT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; - pre_alloc_i2s_obj->hal_cfg.total_chan = 2; - active_chan = 2; - break; - case I2S_CHANNEL_FMT_ONLY_RIGHT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; - pre_alloc_i2s_obj->hal_cfg.total_chan = 1; - active_chan = 1; - break; - case I2S_CHANNEL_FMT_ONLY_LEFT: - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; - pre_alloc_i2s_obj->hal_cfg.total_chan = 1; - active_chan = 1; - break; - case I2S_CHANNEL_FMT_MULTIPLE: - ESP_GOTO_ON_FALSE(i2s_config->chan_mask != 0, ESP_ERR_INVALID_ARG, err, TAG, "i2s all channel are disabled"); - pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->chan_mask; - i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); - break; - default: - ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid I2S channel format:%d", i2s_config->channel_format); - } - pre_alloc_i2s_obj->hal_cfg.left_align = i2s_config->left_align; - pre_alloc_i2s_obj->hal_cfg.big_edin = i2s_config->big_edin; - pre_alloc_i2s_obj->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; - pre_alloc_i2s_obj->hal_cfg.skip_msk = i2s_config->skip_msk; -#endif - - // Set I2S driver configurations - pre_alloc_i2s_obj->i2s_num = i2s_num; - pre_alloc_i2s_obj->mode = i2s_config->mode; - pre_alloc_i2s_obj->channel_num = i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); - pre_alloc_i2s_obj->i2s_queue = i2s_queue; - pre_alloc_i2s_obj->bits_per_sample = 0; - pre_alloc_i2s_obj->bytes_per_sample = 0; // Not initialized yet - pre_alloc_i2s_obj->dma_buf_count = i2s_config->dma_buf_count; - pre_alloc_i2s_obj->dma_buf_len = i2s_config->dma_buf_len; - pre_alloc_i2s_obj->mclk_multiple = i2s_config->mclk_multiple; - #ifdef CONFIG_PM_ENABLE + esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX; #if SOC_I2S_SUPPORTS_APLL if (i2s_config->use_apll) { - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); - } else + pm_lock = ESP_PM_NO_LIGHT_SLEEP; + } #endif // SOC_I2S_SUPPORTS_APLL - { - ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); - } - ESP_GOTO_ON_ERROR(ret, err, TAG, "create PM lock failed"); + ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock), err, TAG, "I2S pm lock error"); #endif //CONFIG_PM_ENABLE -#if SOC_GDMA_SUPPORTED - ret = ESP_OK; - gdma_trigger_t trig = {.periph = GDMA_TRIG_PERIPH_I2S}; -#if SOC_I2S_NUM > 1 - trig.instance_id = (i2s_num == I2S_NUM_0) ? SOC_GDMA_TRIG_PERIPH_I2S0 : SOC_GDMA_TRIG_PERIPH_I2S1; -#else - trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0; + + /* Step 4: Initialize I2S DMA interrupt and DMA object */ + ESP_GOTO_ON_ERROR(i2s_dma_intr_init(i2s_num, i2s_config->intr_alloc_flags), err, TAG, "I2S interrupt initailze failed"); + /* Initialize I2S DMA object */ + ESP_GOTO_ON_ERROR(i2s_dma_object_init(i2s_num), err, TAG, "I2S dma object create failed"); + +#if SOC_I2S_SUPPORTS_ADC + /* If using built-in ADC, we need to enable ADC power manerge*/ + if (pre_alloc_i2s_obj->hal_cfg.mode & I2S_MODE_ADC_BUILT_IN) { + adc_power_acquire(); + } #endif - gdma_channel_alloc_config_t dma_cfg = {.flags.reserve_sibling = 1}; - if (pre_alloc_i2s_obj->mode & I2S_MODE_RX) { - dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->rx_dma_chan), err, TAG, "Register rx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->rx_dma_chan, trig), err, TAG, "Connect rx dma channel error"); - gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback}; - gdma_register_rx_event_callbacks(pre_alloc_i2s_obj->rx_dma_chan, &cb, pre_alloc_i2s_obj); - } - if (pre_alloc_i2s_obj->mode & I2S_MODE_TX) { - dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->tx_dma_chan), err, TAG, "Register tx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->tx_dma_chan, trig), err, TAG, "Connect tx dma channel error"); - gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback}; - gdma_register_tx_event_callbacks(pre_alloc_i2s_obj->tx_dma_chan, &cb, pre_alloc_i2s_obj); - } -#else - //initial interrupt - ret = esp_intr_alloc(i2s_periph_signal[i2s_num].irq, i2s_config->intr_alloc_flags, i2s_intr_handler_default, pre_alloc_i2s_obj, &pre_alloc_i2s_obj->i2s_isr_handle); - ESP_GOTO_ON_ERROR(ret, err, TAG, "Register I2S Interrupt error"); -#endif // SOC_GDMA_SUPPORTED - i2s_stop(i2s_num); - pre_alloc_i2s_obj->use_apll = i2s_config->use_apll; - pre_alloc_i2s_obj->fixed_mclk = i2s_config->fixed_mclk; - pre_alloc_i2s_obj->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; - ret = i2s_param_config(i2s_num); - ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S param configure error"); + /* Enable module clock */ + i2s_hal_enable_module_clock(&p_i2s[i2s_num]->hal); + + /* Step 5: Initialize I2S configuration and set the configurations to register */ + i2s_hal_config_param(&(pre_alloc_i2s_obj->hal), &pre_alloc_i2s_obj->hal_cfg); + + /* Step 6: Initialise i2s event queue if user needs */ if (i2s_queue) { pre_alloc_i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - ESP_GOTO_ON_ERROR((pre_alloc_i2s_obj->i2s_queue != NULL), err, TAG, "I2S queue create failed"); + ESP_GOTO_ON_FALSE(pre_alloc_i2s_obj->i2s_queue, ESP_ERR_NO_MEM, err, TAG, "I2S queue create failed"); *((QueueHandle_t *) i2s_queue) = pre_alloc_i2s_obj->i2s_queue; ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(pre_alloc_i2s_obj->i2s_queue)); } else { pre_alloc_i2s_obj->i2s_queue = NULL; } - //set clock and start -#if SOC_I2S_SUPPORTS_TDM - ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, - (i2s_channel_t)active_chan); -#else - ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, - I2S_CHANNEL_STEREO); -#endif - ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S set clock failed"); - return ret; + /* Step 7: Set I2S clocks and start. No need to give parameters since configurations has been set in 'i2s_driver_init' */ + ESP_GOTO_ON_ERROR(i2s_set_clk(i2s_num, 0, 0, 0), err, TAG, "I2S set clock failed"); + return ESP_OK; + err: + /* I2S install failed, prepare to uninstall */ i2s_driver_uninstall(i2s_num); return ret; } +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S device number + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) { ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num); i2s_obj_t *obj = p_i2s[i2s_num]; i2s_stop(i2s_num); -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_DAC i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); #endif #if SOC_GDMA_SUPPORTED @@ -1342,14 +2016,9 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) esp_intr_free(p_i2s[i2s_num]->i2s_isr_handle); } #endif - if (p_i2s[i2s_num]->tx != NULL && p_i2s[i2s_num]->mode & I2S_MODE_TX) { - i2s_destroy_dma_queue(i2s_num, p_i2s[i2s_num]->tx); - p_i2s[i2s_num]->tx = NULL; - } - if (p_i2s[i2s_num]->rx != NULL && p_i2s[i2s_num]->mode & I2S_MODE_RX) { - i2s_destroy_dma_queue(i2s_num, p_i2s[i2s_num]->rx); - p_i2s[i2s_num]->rx = NULL; - } + /* Destroy dma object if exist */ + i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->tx); + i2s_destroy_dma_object(i2s_num, &p_i2s[i2s_num]->rx); if (p_i2s[i2s_num]->i2s_queue) { vQueueDelete(p_i2s[i2s_num]->i2s_queue); @@ -1367,20 +2036,41 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) #ifdef CONFIG_PM_ENABLE if (p_i2s[i2s_num]->pm_lock) { esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); + p_i2s[i2s_num]->pm_lock = NULL; } #endif + /* Disable module clock */ + i2s_hal_disable_module_clock(&p_i2s[i2s_num]->hal); i2s_priv_deregister_object(i2s_num); free(obj); return ESP_OK; } +/** + * @brief Write data to I2S DMA transmit buffer. + * @note Many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * + * @param i2s_num I2S device number + * @param src Source address to write from + * @param size Size of data in bytes + * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait) { + esp_err_t ret = ESP_OK; char *data_ptr, *src_byte; size_t bytes_can_write; *bytes_written = 0; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "tx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled"); xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); @@ -1389,6 +2079,7 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by while (size > 0) { if (p_i2s[i2s_num]->tx->rw_pos == p_i2s[i2s_num]->tx->buf_size || p_i2s[i2s_num]->tx->curr_ptr == NULL) { if (xQueueReceive(p_i2s[i2s_num]->tx->queue, &p_i2s[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + ret = ESP_ERR_TIMEOUT; break; } p_i2s[i2s_num]->tx->rw_pos = 0; @@ -1409,13 +2100,33 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by #ifdef CONFIG_PM_ENABLE esp_pm_lock_release(p_i2s[i2s_num]->pm_lock); #endif - xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - return ESP_OK; + return ret; } +/** + * @brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. + * @note Many ticks pass without space becoming available in the DMA + * transmit buffer, then the function will return (note that if the + * data is written to the DMA buffer in pieces, the overall operation + * may still take longer than this timeout.) Pass portMAX_DELAY for no + * timeout. + * Format of the data in source buffer is determined by the I2S configuration (see i2s_config_t). + * + * @param i2s_num I2S device number + * @param src Source address to write from + * @param size Size of data in bytes + * @param src_bits Source audio bit + * @param aim_bits Bit wanted, no more than 32, and must be greater than src_bits + * @param[out] bytes_written Number of bytes written, if timeout, the result will be less than the size passed in. + * @param ticks_to_wait TX buffer wait timeout in RTOS ticks. If this + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait) { + esp_err_t ret = ESP_OK; char *data_ptr; int bytes_can_write, tail; int src_bytes, aim_bytes, zero_bytes; @@ -1423,7 +2134,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((size > 0), ESP_ERR_INVALID_ARG, TAG, "size must greater than zero"); ESP_RETURN_ON_FALSE((aim_bits >= src_bits), ESP_ERR_INVALID_ARG, TAG, "aim_bits mustn't be less than src_bits"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "tx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled"); if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) { ESP_LOGE(TAG, "bits mustn't be less than 8, src_bits %d aim_bits %d", src_bits, aim_bits); return ESP_ERR_INVALID_ARG; @@ -1450,6 +2161,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz while (size > 0) { if (p_i2s[i2s_num]->tx->rw_pos == p_i2s[i2s_num]->tx->buf_size || p_i2s[i2s_num]->tx->curr_ptr == NULL) { if (xQueueReceive(p_i2s[i2s_num]->tx->queue, &p_i2s[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + ret = ESP_ERR_TIMEOUT; break; } p_i2s[i2s_num]->tx->rw_pos = 0; @@ -1473,9 +2185,23 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz p_i2s[i2s_num]->tx->rw_pos += bytes_can_write; } xSemaphoreGive(p_i2s[i2s_num]->tx->mux); - return ESP_OK; + return ret; } +/** + * @brief Read data from I2S DMA receive buffer + * @note If the built-in ADC mode is enabled, we should call i2s_adc_enable and i2s_adc_disable around the whole reading process, + * to prevent the data getting corrupted. + * + * @param i2s_num I2S device number + * @param dest Destination address to read into + * @param size Size of data in bytes + * @param[out] bytes_read Number of bytes read, if timeout, bytes read will be less than the size passed in. + * @param ticks_to_wait RX buffer wait timeout in RTOS ticks. If this many ticks pass without bytes becoming available in the DMA receive buffer, then the function will return (note that if data is read from the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_read, TickType_t ticks_to_wait) { esp_err_t ret = ESP_OK; @@ -1484,7 +2210,7 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re *bytes_read = 0; dest_byte = (char *)dest; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "rx NULL"); + ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->rx), ESP_ERR_INVALID_ARG, TAG, "RX mode is not enabled"); xSemaphoreTake(p_i2s[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); #ifdef CONFIG_PM_ENABLE esp_pm_lock_acquire(p_i2s[i2s_num]->pm_lock); diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index 0c8a43db0d..aa649ed479 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -17,7 +17,7 @@ #include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC #include "driver/adc.h" #endif @@ -221,7 +221,7 @@ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_NO_MEM Out of memory - * - ESP_ERR_NOT_FOUND I2S port is not found or has been installed by others (e.g. LCD i80) + * - ESP_ERR_INVALID_STATE Current I2S port is in use */ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue); @@ -355,7 +355,7 @@ esp_err_t i2s_stop(i2s_port_t i2s_num); * * @param i2s_num I2S port number * -* @return + * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ @@ -430,7 +430,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ */ float i2s_get_clk(i2s_port_t i2s_num); -#if SOC_I2S_SUPPORTS_ADC_DAC +#if SOC_I2S_SUPPORTS_ADC /** * @brief Set built-in ADC mode for I2S DMA, this function will initialize ADC pad, * and set ADC parameters. @@ -466,7 +466,9 @@ esp_err_t i2s_adc_enable(i2s_port_t i2s_num); * - ESP_ERR_INVALID_STATE Driver state error */ esp_err_t i2s_adc_disable(i2s_port_t i2s_num); +#endif // SOC_I2S_SUPPORTS_ADC +#if SOC_I2S_SUPPORTS_DAC /** * @brief Set I2S dac mode, I2S built-in DAC is disabled by default * @@ -481,7 +483,7 @@ esp_err_t i2s_adc_disable(i2s_port_t i2s_num); * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode); -#endif //SOC_I2S_SUPPORTS_ADC_DAC +#endif //SOC_I2S_SUPPORTS_DAC #ifdef __cplusplus diff --git a/components/driver/include/driver/mcpwm.h b/components/driver/include/driver/mcpwm.h index db8816069d..2a4e825f77 100644 --- a/components/driver/include/driver/mcpwm.h +++ b/components/driver/include/driver/mcpwm.h @@ -130,16 +130,35 @@ typedef enum { * @brief MCPWM select sync signal input */ typedef enum { - MCPWM_SELECT_SYNC0 = 4, /*!