mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-04 06:06:45 +02:00
Compare commits
197 Commits
sock_utils
...
eppp-v0.3.
Author | SHA1 | Date | |
---|---|---|---|
a74b01e7e3 | |||
c5a4a51d82 | |||
2ff150c310 | |||
b8cdd373f4 | |||
66a5ac0190 | |||
40cce5e926 | |||
1b1ede435d | |||
c6f08ee961 | |||
a21ce883ef | |||
79d6fa2607 | |||
ad90558420 | |||
8541753443 | |||
6660f71d03 | |||
57cd60807e | |||
03dd8006b2 | |||
3863c26b79 | |||
1d1ff365c2 | |||
ef3870573c | |||
ad94cc9502 | |||
4885d28294 | |||
df00a03084 | |||
e2fa11103c | |||
d2e94e5db2 | |||
bc8ac4c684 | |||
b710dbd2ad | |||
5eeb29cb94 | |||
a00117fbdb | |||
6ed672da7b | |||
1b4baaed0e | |||
74b7d85d4a | |||
317faf89ff | |||
b97dfc08b8 | |||
e9d7350219 | |||
61da30b013 | |||
8fd2c99f15 | |||
8560f02191 | |||
703c49297f | |||
2fe8345fa5 | |||
3fc26a5e5c | |||
5db6be7422 | |||
8ca45f34fa | |||
2e28774228 | |||
e14e21ab32 | |||
6f00c369fc | |||
905b84fb97 | |||
d7f5322a39 | |||
eb12d05bf3 | |||
520b8194fc | |||
40142cdcfd | |||
c7663cdef2 | |||
e01e67e7eb | |||
37f84ee5a4 | |||
95b56600ed | |||
a22c3da49e | |||
5d0fd5c1c5 | |||
7ea6879a19 | |||
2db11bbb8c | |||
cdb7bfd188 | |||
c679ceed85 | |||
c009892968 | |||
5f66f35f5f | |||
dea5f1c431 | |||
384d1c23ba | |||
88162d1f3a | |||
e65f22ab6c | |||
7d29b47676 | |||
f1a72ec42c | |||
ce160505dc | |||
87e96b4682 | |||
5b2077e373 | |||
030cb75ece | |||
b45fe143a4 | |||
ee2fbbbee7 | |||
cb061c9c38 | |||
0690eba3a8 | |||
eb4ab52487 | |||
8fcad10ccf | |||
936e43f9d8 | |||
64d818b2d3 | |||
cd07228f81 | |||
1c6580e22b | |||
87f835af0f | |||
85a8dac42d | |||
39866116f5 | |||
7740b591b6 | |||
b57979d967 | |||
42674b49f9 | |||
e069ae7762 | |||
44d476fc50 | |||
55385ec312 | |||
a3c2bbed9e | |||
84c47c37f1 | |||
e7273c46ec | |||
2e7d240abd | |||
d4a004b5b4 | |||
9046af8f8d | |||
eeeb9006eb | |||
b167aa315f | |||
72ba24470d | |||
7dc87d28b2 | |||
ac6a388cdd | |||
76aaea08d2 | |||
5db32cce30 | |||
9bdd429c7c | |||
6f7c52cc3f | |||
27435b7f34 | |||
813331f003 | |||
bd23c233a4 | |||
4eda7d472f | |||
163029c0b6 | |||
96eae25096 | |||
ebec8eff63 | |||
4451a8c5ad | |||
6d19aabb02 | |||
9162de1150 | |||
dbd164dd91 | |||
90d663ad01 | |||
a83f1b6787 | |||
84caca465d | |||
8f81478fff | |||
ae5a8ceeda | |||
774bab22ea | |||
265e38d684 | |||
93f772171c | |||
4ad88e297f | |||
196198ecc9 | |||
e838bf03f4 | |||
99b54ac384 | |||
f5be2f4115 | |||
9b74256b51 | |||
3d8835cfb9 | |||
24f55ce9b4 | |||
8f8516cc3f | |||
75a8e8640a | |||
907087c09b | |||
68a9e14898 | |||
827ea65fd5 | |||
9537721600 | |||
4394f845fc | |||
9b0ba6060f | |||
7c6a3098af | |||
f3f3e23bec | |||
9ed835ba3f | |||
f12a205657 | |||
5bd82c01a5 | |||
b4cb8f8a66 | |||
0499ed93df | |||
ade9448c01 | |||
4745fc8fe1 | |||
73e523e736 | |||
3cd0ed377b | |||
95294f5f89 | |||
5dcc33300f | |||
840a561de4 | |||
e6fb8aa078 | |||
3b2c614d86 | |||
cdeab8f517 | |||
6cce87e465 | |||
d57b8c5b29 | |||
9c11003449 | |||
85a7fc772c | |||
b090a3cb69 | |||
42cde46c97 | |||
beb6e57e5e | |||
15d3a01e11 | |||
e12ecb8e89 | |||
54271a1b96 | |||
886215032f | |||
269351f41c | |||
5e929902c7 | |||
f7c0b7564a | |||
c989c6adae | |||
18f196fa1e | |||
1db83cd1ca | |||
247f1681e8 | |||
32387f7e39 | |||
dbc3ea6809 | |||
8b6ea3311a | |||
8e55b93b59 | |||
0cb59ff80d | |||
1284f66d58 | |||
c5b49de2db | |||
2e9bb6ee45 | |||
1fcc5b1d56 | |||
849fe7b6cb | |||
5eadf1edee | |||
428fdbbd80 | |||
433a033fcc | |||
090b1ff845 | |||
295d99df96 | |||
b65cff3a0b | |||
e711f26670 | |||
4a1cb65c67 | |||
6c61dd39cf | |||
8821ea3a99 | |||
062b8dcacc | |||
7de57bb412 |
@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
idf_target: ["esp32", "esp32s2"]
|
||||
example: ["asio_chat", "async_request", "socks4", "ssl_client_server", "tcp_echo_server", "udp_echo_server"]
|
||||
runs-on: ubuntu-22.04
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
name: Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
idf_target: ["esp32"]
|
||||
example: ["asio_chat", "tcp_echo_server", "udp_echo_server", "ssl_client_server"]
|
||||
needs: build_asio
|
||||
|
9
.github/workflows/clang-tidy.yml
vendored
9
.github/workflows/clang-tidy.yml
vendored
@ -9,12 +9,12 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Run clang-tidy
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: recursive
|
||||
- name: Install esp-clang
|
||||
run: |
|
||||
${IDF_PATH}/tools/idf_tools.py --non-interactive install esp-clang
|
||||
@ -24,6 +24,7 @@ jobs:
|
||||
chmod +x clang-tidy-sarif
|
||||
curl -sSL https://raw.githubusercontent.com/espressif/idf-extra-components/master/.github/filter_sarif.py -o filter_sarif.py
|
||||
- name: Install pyclang
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install pyclang~=0.2.0
|
||||
@ -49,7 +50,7 @@ jobs:
|
||||
results.sarif
|
||||
results.sarif.raw
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: clang-tidy
|
||||
|
32
.github/workflows/console_cmd_mqtt__build.yml
vendored
Normal file
32
.github/workflows/console_cmd_mqtt__build.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: "console_cmd_mqtt: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_console_cmd_mqtt:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'console') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: mqtt_ssl_auth_console, path: "components/console_cmd_mqtt/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: ${{matrix.test.path}}
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
6
.github/workflows/eppp__build.yml
vendored
6
.github/workflows/eppp__build.yml
vendored
@ -13,14 +13,16 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3"]
|
||||
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: "DeprecationWarning: 'MultiCommand' is deprecated and will be removed\nCryptographyDeprecationWarning: Parsed a serial number which wasn't positive"
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
|
38
.github/workflows/esp_dns__build.yml
vendored
Normal file
38
.github/workflows/esp_dns__build.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: "esp_dns: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_esp_dns:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'dns') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: esp_dns_basic, path: "components/esp_dns/examples"}]
|
||||
include:
|
||||
- idf_ver: "latest"
|
||||
warning: "the choice symbol ETHERNET_PHY_LAN867X\nis deprecated: Please use smi_gpio instead"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
shell: bash
|
||||
working-directory: ${{matrix.test.path}}
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
python ../../../ci/build_apps.py ./${{ matrix.test.app }} --target ${{ matrix.idf_target }} -vv --preserve-all --pytest-app
|
50
.github/workflows/examples_build-host-test.yml
vendored
50
.github/workflows/examples_build-host-test.yml
vendored
@ -13,12 +13,15 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
include:
|
||||
- idf_ver: "latest"
|
||||
warning: "Warning: The smallest app partition is nearly full"
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TARGET_TEST: examples/esp_netif/slip_custom_netif/
|
||||
TARGET_TEST_DIR: build_esp32c3_target
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
@ -31,6 +34,16 @@ jobs:
|
||||
python -m pip install idf-build-apps
|
||||
# Build default configs for all targets
|
||||
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
|
||||
# Build target tests
|
||||
python ./ci/build_apps.py ${TARGET_TEST} -r sdkconfig.ci=target
|
||||
cd ${TARGET_TEST}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
zip -qur artifacts.zip ${TARGET_TEST_DIR}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: slip_target_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TARGET_TEST }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build_and_run_on_host:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
|
||||
@ -51,3 +64,38 @@ jobs:
|
||||
python ./ci/build_apps.py examples/mqtt -l -t linux
|
||||
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
|
||||
grep 'MQTT_EVENT_DATA' test.log
|
||||
|
||||
run_on_target:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push' )
|
||||
name: Slip example target test
|
||||
needs: build_all_examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4", "latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- modem
|
||||
env:
|
||||
TARGET_TEST: examples/esp_netif/slip_custom_netif/
|
||||
TARGET_TEST_DIR: build_esp32c3_target
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: slip_target_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TARGET_TEST }}/ci/
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TARGET_TEST }}
|
||||
run: |
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool netifaces
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
mv $dir build
|
||||
python -m pytest --log-cli-level DEBUG --target=esp32c3
|
||||
done
|
||||
|
86
.github/workflows/lws_build.yml
vendored
Normal file
86
.github/workflows/lws_build.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
name: "lws: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_lws:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push'
|
||||
name: Libwebsockets build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps
|
||||
python ./ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
for dir in `ls -d build_esp32_*`; do
|
||||
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
|
||||
zip -qur artifacts.zip $dir
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
run-target-lws:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push' )
|
||||
name: Target test
|
||||
needs: build_lws
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
env:
|
||||
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
- name: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
mv $dir build
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
|
||||
path: components/libwebsockets/${{ matrix.test.path }}/*.xml
|
24
.github/workflows/mdns__host-tests.yml
vendored
24
.github/workflows/mdns__host-tests.yml
vendored
@ -35,30 +35,36 @@ jobs:
|
||||
# Next we run the pytest (using the console app)
|
||||
pytest
|
||||
|
||||
build_afl_host_test_mdns:
|
||||
host_compat_checks:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Build AFL host test
|
||||
name: Set of compatibility checks
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Install Necessary Libs
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
- name: Test AFL compat build
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_afl_fuzz_host/
|
||||
cd components/mdns/tests/test_afl_fuzz_host/
|
||||
make INSTR=off
|
||||
- name: Test no malloc functions
|
||||
shell: bash
|
||||
run: |
|
||||
cd components/mdns
|
||||
for file in $(ls *.c); do
|
||||
cp $file /tmp
|
||||
echo -n "Checking that $file does not call any std allocations directly..."
|
||||
python mem_prefix_script.py $file
|
||||
diff -q $file /tmp/$file || exit 1
|
||||
echo "OK"
|
||||
done
|
||||
|
33
.github/workflows/modem__build-host-tests.yml
vendored
33
.github/workflows/modem__build-host-tests.yml
vendored
@ -13,11 +13,8 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v4.4", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
|
||||
exclude:
|
||||
- idf_ver: "release-v4.4"
|
||||
example: modem_tcp_client
|
||||
include:
|
||||
- idf_ver: "release-v5.0"
|
||||
example: "simple_cmux_client"
|
||||
@ -26,13 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Check out code (v3) # @v4 failed due to Node 20's requirement, incompatible with older IDF versions
|
||||
if: matrix.idf_ver != 'latest' && matrix.idf_ver < 'release-v5.0'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: protocols
|
||||
- name: Check out code (v4)
|
||||
if: matrix.idf_ver == 'latest' || matrix.idf_ver >= 'release-v5.0'
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: protocols
|
||||
@ -53,7 +44,7 @@ jobs:
|
||||
name: Build tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "latest"]
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
|
||||
test: ["target", "target_ota", "target_iperf"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
@ -100,3 +91,21 @@ jobs:
|
||||
run_executable: false
|
||||
run_coverage: false
|
||||
pre_run_script: "esp-protocols/components/esp_modem/test/host_test/env.sh"
|
||||
|
||||
esp_modem_generated_commands:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem') || github.event_name == 'push'
|
||||
name: Generated commands compatibility
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
- name: Compat check
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y astyle
|
||||
cd components/esp_modem
|
||||
find examples/ -type f -regex '.*/generate/.*\.\(hpp\|cpp\)' -exec ./scripts/generate.sh {} \;
|
||||
./scripts/generate.sh
|
||||
git diff --name-only
|
||||
git diff --quiet
|
||||
|
13
.github/workflows/modem__target-test.yml
vendored
13
.github/workflows/modem__target-test.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
idf.py build
|
||||
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/build
|
||||
@ -75,12 +75,17 @@ jobs:
|
||||
run: |
|
||||
sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/build
|
||||
- name: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
|
||||
run: |
|
||||
python -m pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
|
||||
pip install -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
cd ${{ env.TEST_DIR }}
|
||||
python -m pytest --log-cli-level DEBUG --target=${{ matrix.idf_target }}
|
||||
|
115
.github/workflows/mosq__build.yml
vendored
115
.github/workflows/mosq__build.yml
vendored
@ -17,7 +17,8 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/mosquitto/examples/broker
|
||||
TEST_DIR: components/mosquitto/examples
|
||||
TARGET_TEST: broker
|
||||
TARGET_TEST_DIR: build_esp32_default
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
@ -29,14 +30,15 @@ jobs:
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
python ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
python ci/build_apps.py -c ${TEST_DIR} -m components/mosquitto/.build-test-rules.yml
|
||||
# upload only the target test artifacts
|
||||
cd ${TEST_DIR}/${TARGET_TEST}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
zip -qur artifacts.zip ${TARGET_TEST_DIR}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mosq_target_esp32_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/artifacts.zip
|
||||
path: ${{ env.TEST_DIR }}/${{ env.TARGET_TEST }}/artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
test_mosq:
|
||||
@ -71,3 +73,108 @@ jobs:
|
||||
mv $dir build
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32
|
||||
done
|
||||
|
||||
check_consistency:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push'
|
||||
name: Checks that API docs and versions are consistent
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checks API Docs and versions
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen
|
||||
pip install esp-doxybook
|
||||
cd components/mosquitto
|
||||
cp api.md api_orig.md
|
||||
./generate_api_docs.sh
|
||||
diff -wB api.md api_orig.md
|
||||
# check version consistency
|
||||
CONFIG_VERSION=$(grep -Po '(?<=#define VERSION ")[^"]*' port/priv_include/config.h)
|
||||
CZ_VERSION=$(grep -Po '(?<=version: )[^"]*' .cz.yaml)
|
||||
COMP_VERSION=$(grep -Po '(?<=version: ")[^"]*' idf_component.yml)
|
||||
if [ "$CONFIG_VERSION" != "v$CZ_VERSION" ] || [ "$CONFIG_VERSION" != "v$COMP_VERSION" ]; then
|
||||
echo "Version mismatch detected:"
|
||||
echo "config.h: $CONFIG_VERSION"
|
||||
echo ".cz.yaml: $CZ_VERSION"
|
||||
echo "idf_component.yml: $COMP_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
echo "Versions are consistent: $CONFIG_VERSION"
|
||||
|
||||
build_idf_tests_with_mosq:
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push' )
|
||||
name: Build IDF tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TARGET_TEST_DIR: build_esp32_local_broker
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
export MOSQUITTO_PATH=`pwd`/components/mosquitto
|
||||
# to use the actual version of mosquitto
|
||||
sed -i '/espressif\/mosquitto:/a \ \ \ \ override_path: "${MOSQUITTO_PATH}"' ${IDF_PATH}/${{matrix.test.path}}/main/idf_component.yml
|
||||
export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function"
|
||||
export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes"
|
||||
export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}"
|
||||
cd ${IDF_PATH}/${{matrix.test.path}}
|
||||
idf-build-apps find --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
|
||||
idf-build-apps build --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
# to replace mqtt test configs with specific mosquitto markers
|
||||
python ${MOSQUITTO_PATH}/test/replace_decorators.py pytest_mqtt_publish_app.py ${TARGET_TEST_DIR}/pytest_mosquitto.py
|
||||
zip -qur ${GITHUB_WORKSPACE}/artifacts.zip ${TARGET_TEST_DIR}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mosq_publish_esp32_${{ matrix.idf_ver }}
|
||||
path: artifacts.zip
|
||||
if-no-files-found: error
|
||||
|
||||
test_idf_ci_with_mosq:
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
if: |
|
||||
github.repository == 'espressif/esp-protocols' &&
|
||||
( contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push' )
|
||||
name: Mosquitto IDF target tests
|
||||
needs: build_idf_tests_with_mosq
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
env:
|
||||
TEST_DIR: examples
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: mosq_publish_esp32_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2" --upgrade
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
mv $dir build
|
||||
mv build/*.py .
|
||||
# Run only "test_mosquitto" marked tests
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32 -m test_mosquitto
|
||||
done
|
||||
|
3
.github/workflows/publish-docs-component.yml
vendored
3
.github/workflows/publish-docs-component.yml
vendored
@ -98,8 +98,11 @@ jobs:
|
||||
components/console_cmd_ping;
|
||||
components/console_cmd_ifconfig;
|
||||
components/console_cmd_wifi;
|
||||
components/console_cmd_mqtt;
|
||||
components/mbedtls_cxx;
|
||||
components/mosquitto;
|
||||
components/sock_utils;
|
||||
components/libwebsockets;
|
||||
components/esp_dns;
|
||||
namespace: "espressif"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
2
.github/workflows/tls_cxx__build.yml
vendored
2
.github/workflows/tls_cxx__build.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
test: [ { app: client, path: "examples/tls_client" }, { app: udp, path: "examples/udp_mutual_auth" }, { app: test, path: "tests/uart_mutual_auth" } ]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
|
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -1,6 +1,9 @@
|
||||
[submodule "components/asio/asio"]
|
||||
path = components/asio/asio
|
||||
url = https://github.com/espressif/asio
|
||||
url = https://github.com/chriskohlhoff/asio
|
||||
[submodule "components/mosquitto/mosquitto"]
|
||||
path = components/mosquitto/mosquitto
|
||||
url = https://github.com/eclipse/mosquitto
|
||||
[submodule "components/libwebsockets/libwebsockets"]
|
||||
path = components/libwebsockets/libwebsockets
|
||||
url = https://github.com/warmcat/libwebsockets.git
|
||||
|
@ -51,7 +51,7 @@ repos:
|
||||
rev: v1.0.5
|
||||
hooks:
|
||||
- id: astyle_py
|
||||
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--exclude-list=ci/ignore_astyle.txt']
|
||||
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--unpad-paren', '--max-continuation-indent=120', '--exclude-list=ci/ignore_astyle.txt']
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
rev: v2.42.1
|
||||
hooks:
|
||||
@ -61,8 +61,8 @@ repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit message scopes
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls)\)\:)'
|
||||
name: "commit message must be scoped with: mdns, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws)\)\:)'
|
||||
language: pygrep
|
||||
args: [--multiline]
|
||||
stages: [commit-msg]
|
||||
|
12
README.md
12
README.md
@ -66,3 +66,15 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
### Socket helpers (sock-utils)
|
||||
|
||||
* Brief introduction [README](components/sock_utils/README.md)
|
||||
|
||||
### libwebsockets
|
||||
|
||||
* Brief introduction [README](components/libwebsockets/README.md)
|
||||
|
||||
### console_cmd_mqtt
|
||||
|
||||
* Brief introduction [README](components/console_cmd_mqtt/README.md)
|
||||
|
||||
### esp_dns
|
||||
|
||||
* Brief introduction [README](components/esp_dns/README.md)
|
||||
|
@ -0,0 +1 @@
|
||||
components/mosquitto/examples/serverless_mqtt/components/libjuice/port/juice_random.c
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -64,7 +64,7 @@ static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint
|
||||
return h;
|
||||
}
|
||||
|
||||
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
|
||||
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize)
|
||||
{
|
||||
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
|
||||
}
|
||||
@ -75,7 +75,7 @@ uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t
|
||||
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
|
||||
}
|
||||
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
|
||||
{
|
||||
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
|
||||
}
|
||||
@ -86,7 +86,7 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
|
||||
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
|
||||
BaseType_t xSemaphoreGive(QueueHandle_t xQueue)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX) {
|
||||
@ -96,7 +96,7 @@ BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
|
||||
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
|
||||
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX_REC) {
|
||||
@ -106,7 +106,7 @@ BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
|
||||
return pdFALSE;
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX) {
|
||||
@ -116,7 +116,7 @@ BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
|
||||
}
|
||||
|
||||
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->type == MUTEX_REC) {
|
||||
@ -126,7 +126,7 @@ BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
|
||||
return pdFALSE;
|
||||
}
|
||||
|
||||
void vQueueDelete( QueueHandle_t xQueue )
|
||||
void vQueueDelete(QueueHandle_t xQueue)
|
||||
{
|
||||
struct generic_queue_handle *h = xQueue;
|
||||
if (h->q) {
|
||||
@ -166,6 +166,8 @@ void vTaskDelete(TaskHandle_t *task)
|
||||
|
||||
if (task == NULL) {
|
||||
pthread_exit(0);
|
||||
} else {
|
||||
pthread_cancel((pthread_t)task);
|
||||
}
|
||||
void *thread_rval = NULL;
|
||||
pthread_join((pthread_t)task, &thread_rval);
|
||||
@ -176,14 +178,14 @@ void vTaskSuspend(void *task)
|
||||
vTaskDelete(task);
|
||||
}
|
||||
|
||||
TickType_t xTaskGetTickCount( void )
|
||||
TickType_t xTaskGetTickCount(void)
|
||||
{
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_REALTIME, &spec);
|
||||
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
|
||||
}
|
||||
|
||||
void vTaskDelay( const TickType_t xTicksToDelay )
|
||||
void vTaskDelay(const TickType_t xTicksToDelay)
|
||||
{
|
||||
usleep(xTicksToDelay * 1000);
|
||||
}
|
||||
@ -212,13 +214,27 @@ void *pthread_task(void *params)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t usStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID)
|
||||
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t ulStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
StackType_t *const puxStackBuffer,
|
||||
StaticTask_t *const pxTaskBuffer,
|
||||
const BaseType_t xCoreID)
|
||||
{
|
||||
static TaskHandle_t pvCreatedTask;
|
||||
xTaskCreate(pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &pvCreatedTask);
|
||||
return pvCreatedTask;
|
||||
}
|
||||
|
||||
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t usStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID)
|
||||
{
|
||||
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
|
||||
return pdTRUE;
|
||||
@ -266,7 +282,7 @@ void xTaskNotifyGive(TaskHandle_t task)
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -276,32 +292,32 @@ TaskHandle_t xTaskGetCurrentTaskHandle(void)
|
||||
return (TaskHandle_t)pthread_self();
|
||||
}
|
||||
|
||||
EventGroupHandle_t xEventGroupCreate( void )
|
||||
EventGroupHandle_t xEventGroupCreate(void)
|
||||
{
|
||||
return osal_signal_create();
|
||||
}
|
||||
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
|
||||
void vEventGroupDelete(EventGroupHandle_t xEventGroup)
|
||||
{
|
||||
osal_signal_delete(xEventGroup);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
|
||||
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)
|
||||
{
|
||||
return osal_signal_clear(xEventGroup, uxBitsToClear);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
|
||||
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)
|
||||
{
|
||||
return osal_signal_get(xEventGroup);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
|
||||
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
|
||||
{
|
||||
return osal_signal_set(xEventGroup, uxBitsToSet);
|
||||
}
|
||||
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
|
||||
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait)
|
||||
{
|
||||
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -15,7 +16,10 @@ extern "C" {
|
||||
#define TaskHandle_t TaskHandle_t
|
||||
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
|
||||
|
||||
void vTaskDelay( const TickType_t xTicksToDelay );
|
||||
typedef void *StackType_t;
|
||||
typedef void *StaticTask_t;
|
||||
|
||||
void vTaskDelay(const TickType_t xTicksToDelay);
|
||||
|
||||
void xTaskNotifyGive(TaskHandle_t task);
|
||||
|
||||
@ -23,39 +27,48 @@ void ulTaskNotifyTake(bool stuff, uint32_t timeout);
|
||||
|
||||
TaskHandle_t xTaskGetCurrentTaskHandle(void);
|
||||
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time );
|
||||
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
|
||||
|
||||
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t usStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID);
|
||||
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t ulStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
StackType_t *const puxStackBuffer,
|
||||
StaticTask_t *const pxTaskBuffer,
|
||||
const BaseType_t xCoreID);
|
||||
|
||||
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
|
||||
const char *const pcName,
|
||||
const uint32_t usStackDepth,
|
||||
void *const pvParameters,
|
||||
UBaseType_t uxPriority,
|
||||
TaskHandle_t *const pvCreatedTask,
|
||||
const BaseType_t xCoreID);
|
||||
|
||||
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
|
||||
|
||||
TickType_t xTaskGetTickCount( void );
|
||||
TickType_t xTaskGetTickCount(void);
|
||||
|
||||
void vQueueDelete( QueueHandle_t xQueue );
|
||||
void vQueueDelete(QueueHandle_t xQueue);
|
||||
|
||||
QueueHandle_t xSemaphoreCreateBinary(void);
|
||||
|
||||
QueueHandle_t xSemaphoreCreateMutex(void);
|
||||
QueueHandle_t xSemaphoreCreateRecursiveMutex(void);
|
||||
|
||||
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
|
||||
BaseType_t xSemaphoreGive(QueueHandle_t xQueue);
|
||||
|
||||
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
|
||||
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask);
|
||||
|
||||
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue);
|
||||
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue);
|
||||
|
||||
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask );
|
||||
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask);
|
||||
|
||||
void vTaskDelete(TaskHandle_t *task);
|
||||
|
||||
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
|
||||
uint32_t uxItemSize );
|
||||
QueueHandle_t xQueueCreate(uint32_t uxQueueLength,
|
||||
uint32_t uxItemSize);
|
||||
|
||||
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
|
||||
|
||||
@ -63,23 +76,26 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
|
||||
|
||||
void vTaskSuspend(void *task);
|
||||
|
||||
EventGroupHandle_t xEventGroupCreate( void );
|
||||
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
|
||||
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear );
|
||||
EventGroupHandle_t xEventGroupCreate(void);
|
||||
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
|
||||
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToClear);
|
||||
|
||||
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait );
|
||||
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToWaitFor,
|
||||
const BaseType_t xClearOnExit,
|
||||
const BaseType_t xWaitForAllBits,
|
||||
TickType_t xTicksToWait);
|
||||
|
||||
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup);
|
||||
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
|
||||
|
||||
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet );
|
||||
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
|
||||
const EventBits_t uxBitsToSet);
|
||||
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
|
||||
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
|
||||
|
||||
void *heap_caps_malloc(size_t size, uint32_t caps);
|
||||
void heap_caps_free(void *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(asio): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py asio
|
||||
tag_format: asio-v$version
|
||||
version: 1.28.0~0
|
||||
version: 1.32.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## [1.32.0](https://github.com/espressif/esp-protocols/commits/asio-v1.32.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Upgrade asio to 1.32 ([9bdd429c](https://github.com/espressif/esp-protocols/commit/9bdd429c))
|
||||
- Drop esp/asio patches in favor of sock-utils ([27435b7f](https://github.com/espressif/esp-protocols/commit/27435b7f))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix chat example to print only the message body ([76aaea08](https://github.com/espressif/esp-protocols/commit/76aaea08))
|
||||
- Make asio enable if_nametoindex to fix linking ([5db32cce](https://github.com/espressif/esp-protocols/commit/5db32cce))
|
||||
- Re-applie refs to common comps idf_component.yml ([9fe44a45](https://github.com/espressif/esp-protocols/commit/9fe44a45))
|
||||
- Reference common component from IDF ([74fc228c](https://github.com/espressif/esp-protocols/commit/74fc228c))
|
||||
- Revert referencing protocol_examples_common from IDF ([f9e0281a](https://github.com/espressif/esp-protocols/commit/f9e0281a))
|
||||
- reference protocol_examples_common from IDF ([09abb18b](https://github.com/espressif/esp-protocols/commit/09abb18b))
|
||||
- specify override_path in example manifest files ([1d8923cf](https://github.com/espressif/esp-protocols/commit/1d8923cf))
|
||||
|
||||
### Updated
|
||||
|
||||
- docs(asio): Updates asio docs ([ce9337d3](https://github.com/espressif/esp-protocols/commit/ce9337d3))
|
||||
|
||||
## [1.28.2~0](https://github.com/espressif/esp-protocols/commits/asio-1.28.2_0)
|
||||
|
||||
### Bug Fixes
|
||||
|
@ -6,8 +6,8 @@ if(NOT CONFIG_LWIP_IPV6 AND NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(asio_sources "asio/asio/src/asio.cpp")
|
||||
set(asio_requires lwip)
|
||||
set(asio_sources "asio/asio/src/asio.cpp" "port/src/asio_stub.cpp")
|
||||
set(asio_requires lwip sock_utils)
|
||||
|
||||
if(CONFIG_ASIO_SSL_SUPPORT)
|
||||
list(APPEND asio_sources
|
||||
@ -18,7 +18,7 @@ if(CONFIG_ASIO_SSL_SUPPORT)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${asio_sources}
|
||||
INCLUDE_DIRS "asio/asio/include" "port/include"
|
||||
INCLUDE_DIRS "port/include" "asio/asio/include"
|
||||
PRIV_INCLUDE_DIRS ${asio_priv_includes}
|
||||
PRIV_REQUIRES ${asio_requires})
|
||||
|
||||
@ -30,6 +30,7 @@ target_compile_definitions(${COMPONENT_LIB} PUBLIC SA_RESTART=0x01
|
||||
ASIO_STANDALONE
|
||||
ASIO_HAS_PTHREADS
|
||||
OPENSSL_NO_ENGINE
|
||||
ASIO_DETAIL_IMPL_POSIX_EVENT_IPP # this replaces asio's posix_event constructor
|
||||
)
|
||||
|
||||
if(NOT CONFIG_COMPILER_CXX_EXCEPTIONS)
|
||||
|
@ -1,6 +1,15 @@
|
||||
menu "ESP-ASIO"
|
||||
visible if LWIP_IPV6
|
||||
|
||||
config ASIO_IS_ENABLED
|
||||
# Invisible option that is enabled if ASIO is added to the IDF components.
|
||||
# This is used to "select" LWIP_NETIF_API option
|
||||
# which enables if_indextoname() and if_nametoindex() functions
|
||||
# (these are optionally used in asio)
|
||||
bool
|
||||
default "y"
|
||||
select LWIP_NETIF_API
|
||||
|
||||
config ASIO_SSL_SUPPORT
|
||||
bool "Enable SSL/TLS support of ASIO"
|
||||
default n
|
||||
|
Submodule components/asio/asio updated: a2e0f70d61...03ae834edb
@ -120,7 +120,7 @@ private:
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/) {
|
||||
if (!ec) {
|
||||
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
|
||||
ESP_LOGD("asio-chat", "%.*s", read_msg_.body_length(), read_msg_.body());
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
} else {
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "asio/ssl.hpp"
|
||||
#include "asio/buffer.hpp"
|
||||
#include "esp_pthread.h"
|
||||
// allows for direct access to mbedtls specifics
|
||||
#include "asio/ssl/mbedtls_specific.hpp"
|
||||
|
||||
extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start");
|
||||
extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end");
|
||||
@ -217,6 +219,7 @@ void ssl_server_thread()
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
|
||||
void ssl_client_thread()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
@ -229,6 +232,11 @@ void ssl_client_thread()
|
||||
asio::ssl::context ctx(asio::ssl::context::tls_client);
|
||||
#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
ctx.add_certificate_authority(cert_chain);
|
||||
// mbedtls (from 3.6.3) requires hostname to be set when performing TLS handshake with verify-peer option
|
||||
// asio::ssl allows for name verification using verification callback, i.e. socket_.set_verify_callback(asio::ssl::host_name_verification()),
|
||||
// - which is not supported in Espressif ASIO port yet.
|
||||
// Therefore we provide a way to directly use mbedtls API and here we just configure the expected hostname to verify
|
||||
asio::ssl::mbedtls::set_hostname(ctx.native_handle(), server_ip);
|
||||
#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
|
||||
Client c(io_context, ctx, endpoints);
|
||||
|
@ -1,22 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkzCCAnugAwIBAgIUNI5wldYysh6rtCzYmda6H414aRswDQYJKoZIhvcNAQEL
|
||||
MIIDkzCCAnugAwIBAgIUb25LYOLubieEbKPQDiM+8T5p4yUwDQYJKoZIhvcNAQEL
|
||||
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMB4X
|
||||
DTIwMDEyMTA5MDk0NloXDTI1MDEyMDA5MDk0NlowWTELMAkGA1UEBhMCQVUxEzAR
|
||||
DTI1MDQwNzA5NDkzOFoXDTQ1MDQwMjA5NDkzOFowWTELMAkGA1UEBhMCQVUxEzAR
|
||||
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
|
||||
IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAyadSpRnIQBVbEAsbpkrKrOMlBOMIUmA8AfNyOYPLfv0Oa5lBiMAV
|
||||
3OQDu5tYyFYKwkCUqq65iAm50fPbSH71w1tkja6nZ1yAIM+TvpMlM/WiFGrhY+Tc
|
||||
kAcLcKUJyPxrv/glzoVslbqUgIhuhCSKA8uk1+ILcn3nWzPcbcowLx31+AHeZj8h
|
||||
bIAdj6vjqxMCFStp4IcA+ikmCk75LCN4vkkifdkebb/ZDNYCZZhpCBnCHyFAjPc4
|
||||
7C+FDVGT3/UUeeTy+Mtn+MqUAhB+W0sPDm1n2h59D4Z/MFm0hl6GQCAKeMJPzssU
|
||||
BBsRm6zoyPQ4VTqG0uwfNNbORyIfKONMUwIDAQABo1MwUTAdBgNVHQ4EFgQUGYLV
|
||||
EkgWzxjpltE6texha7zZVxowHwYDVR0jBBgwFoAUGYLVEkgWzxjpltE6texha7zZ
|
||||
VxowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb2EF4Zg2XWNb
|
||||
eZHnzupCDd9jAhwPqkt7F1OXvxJa/RFUSB9+2izGvikGGhuKY4f0iLuqF+bhExD9
|
||||
sapDcdFO2Suh4J3onbwEvmKvsv56K3xhapYg8WwPofpkVirnkwFjpQXGzrYxPujg
|
||||
BPmSy3psQrhvOr/WH7SefJv2qr4ikaugfE+3enY4PL+C1dSQAuNo1QGgWsZIu0c8
|
||||
TZybNZ13vNVMA+tgj2CM8FR3Etaabwtu3TTcAnO7aoBTix/bLBTuZoczhN8/MhG3
|
||||
GylmDzFI8a6aKxQL3Fi4PsM82hRKWu3gfs39sR1Ci4V22v8uO5EWBPK0QZvDSc1a
|
||||
KwwxI4zA0w==
|
||||
MIIBCgKCAQEArJsjwSNjPOBpTCRW+pIag9gJgRaNIjscea/ilRYRwAnqWKLNssNw
|
||||
Kye79KmQ5TxnOEvBIYjesArst1l7MghPLaELscCKo96jzCkSmgPLbxPs+5/E4daO
|
||||
9ItxOSH2mjOgG5yFQLEb8xOvsvWWrJAUBj6RBjhzgSYLYRbesWKAyVi9fxSuzfZm
|
||||
ROV0B2NsO1PlUDzweo9RYSuvpyNR3kddNnc6lJLXtZhf6IHczjFDFd5/PQuzLIO/
|
||||
Dbg+5AMpQykbMFhcQI/Y49GlMMXFDIaWjP+XfE/yUJ4GyYd2EzpDFDFMisnkuR9d
|
||||
LQgSXZNwygO8SIfYnnm1pwcGuG/fCQZYpQIDAQABo1MwUTAdBgNVHQ4EFgQUMTUG
|
||||
OZ7ujyz7oXSuhDgbpoPRo1cwHwYDVR0jBBgwFoAUMTUGOZ7ujyz7oXSuhDgbpoPR
|
||||
o1cwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAD5JwzRVEvnTK
|
||||
R2bfMNy60FmFNTOEEYP+XYoNGBiXVY3MRrWlfbY5Pbs4Nq7sCfzEWMj2UsjFmjZE
|
||||
DU6FdsaL6rhnps03MR5yiuE5w2aPiH/ijgzVfZtdLe6nKcnrv1YInjEKk+Y3qGu6
|
||||
2ZC+MEINPBfRiuN6gCAdxGiK81J4FPLlZImLO/g/0fSrIXCzBUzjoYRYjsy5AP60
|
||||
0kbaoGA/SshP0aeNvWB0wUab40idGXBFJ3vnEfMbLIMdc/uCqnzRpqK0m1DacwrI
|
||||
nTUMl0bI302Oa/gym+Ma0nJ1nVADcLKoZ1syWjyzIcl6zr+ITY5S+pbeO/geQgKh
|
||||
NSUkahX6MQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
22
components/asio/examples/ssl_client_server/main/regenerate_test_certs.sh
Executable file
22
components/asio/examples/ssl_client_server/main/regenerate_test_certs.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# This script generates self-signed certificates for testing purposes only.
|
||||
# DO NOT use these certificates in production environments.
|
||||
# These certificates are meant for development and testing of SSL/TLS functionality.
|
||||
|
||||
# 1. Generate CA private key
|
||||
openssl genrsa -out ca.key 2048
|
||||
|
||||
# 2. Generate CA certificate (validity: 20 years, CN=Espressif)
|
||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 7300 -out ca.crt -subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=Espressif"
|
||||
|
||||
# 3. Generate server private key
|
||||
openssl genrsa -out server.key 2048
|
||||
|
||||
# 4. Generate server Certificate Signing Request (CSR)
|
||||
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
|
||||
|
||||
# 5. Generate server certificate signed by CA (validity: 20 years)
|
||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out srv.crt -days 7300 -sha256
|
||||
|
||||
# 6. Clean up intermediate files
|
||||
rm server.csr ca.srl
|
@ -1,27 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAlUCywNhVv4RO2y9h/XGKZ1azzk3jzHpSBzIGO9LoiA8trC/p
|
||||
1ykGaUfYPJllYK4HMhC4fUyE3J7tVL2Eskzl26LNPLbEoaBWZM9NhV3iA1/1EtOu
|
||||
p6umLx+y3sDfvK35YAOUbjdAlBfhnJ4r8h7oTsxl3J5jZ18zgjJnJi2NEFq/yTpO
|
||||
MiwHLWPjy25fDFixfV9UzSvbgt1JaGPmC7c4QkhHzjyp0+ikuvRIw0p9BBNeqBV2
|
||||
da3qBMB5FtodUJTAz6o6OKWbTalLjQi6C1H6z9TnY7IrJBUOy/FWkQH/sEsLdscD
|
||||
hHa1Dz2oT203QjhzyOSfnNF95D/1MdNcMt6l0wIDAQABAoIBAC1JJTOoMFRc48RT
|
||||
myrYQYNbZlEphv3q+2qdfhC2zMFDwbrmCtCy7PQSzYSNkpoEE8DYG/JAvmtmeWJl
|
||||
4pZrCK9ctWM/nWfhC3WpBL97nfEiM20T94F+bn0L5Cz8XqaULv839th+QUTt/hGU
|
||||
WIctY5VNJXcMQ+MAmtNdUbjex1d3iuxiKHUo4nDoZ8digKFNdtdP5B5nlMq5chCL
|
||||
mxNRcsGsx2dDAxbGUapdTVPWHPJKpLOBoSkluDsfd2KZADFU2R1SJpAX9+RYh3HM
|
||||
5FTUdHTUaISxbKkgeDKlEM0lqk2TtGUwCyEj098ewi7Wzsu9w60IplPPUJx5FRG6
|
||||
jp3wzLkCgYEAxKp5T20rf/7ysX7x053I7VCjDXUxAaWOEj1uS3AhOkl0NaZg7Di+
|
||||
y53fWNkcHdkt2n2LqMt/43UgMYq3TVVcq2eunPNF11e1bJw8CjDafwDs4omwwyVn
|
||||
lYhPuB4dK2OAib+vU5Zqpp0kZMoxk2MZVgon8z+s8DW/zmB6aFqAWeUCgYEAwkhC
|
||||
OgmXKMdjOCVy5t2f5UbY8Y9rV3w8eUATuJ47MMwLr4pGYnKoEn9JB4ltWrHv/u5S
|
||||
fOv3tIrrCEvnCoCbOILwCsY5LqTNXgqova8FB6RpMUQCzhDd8LHuvdHv0WMnMzX1
|
||||
3PKuqwh8JS55m4WqZRhzr5BFKG4fHPVs4IcaJVcCgYAzzCaJSdqUKqTnJOUydDNQ
|
||||
ddWMHNqccWs62J0tF0pZHLGT089hSAzQejMyJnSmU+Ykzr4y5e44DUg+ZCelIZ93
|
||||
saYmxlgVwI8THQ8fLADQRIEfpV4996MRmkZM2vmZzOo03Zyi6lIKsga82Rg3lnk8
|
||||
1Q3ynknBNpbfF0AGLhfyFQKBgBYlxJ73HutAJ5hr9HhLBYJOnEaVUehMOlycKGNg
|
||||
bmD2sdJWEgYBChXpurqIORYguLo4EuE4ySkkuPxeIr14wbkkfBbOWBBwKxUwY+IT
|
||||
xKAFZxR9q1AwbgyVTCEJgKw/AGX/HcMNS0omEnjunmBTUYRq0C1QZgHg490aQUor
|
||||
PJjLAoGAevzdTpFlVeuKeYh1oDubGO1LinyXpBv7fPFjl+zu4AVbjojcU6yC4OO6
|
||||
QvqopE6SyAECKy8kAOFcESPsGc9Lta2XUvI203z7pIVlNVEcJ0+90mQh3Mn1U46l
|
||||
sZ49PdRvNwNb5wvkh1UqNsMlGFbRlzMbIk45ou4311kCobowZek=
|
||||
MIIEpAIBAAKCAQEA1zQFgwUxyIteLl5SiciuRBZbn/3KWRmsVpyo5JiYCK9NM3Q8
|
||||
dDur/dyQ4y2Mq0RVuM7uZL7VraRA4F9+Tm8D2BJ8q4Ai3nRDiGQkFEfbvX0Wic6P
|
||||
P6q7lKm65fnYMvbWHqhMOglr8e4b7dMOFpZdNLRZVcoP4/43+/9dOgOj7TXczTFs
|
||||
jnlLJBijOnwABf/xlTpTR7ZbQ3uOlckc0TK/Lqxex+NUq4dXQKa96a/wpA6smW38
|
||||
0XU5hvVBmhA9YNK86CIpAVOny9gNM1Wxv+aAdVZigNNi6Hht75neC0DhFqGbfpMi
|
||||
nLhW8qGwVtY3T1pM8HrpdAu5plkdvmcDm8tUAwIDAQABAoIBADHwOHc29V58ONa5
|
||||
vJ2MnCPgrFJsKlCSzJMst2SUpHMfeuK9zmmKj1bRoC2XnFUB/oJsQpXOUveAbi2i
|
||||
+0RoLpQtdhC2I2FLyYAU/OpX4n4OUPSZolQ74luVJ3HGkI0DCp0CoO220f3KK2D8
|
||||
4QAM1IQudayavyVBEOzBTXjw71FUCXmg0viBtd7JRSug9FR69PPLWP14uzo1viBA
|
||||
dtRsLePDyTatW+bfKkPwDG8hhJoummw+fYRPZbM05aWLmVvTiZ/eQtRq1jkwUNSH
|
||||
zXsJiCQbHLqwoXISiEsdagQM8hNW2bw5B+ijCQhWNj2ZZBZPJIRtI75sXiXGy2eC
|
||||
kDaP0cECgYEA8GpoGOyVZ+KMm7PVNcNbdLJrjpoNhdVXdGwR+yd3u6EoQ3MMD5tr
|
||||
4F7pFtN3IxwLoCSM/dnobysNFdKdQRnOB+o3uLADiPn9REM7ebuGUwUP3BWwkb7S
|
||||
orDVPUMA82A1p0T4Dwi+OAPpS5bUoI7S/6ZwheWTdoGZYjCrtsqkeEUCgYEA5Sc5
|
||||
sgLFWIqvYUQF/+aLAwjHR/bBD9NkvHgdW8GmgXdnEd0DIKHDpJ2yEK3BbQxVp/Kk
|
||||
O2KG1NGyJ9UqA9QQ3q5UgpZNHQBWxz2GUl0jHsy8enhgsZr4K2+wvuw5F5bCdXPe
|
||||
m/dyFIGnUJ7ic+DtvDGjXdcwAR8Cgc97m5Pg06cCgYEAgyjqBb78e6KDJ2biyOP9
|
||||
fxrfxvqQqhUMEz3qSWTs03ZGaxXW3KTkI5JkA8n2Uzc3uHR4Xv2E6zFHgEJY/G1B
|
||||
k9vZ7m5IX3BTFezAA9eknqJCVsWWgMzkSVHD5Bor6JryaoEb+8e/TvwDSPPOqJGC
|
||||
12pMNSBcZOirb4AyDhVbySkCgYB3Lu2dHj/SC1+oMR8Ft7y5eUlcroQ/XO1Z8Qck
|
||||
ABY/5ABhlBfaUwhUiAhjEFw4AWBTl6m/kUEbU21btkzB7PxRNU6TFOVKnjCENAW2
|
||||
tOZdUJL/B7kS5s0ImnDM/EO9dxXwzLENYaed7sk870ZMisJbTV3wosk+7Af7yBQ8
|
||||
GK+opQKBgQCdZy3KX2FT8S/K2SjDuRM8uDzJ+IcaqScDhgbJFMlrbWmTSML69oRD
|
||||
Ic6xVe5hWkkPIs521gwrQSD5E3dbb2dFmjhUZpZkHdv0u/AUupFN0EaCFb/I2A0P
|
||||
fRebd9oKoZjlUrEPeID0kjzbmnPGbtG+gFZYmkRb5iLdcCVAn0O1AQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
@ -1,18 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC9DCCAdwCFA1lSIcHwYKdB2UqOrZxZnVgPObTMA0GCSqGSIb3DQEBCwUAMFkx
|
||||
MIIC9DCCAdwCFHNjaiCN2RT7W7NHXho8HlgxdAygMA0GCSqGSIb3DQEBCwUAMFkx
|
||||
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yMDA2
|
||||
MTIwNjA0MTNaFw0yMjA2MDIwNjA0MTNaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJVAssDYVb+ETtsvYf1ximdW
|
||||
s85N48x6UgcyBjvS6IgPLawv6dcpBmlH2DyZZWCuBzIQuH1MhNye7VS9hLJM5dui
|
||||
zTy2xKGgVmTPTYVd4gNf9RLTrqerpi8fst7A37yt+WADlG43QJQX4ZyeK/Ie6E7M
|
||||
ZdyeY2dfM4IyZyYtjRBav8k6TjIsBy1j48tuXwxYsX1fVM0r24LdSWhj5gu3OEJI
|
||||
R848qdPopLr0SMNKfQQTXqgVdnWt6gTAeRbaHVCUwM+qOjilm02pS40IugtR+s/U
|
||||
52OyKyQVDsvxVpEB/7BLC3bHA4R2tQ89qE9tN0I4c8jkn5zRfeQ/9THTXDLepdMC
|
||||
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAnMYGW+idt37bEE4WPgrRorKWuplR+zHD
|
||||
wJFz53DQzyIZJHmJ2hR5U0jNcHy/nMq7tbdz9LZPrVF4lZJ3TJhnmkOKjMFPCQE8
|
||||
YcmsP3il6eXgtGqg53InOi/uJqEQ9TfM54cbpp6xKbnmpwk4uprISBRQt7u2ZLk2
|
||||
40ED6zgjFPDTYmSjSpb2AN6KUB6PflgVs+4p9ViHNq4U3AlYV/BM0+3G4aMX2wNl
|
||||
ZIpQfOyuaYD5MU50mY+O+gDiiypkpYf6a6S4YJ1sMbavDsP7bW5UMnP0jKYR549q
|
||||
5hF1fdkXq52DfJ9ya2kl3mANFkKssQV+1KCBMxGoeqfakmJfa03xXA==
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yNTA0
|
||||
MDcwOTQ5MzhaFw00NTA0MDIwOTQ5MzhaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc0BYMFMciLXi5eUonIrkQW
|
||||
W5/9ylkZrFacqOSYmAivTTN0PHQ7q/3ckOMtjKtEVbjO7mS+1a2kQOBffk5vA9gS
|
||||
fKuAIt50Q4hkJBRH2719FonOjz+qu5SpuuX52DL21h6oTDoJa/HuG+3TDhaWXTS0
|
||||
WVXKD+P+N/v/XToDo+013M0xbI55SyQYozp8AAX/8ZU6U0e2W0N7jpXJHNEyvy6s
|
||||
XsfjVKuHV0Cmvemv8KQOrJlt/NF1OYb1QZoQPWDSvOgiKQFTp8vYDTNVsb/mgHVW
|
||||
YoDTYuh4be+Z3gtA4Rahm36TIpy4VvKhsFbWN09aTPB66XQLuaZZHb5nA5vLVAMC
|
||||
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAoiuycWVVjmS9IMS1n9ll8UlIqq8dl7vs
|
||||
Y5ckGMrdSGR8BPgloTrB6ual4vRPgbn2rGBGAvGusdcmhc1vkVbYsI3JRpTTDDoE
|
||||
PvQqHWXwV2RDRU5kG6ZOsU+o01Ir4b3w3qfP2LT20FCuuAMIMh23PsSmoc7ziFZ8
|
||||
76+ox6FjhJMPMF2aftiDmP44/fFg16C1t2PFH/Bk4sm4qRdpXVcWeiaHaSF9JkHa
|
||||
XwW3TuDSxJwlFFU7ffTRgYYkQ61q8B0LjWV4FF1dBBqflAiXEhWcVhljqfsWn7rq
|
||||
NBJ/QzZ3GhgQO9GOCokh/ckcp/ZMOm9tv9lV2huGz8Akk8/UYhMUEg==
|
||||
-----END CERTIFICATE-----
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: "1.28.2~0"
|
||||
version: "1.32.0"
|
||||
description: Cross-platform C++ library for network and I/O programming
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/asio
|
||||
issues: https://github.com/espressif/esp-protocols/issues
|
||||
@ -7,3 +7,5 @@ repository: https://github.com/espressif/esp-protocols.git
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
espressif/sock_utils:
|
||||
version: "^0.1"
|
||||
|
11
components/asio/port/include/asio/detail/config.hpp
Normal file
11
components/asio/port/include/asio/detail/config.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "sys/socket.h"
|
||||
#include "socketpair.h"
|
||||
|
||||
#include_next "asio/detail/config.hpp"
|
29
components/asio/port/include/asio/ssl/mbedtls_specific.hpp
Normal file
29
components/asio/port/include/asio/ssl/mbedtls_specific.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asio/ssl/context_base.hpp"
|
||||
#include "asio/ssl/context.hpp"
|
||||
#include "asio/ssl/detail/openssl_types.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
/**
|
||||
* @brief Configures specific hostname to be used in peer verification
|
||||
*
|
||||
* @param handle asio::ssl context handle type
|
||||
* @param name hostname to be verified (std::string ownership will be moved to ssl::context)
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool set_hostname(asio::ssl::context::native_handle_type handle, std::string name);
|
||||
|
||||
};
|
||||
};
|
||||
} // namespace asio::ssl::mbedtls
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_ASIO_CONFIG_H_
|
||||
#define _ESP_ASIO_CONFIG_H_
|
||||
|
||||
#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
|
||||
#include "openssl_stub.hpp"
|
||||
|
||||
#endif // _ESP_ASIO_CONFIG_H_
|
@ -1,5 +1,5 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
// SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
@ -52,6 +52,12 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool set_hostname(std::string hostname)
|
||||
{
|
||||
hostname_ = std::move(hostname);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t size(container c) const
|
||||
{
|
||||
switch (c) {
|
||||
@ -70,6 +76,7 @@ public:
|
||||
const_buffer cert_chain_;
|
||||
const_buffer private_key_;
|
||||
const_buffer ca_cert_;
|
||||
std::string hostname_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
// SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
@ -16,6 +16,11 @@ namespace asio {
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
bool set_hostname(asio::ssl::context::native_handle_type handle, std::string name)
|
||||
{
|
||||
return handle->get()->set_hostname(std::move(name));
|
||||
}
|
||||
|
||||
const char *error_message(int error_code)
|
||||
{
|
||||
static char error_buf[100];
|
||||
@ -25,7 +30,7 @@ const char *error_message(int error_code)
|
||||
|
||||
void throw_alloc_failure(const char *location)
|
||||
{
|
||||
asio::error_code ec( MBEDTLS_ERR_SSL_ALLOC_FAILED, asio::error::get_mbedtls_category());
|
||||
asio::error_code ec(MBEDTLS_ERR_SSL_ALLOC_FAILED, asio::error::get_mbedtls_category());
|
||||
asio::detail::throw_error(ec, location);
|
||||
}
|
||||
|
||||
@ -269,6 +274,16 @@ private:
|
||||
} else {
|
||||
mbedtls_ssl_conf_ca_chain(&conf_, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Configure hostname before handshake if users pre-configured any
|
||||
// use NULL if not set (to preserve the default behaviour of mbedtls < v3.6.3)
|
||||
const char* hostname = !ctx->hostname_.empty() ? ctx->hostname_.c_str() : NULL;
|
||||
ret = mbedtls_ssl_set_hostname(&ssl_, hostname);
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_ssl_set_hostname", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = mbedtls_ssl_setup(&ssl_, &conf_);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_setup", ret);
|
||||
|
@ -8,7 +8,7 @@
|
||||
//
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "openssl_stub.hpp"
|
||||
#include "asio/ssl/detail/openssl_types.hpp"
|
||||
#include <cstring>
|
||||
#include "asio/detail/throw_error.hpp"
|
||||
#include "asio/error.hpp"
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "openssl_stub.hpp"
|
||||
#include "asio/ssl/detail/openssl_types.hpp"
|
||||
#include "asio/detail/throw_error.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/ssl/detail/engine.hpp"
|
||||
|
36
components/asio/port/src/asio_stub.cpp
Normal file
36
components/asio/port/src/asio_stub.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
// SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
#include "asio/detail/posix_event.hpp"
|
||||
#include "asio/detail/throw_error.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/detail/push_options.hpp"
|
||||
#include <unistd.h>
|
||||
#include <climits>
|
||||
|
||||
namespace asio::detail {
|
||||
// This replaces asio's posix_event constructor
|
||||
// since the default POSIX version uses pthread_condattr_t operations (init, setclock, destroy),
|
||||
// which are not available on all IDF versions (some are defined in compilers' headers, others in
|
||||
// pthread library, but they typically return `ENOSYS` which causes trouble in the event wrapper)
|
||||
// IMPORTANT: Check implementation of posix_event() when upgrading upstream asio in order not to
|
||||
// miss any initialization step.
|
||||
posix_event::posix_event()
|
||||
: state_(0)
|
||||
{
|
||||
int error = ::pthread_cond_init(&cond_, nullptr);
|
||||
asio::error_code ec(error, asio::error::get_system_category());
|
||||
asio::detail::throw_error(ec, "event");
|
||||
}
|
||||
} // namespace asio::detail
|
||||
|
||||
extern "C" int pause (void)
|
||||
{
|
||||
while (true) {
|
||||
::sleep(UINT_MAX);
|
||||
}
|
||||
}
|
8
components/console_cmd_mqtt/.cz.yaml
Normal file
8
components/console_cmd_mqtt/.cz.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(console): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py console_cmd_mqtt
|
||||
tag_format: console_cmd_mqtt-v$version
|
||||
version: 1.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
7
components/console_cmd_mqtt/CHANGELOG.md
Normal file
7
components/console_cmd_mqtt/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/console_cmd_mqtt-v1.0.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Added component with mqtt command ([1fcc5b1d](https://github.com/espressif/esp-protocols/commit/1fcc5b1d))
|
7
components/console_cmd_mqtt/CMakeLists.txt
Normal file
7
components/console_cmd_mqtt/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
idf_component_register(SRCS "console_mqtt.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_netif console mqtt)
|
||||
|
||||
if(CONFIG_MQTT_CMD_AUTO_REGISTRATION)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u console_cmd_mqtt_register")
|
||||
endif()
|
15
components/console_cmd_mqtt/Kconfig.projbuild
Normal file
15
components/console_cmd_mqtt/Kconfig.projbuild
Normal file
@ -0,0 +1,15 @@
|
||||
menu "MQTT Configuration"
|
||||
|
||||
config MQTT_CMD_AUTO_REGISTRATION
|
||||
bool "Enable Console command mqtt Auto-registration"
|
||||
default y
|
||||
help
|
||||
Enabling this allows for the autoregistration of the wifi command.
|
||||
|
||||
config MQTT_BROKER_URL
|
||||
string "Broker URL or IP address"
|
||||
default "mqtt://mqtt.eclipseprojects.io"
|
||||
help
|
||||
URL or IP address of the broker to connect to
|
||||
|
||||
endmenu
|
87
components/console_cmd_mqtt/README.md
Normal file
87
components/console_cmd_mqtt/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Console command mqtt
|
||||
The component provides a console where mqtt commands can be executed.
|
||||
|
||||
|
||||
## MQTT Configuration:
|
||||
1. Broker: Use menuconfig **"MQTT Configuration"** to configure the broker url.
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Steps to enable console in an example code:
|
||||
1. Add this component to your project using ```idf.py add-dependency``` command.
|
||||
2. In the main file of the example, add the following line:
|
||||
```c
|
||||
#include "console_mqtt.h"
|
||||
```
|
||||
3. Ensure esp-netif is initialized and default event loop is created in your app_main():
|
||||
```c
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
```
|
||||
4. In your app_main() function, add the following line as the last line:
|
||||
```c
|
||||
ESP_ERROR_CHECK(console_cmd_init()); // Initialize console
|
||||
|
||||
// Register all plugin command added to your project
|
||||
ESP_ERROR_CHECK(console_cmd_all_register());
|
||||
|
||||
// To register only mqtt command skip calling console_cmd_all_register()
|
||||
ESP_ERROR_CHECK(console_cmd_mqtt_register());
|
||||
|
||||
ESP_ERROR_CHECK(console_cmd_start()); // Start console
|
||||
```
|
||||
|
||||
Note: Auto-registration of a specific plugin command can be disabled from menuconfig.
|
||||
|
||||
### Certificate Integration for Mutual Authentication
|
||||
To enhance security and enable secure communication over MQTT, three functions have been added to the API, allowing users to set client certificates, client keys, and broker certificates separately.
|
||||
|
||||
Setting the client certificate:
|
||||
```c
|
||||
set_mqtt_client_cert(client_cert_pem_start, client_cert_pem_end);
|
||||
```
|
||||
Setting the client key:
|
||||
```c
|
||||
set_mqtt_client_key(client_key_pem_start, client_key_pem_end);
|
||||
```
|
||||
Setting the broker certificate:
|
||||
```c
|
||||
set_mqtt_broker_certs(broker_cert_pem_start, broker_cert_pem_end);
|
||||
```
|
||||
Each function takes pointers to the start and end of the respective PEM-encoded data, allowing users to specify the necessary certificate and key information independently. For a complete secure MQTT setup, users should call all three functions in their application code.
|
||||
|
||||
To utilize these certificates, users need to include additional arguments when establishing MQTT connections using the library. Specifically, users should provide the `--cert`, `--key`, and `--cafile` options along with the MQTT connection command.
|
||||
|
||||
### Adding a plugin command or component:
|
||||
To add a plugin command or any component from IDF component manager into your project, simply include an entry within the `idf_component.yml` file.
|
||||
|
||||
For more details refer [IDF Component Manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html)
|
||||
|
||||
## Suported command:
|
||||
|
||||
### mqtt:
|
||||
```
|
||||
mqtt [-CsD] [-h <host>] [-u <username>] [-P <password>] [--cert] [--key] [--cafile]
|
||||
mqtt command
|
||||
-C, --connect Connect to a broker (flag, no argument)
|
||||
-h, --host=<host> Specify the host uri to connect to
|
||||
-s, --status Displays the status of the mqtt client (flag, no argument)
|
||||
-u, --username=<username> Provide a username to be used for authenticating with the broker
|
||||
-P, --password=<password> Provide a password to be used for authenticating with the broker
|
||||
--cert Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)
|
||||
--key Define the PEM encoded private key for this client, if required by the broker (flag, no argument)
|
||||
--cafile Define the PEM encoded CA certificates that are trusted (flag, no argument)
|
||||
--use-internal-bundle Use the internal certificate bundle for TLS (flag, no argument)
|
||||
-D, --disconnect Disconnect from the broker (flag, no argument)
|
||||
|
||||
mqtt_pub [-t <topic>] [-m <message>]
|
||||
mqtt publish command
|
||||
-t, --topic=<topic> Topic to Subscribe/Publish
|
||||
-m, --message=<message> Message to Publish
|
||||
|
||||
mqtt_sub [-U] [-t <topic>]
|
||||
mqtt subscribe command
|
||||
-t, --topic=<topic> Topic to Subscribe/Publish
|
||||
-U, --unsubscribe Unsubscribe from a topic
|
||||
```
|
475
components/console_cmd_mqtt/console_mqtt.c
Normal file
475
components/console_cmd_mqtt/console_mqtt.c
Normal file
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "console_mqtt.h"
|
||||
#include "mqtt_client.h"
|
||||
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
#include "esp_crt_bundle.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "console_mqtt";
|
||||
|
||||
#define CONNECT_HELP_MSG "mqtt -C -h <host uri> -u <username> -P <password> --cert --key --cafile\n"
|
||||
#define PUBLISH_HELP_MSG "Usage: mqtt -P -t <topic> -d <data>\n"
|
||||
#define SUBSCRIBE_HELP_MSG "Usage: mqtt -S -t <topic>\n"
|
||||
#define UNSUBSCRIBE_HELP_MSG "Usage: mqtt -U\n"
|
||||
#define DISCONNECT_HELP_MSG "Usage: mqtt -D\n"
|
||||
|
||||
#if CONFIG_MQTT_CMD_AUTO_REGISTRATION
|
||||
/**
|
||||
* Static registration of this plugin is achieved by defining the plugin description
|
||||
* structure and placing it into .console_cmd_desc section.
|
||||
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
|
||||
*/
|
||||
static const console_cmd_plugin_desc_t __attribute__((section(".console_cmd_desc"), used)) PLUGIN = {
|
||||
.name = "console_cmd_mqtt",
|
||||
.plugin_regd_fn = &console_cmd_mqtt_register
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
struct arg_lit *connect;
|
||||
struct arg_str *uri;
|
||||
struct arg_lit *status;
|
||||
struct arg_str *username;
|
||||
struct arg_str *password;
|
||||
struct arg_lit *cert;
|
||||
struct arg_lit *key;
|
||||
struct arg_lit *cafile;
|
||||
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
struct arg_lit *use_internal_bundle;
|
||||
#endif
|
||||
struct arg_lit *disconnect;
|
||||
|
||||
struct arg_end *end;
|
||||
} mqtt_args;
|
||||
|
||||
static struct {
|
||||
struct arg_str *topic;
|
||||
struct arg_lit *unsubscribe;
|
||||
|
||||
struct arg_end *end;
|
||||
} mqtt_sub_args;
|
||||
|
||||
static struct {
|
||||
struct arg_str *topic;
|
||||
struct arg_str *message;
|
||||
|
||||
struct arg_end *end;
|
||||
} mqtt_pub_args;
|
||||
|
||||
typedef enum {
|
||||
MQTT_STATE_INIT = 0,
|
||||
MQTT_STATE_DISCONNECTED,
|
||||
MQTT_STATE_CONNECTED,
|
||||
MQTT_STATE_ERROR,
|
||||
MQTT_STATE_STOPPED,
|
||||
} mqtt_client_state_t;
|
||||
|
||||
mqtt_client_state_t client_status = MQTT_STATE_INIT;
|
||||
|
||||
static esp_mqtt_client_handle_t client_handle = NULL;
|
||||
|
||||
static const uint8_t *s_own_cert_pem_start = NULL;
|
||||
static const uint8_t *s_own_cert_pem_end = NULL;
|
||||
static const uint8_t *s_own_key_pem_start = NULL;
|
||||
static const uint8_t *s_own_key_pem_end = NULL;
|
||||
static const uint8_t *s_ca_cert_pem_start = NULL;
|
||||
static const uint8_t *s_ca_cert_pem_end = NULL;
|
||||
|
||||
static void log_error_if_nonzero(const char *message, int error_code)
|
||||
{
|
||||
if (error_code != 0) {
|
||||
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Event handler registered to receive MQTT events
|
||||
*
|
||||
* This function is called by the MQTT client event loop.
|
||||
*
|
||||
* @param handler_args user data registered to the event.
|
||||
* @param base Event base for the handler(always MQTT Base in this example).
|
||||
* @param event_id The id for the received event.
|
||||
* @param event_data The data for the event, esp_mqtt_event_handle_t.
|
||||
*/
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
switch ((esp_mqtt_event_id_t)event_id) {
|
||||
case MQTT_EVENT_BEFORE_CONNECT:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
|
||||
break;
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
client_status = MQTT_STATE_CONNECTED;
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
client_status = MQTT_STATE_DISCONNECTED;
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
client_status = MQTT_STATE_ERROR;
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
|
||||
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *mqtt_state_to_string(mqtt_client_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case MQTT_STATE_INIT:
|
||||
return "Initializing";
|
||||
case MQTT_STATE_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
case MQTT_STATE_CONNECTED:
|
||||
return "Connected";
|
||||
case MQTT_STATE_ERROR:
|
||||
return "Error";
|
||||
case MQTT_STATE_STOPPED:
|
||||
return "Disconnected and Stopped";
|
||||
default:
|
||||
return "Unknown State";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int do_mqtt_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&mqtt_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, mqtt_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mqtt_args.status->count > 0) {
|
||||
ESP_LOGI(TAG, "MQTT Client Status: %s\n", mqtt_state_to_string(client_status));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mqtt_args.connect->count > 0) {
|
||||
|
||||
if (client_handle != NULL) {
|
||||
ESP_LOGW(TAG, "mqtt client already connected");
|
||||
ESP_LOGI(TAG, "Try: %s", DISCONNECT_HELP_MSG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *uri = CONFIG_MQTT_BROKER_URL;
|
||||
if (mqtt_args.uri->count > 0) {
|
||||
uri = (char *)mqtt_args.uri->sval[0];
|
||||
}
|
||||
|
||||
esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = uri,
|
||||
};
|
||||
|
||||
if ((mqtt_args.username->count > 0) && (mqtt_args.password->count > 0)) {
|
||||
mqtt_cfg.credentials.username = mqtt_args.username->sval[0];
|
||||
mqtt_cfg.credentials.authentication.password = mqtt_args.password->sval[0];
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "broker: %s", mqtt_cfg.broker.address.uri);
|
||||
|
||||
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
/* Ensure --use_internal_bundle and --cafile are mutually exclusive */
|
||||
if ((mqtt_args.use_internal_bundle->count > 0) && (mqtt_args.cafile->count > 0)) {
|
||||
ESP_LOGE(TAG, "Error: Options can't be used together. Use either --use-internal-bundle or --cafile. \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mqtt_args.use_internal_bundle->count > 0) {
|
||||
mqtt_cfg.broker.verification.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mqtt_args.cafile->count > 0) {
|
||||
if (s_ca_cert_pem_start && s_ca_cert_pem_end) {
|
||||
mqtt_cfg.broker.verification.certificate = (const char *)s_ca_cert_pem_start;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "cafile not provided");
|
||||
}
|
||||
}
|
||||
|
||||
if (mqtt_args.cert->count > 0) {
|
||||
if (s_own_cert_pem_start && s_own_cert_pem_end) {
|
||||
mqtt_cfg.credentials.authentication.certificate = (const char *)s_own_cert_pem_start;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "cert not provided");
|
||||
}
|
||||
|
||||
if (mqtt_args.key->count > 0) {
|
||||
if (s_own_key_pem_start && s_own_key_pem_end) {
|
||||
mqtt_cfg.credentials.authentication.key = (const char *)s_own_key_pem_start;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "key not provided");
|
||||
}
|
||||
} else {
|
||||
mqtt_cfg.credentials.authentication.key = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
client_handle = esp_mqtt_client_init(&mqtt_cfg);
|
||||
if (client_handle == NULL) {
|
||||
ESP_LOGE(TAG, "ERROR: Client init");
|
||||
ESP_LOGI(TAG, "Try: %s", DISCONNECT_HELP_MSG);
|
||||
ESP_LOGE(TAG, CONNECT_HELP_MSG);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
|
||||
esp_mqtt_client_register_event(client_handle, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(client_handle);
|
||||
|
||||
} else if (mqtt_args.disconnect->count > 0) {
|
||||
ESP_LOGD(TAG, "Disconnect command received:");
|
||||
|
||||
if (client_handle == NULL) {
|
||||
ESP_LOGE(TAG, "mqtt client not connected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (esp_mqtt_client_stop(client_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to stop mqtt client task");
|
||||
return 1;
|
||||
}
|
||||
|
||||
client_handle = NULL;
|
||||
client_status = MQTT_STATE_STOPPED;
|
||||
ESP_LOGI(TAG, "mqtt client disconnected and stopped");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t set_mqtt_client_cert(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i)
|
||||
{
|
||||
if (!client_cert_pem_start_i || !client_cert_pem_end_i ||
|
||||
(client_cert_pem_start_i > client_cert_pem_end_i)) {
|
||||
ESP_LOGE(TAG, "Invalid mqtt Client certs(%d)\n", __LINE__);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
s_own_cert_pem_start = client_cert_pem_start_i;
|
||||
s_own_cert_pem_end = client_cert_pem_end_i;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t set_mqtt_client_key(const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i)
|
||||
{
|
||||
if (client_key_pem_start_i && client_key_pem_end_i &&
|
||||
(client_key_pem_start_i >= client_key_pem_end_i)) {
|
||||
ESP_LOGE(TAG, "Invalid mqtt Client key(%d)\n", __LINE__);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
s_own_key_pem_start = client_key_pem_start_i;
|
||||
s_own_key_pem_end = client_key_pem_end_i;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t set_mqtt_broker_certs(const uint8_t *ca_cert_pem_start_i, const uint8_t *ca_cert_pem_end_i)
|
||||
{
|
||||
if (!ca_cert_pem_start_i || !ca_cert_pem_end_i ||
|
||||
(ca_cert_pem_start_i > ca_cert_pem_end_i)) {
|
||||
ESP_LOGE(TAG, "Invalid mqtt ca cert(%d)\n", __LINE__);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
s_ca_cert_pem_start = ca_cert_pem_start_i;
|
||||
s_ca_cert_pem_end = ca_cert_pem_end_i;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static int do_mqtt_sub_cmd(int argc, char **argv)
|
||||
{
|
||||
int msg_id;
|
||||
int nerrors = arg_parse(argc, argv, (void **)&mqtt_sub_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, mqtt_sub_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (client_handle == NULL) {
|
||||
ESP_LOGE(TAG, "mqtt client not connected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mqtt_sub_args.unsubscribe->count > 0) {
|
||||
if (mqtt_sub_args.topic->count <= 0) {
|
||||
ESP_LOGE(TAG, UNSUBSCRIBE_HELP_MSG);
|
||||
return 0;
|
||||
}
|
||||
char *topic = (char *)mqtt_sub_args.topic->sval[0];
|
||||
|
||||
msg_id = esp_mqtt_client_unsubscribe(client_handle, mqtt_sub_args.topic->sval[0]);
|
||||
ESP_LOGI(TAG, "Unsubscribe successful, msg_id=%d, topic=%s", msg_id, topic);
|
||||
|
||||
} else {
|
||||
if (mqtt_sub_args.topic->count <= 0) {
|
||||
ESP_LOGE(TAG, SUBSCRIBE_HELP_MSG);
|
||||
return 0;
|
||||
}
|
||||
char *topic = (char *)mqtt_sub_args.topic->sval[0];
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client_handle, topic, 0);
|
||||
ESP_LOGI(TAG, "Subscribe successful, msg_id=%d, topic=%s", msg_id, topic);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int do_mqtt_pub_cmd(int argc, char **argv)
|
||||
{
|
||||
int msg_id;
|
||||
int nerrors = arg_parse(argc, argv, (void **)&mqtt_pub_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, mqtt_pub_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (client_handle == NULL) {
|
||||
ESP_LOGE(TAG, "mqtt client not connected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((mqtt_pub_args.topic->count <= 0) || (mqtt_pub_args.message->count <= 0)) {
|
||||
ESP_LOGE(TAG, PUBLISH_HELP_MSG);
|
||||
}
|
||||
|
||||
msg_id = esp_mqtt_client_publish(client_handle,
|
||||
mqtt_pub_args.topic->sval[0],
|
||||
mqtt_pub_args.message->sval[0],
|
||||
0, 1, 0);
|
||||
if (msg_id == -1) {
|
||||
ESP_LOGE(TAG, "mqtt client not connected");
|
||||
return 0;
|
||||
}
|
||||
ESP_LOGI(TAG, "Publish successful, msg_id=%d, topic=%s, data=%s",
|
||||
msg_id, mqtt_pub_args.topic->sval[0], mqtt_pub_args.message->sval[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers the mqtt commands.
|
||||
*
|
||||
* @return
|
||||
* - esp_err_t
|
||||
*/
|
||||
esp_err_t console_cmd_mqtt_register(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
/* Register mqtt */
|
||||
mqtt_args.connect = arg_lit0("C", "connect", "Connect to a broker (flag, no argument)");
|
||||
mqtt_args.uri = arg_str0("h", "host", "<host>", "Specify the host uri to connect to");
|
||||
mqtt_args.status = arg_lit0("s", "status", "Displays the status of the mqtt client (flag, no argument)");
|
||||
mqtt_args.username = arg_str0("u", "username", "<username>", "Provide a username to be used for authenticating with the broker");
|
||||
mqtt_args.password = arg_str0("P", "password", "<password>", "Provide a password to be used for authenticating with the broker");
|
||||
mqtt_args.cert = arg_lit0(NULL, "cert", "Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)");
|
||||
mqtt_args.key = arg_lit0(NULL, "key", "Define the PEM encoded private key for this client, if required by the broker (flag, no argument)");
|
||||
mqtt_args.cafile = arg_lit0(NULL, "cafile", "Define the PEM encoded CA certificates that are trusted (flag, no argument)");
|
||||
#if defined(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
mqtt_args.use_internal_bundle = arg_lit0(NULL, "use-internal-bundle", "Use the internal certificate bundle for TLS (flag, no argument)");
|
||||
#endif
|
||||
mqtt_args.disconnect = arg_lit0("D", "disconnect", "Disconnect from the broker (flag, no argument)");
|
||||
mqtt_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t mqtt_cmd = {
|
||||
.command = "mqtt",
|
||||
.help = "mqtt command",
|
||||
.hint = NULL,
|
||||
.func = &do_mqtt_cmd,
|
||||
.argtable = &mqtt_args
|
||||
};
|
||||
|
||||
ret = esp_console_cmd_register(&mqtt_cmd);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Unable to register mqtt");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register mqtt_pub */
|
||||
mqtt_pub_args.topic = arg_str0("t", "topic", "<topic>", "Topic to Subscribe/Publish");
|
||||
mqtt_pub_args.message = arg_str0("m", "message", "<message>", "Message to Publish");
|
||||
mqtt_pub_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t mqtt_pub_cmd = {
|
||||
.command = "mqtt_pub",
|
||||
.help = "mqtt publish command",
|
||||
.hint = NULL,
|
||||
.func = &do_mqtt_pub_cmd,
|
||||
.argtable = &mqtt_pub_args
|
||||
};
|
||||
|
||||
ret = esp_console_cmd_register(&mqtt_pub_cmd);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Unable to register mqtt_pub");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register mqtt_sub */
|
||||
mqtt_sub_args.topic = arg_str0("t", "topic", "<topic>", "Topic to Subscribe/Publish");
|
||||
mqtt_sub_args.unsubscribe = arg_lit0("U", "unsubscribe", "Unsubscribe from a topic");
|
||||
mqtt_sub_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t mqtt_sub_cmd = {
|
||||
.command = "mqtt_sub",
|
||||
.help = "mqtt subscribe command",
|
||||
.hint = NULL,
|
||||
.func = &do_mqtt_sub_cmd,
|
||||
.argtable = &mqtt_sub_args
|
||||
};
|
||||
|
||||
ret = esp_console_cmd_register(&mqtt_sub_cmd);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Unable to register mqtt_sub");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
73
components/console_cmd_mqtt/console_mqtt.h
Normal file
73
components/console_cmd_mqtt/console_mqtt.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "console_simple_init.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Registers the mqtt command.
|
||||
*
|
||||
* @return
|
||||
* - esp_err_t
|
||||
*/
|
||||
esp_err_t console_cmd_mqtt_register(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set MQTT client certificate
|
||||
*
|
||||
* This function sets the MQTT client certificate for secure communication.
|
||||
* The function takes the PEM(Privacy Enhanced Mail) encoded certificate arguments.
|
||||
*
|
||||
* @param client_cert_pem_start_i Pointer to the beginning of the client certificate PEM data.
|
||||
* @param client_cert_pem_end_i Pointer to the end of the client certificate PEM data.
|
||||
*
|
||||
* @return
|
||||
* ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on invalid arguments
|
||||
*/
|
||||
esp_err_t set_mqtt_client_cert(const uint8_t *client_cert_pem_start_i, const uint8_t *client_cert_pem_end_i);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set MQTT client key
|
||||
*
|
||||
* This function sets the MQTT client key for secure communication.
|
||||
* The function takes the PEM(Privacy Enhanced Mail) encoded key arguments.
|
||||
*
|
||||
* @param client_key_pem_start_i Pointer to the beginning of the client key PEM data.
|
||||
* @param client_key_pem_end_i Pointer to the end of the client key PEM data.
|
||||
*
|
||||
* @return
|
||||
* ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on invalid arguments
|
||||
*/
|
||||
esp_err_t set_mqtt_client_key(const uint8_t *client_key_pem_start_i, const uint8_t *client_key_pem_end_i);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set MQTT broker certificate
|
||||
*
|
||||
* This function sets the MQTT broker certificate for secure communication.
|
||||
* The function takes the PEM(Privacy Enhanced Mail) encoded broker certificate arguments.
|
||||
*
|
||||
* @param broker_cert_pem_start_i Pointer to the beginning of the broker certificate PEM data.
|
||||
* @param broker_cert_pem_end_i Pointer to the end of the broker certificate PEM data.
|
||||
*
|
||||
* @return
|
||||
* ESP_OK on success
|
||||
* ESP_ERR_INVALID_ARG on invalid arguments
|
||||
*/
|
||||
esp_err_t set_mqtt_broker_certs(const uint8_t *broker_cert_pem_start_i, const uint8_t *broker_cert_pem_end_i);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,11 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mqtt_ssl_auth_console)
|
||||
|
||||
# Certs for mqtts://test.mosquitto.org:8884
|
||||
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/client.crt" TEXT)
|
||||
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/client.key" TEXT)
|
||||
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "certs/mosquitto.org.pem" TEXT)
|
@ -0,0 +1,174 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP-MQTT SSL Authentication Console
|
||||
|
||||
This example demonstrates the use of the MQTT command-line component to connect to both secured and unsecured MQTT brokers. It provides multiple modes of connection, including:
|
||||
|
||||
* Unsecured transport: Connect to a broker without encryption.
|
||||
* SSL/TLS transport: Securely connect using SSL/TLS with options for:
|
||||
* Validating the broker using a provided CA certificate.
|
||||
* Validating the broker using the internal certificate bundle.
|
||||
* Performing SSL mutual authentication using client and broker certificates.
|
||||
|
||||
Additionally, the example allows subscribing to topics, unsubscribing from topics, and publishing messages to a specified topic through commands. Connections to the broker at test.mosquitto.org are used to demonstrate these features.
|
||||
(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org)
|
||||
|
||||
It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet.
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
* Generate your client keys and certificate (specific to testing with Mosquitto broker)
|
||||
|
||||
Note: The following steps are for testing with the Mosquitto broker. If you're using a different broker, you may need to adapt the steps to meet your broker's certificate and key requirements.
|
||||
|
||||
#### Steps for SSL Mutual authentication:
|
||||
Navigate to the certs directory
|
||||
|
||||
```
|
||||
cd certs
|
||||
```
|
||||
|
||||
Generate a client key and a CSR. When you are generating the CSR, do not use the default values. At a minimum, the CSR must include the Country, Organisation and Common Name fields.
|
||||
|
||||
```
|
||||
openssl genrsa -out client.key
|
||||
openssl req -out client.csr -key client.key -new
|
||||
```
|
||||
|
||||
Paste the generated CSR in the [Mosquitto test certificate signer](https://test.mosquitto.org/ssl/index.php), click Submit and copy the downloaded `client.crt` in the `main` directory.
|
||||
|
||||
Please note, that the supplied files `client.crt` and `client.key` in the `main` directory are only placeholders for your client certificate and key (i.e. the example "as is" would compile but would not connect to the broker)
|
||||
|
||||
The broker certificate `mosquitto.org.pem` can be downloaded in pem format from [mosquitto.org.crt](https://test.mosquitto.org/ssl/mosquitto.org.crt). Convert it to `mosquitto.org.pem` simply by renaming it.
|
||||
|
||||
Note: If your certificate and key file names differ, update the root `CMakeLists.txt` file and main/`mqtt_ssl_auth_console.c` accordingly.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
Warning: This example might need a bigger app partition size if you're compiling it for debug. To ensere this issue doesn't happen "optimize for size is enabled in menuconfig.
|
||||
|
||||
### Command Usage:
|
||||
```
|
||||
esp> help
|
||||
help [<string>]
|
||||
Print the summary of all registered commands if no arguments are given,
|
||||
otherwise print summary of given command.
|
||||
<string> Name of command
|
||||
|
||||
mqtt [-CsD] [-h <host>] [-u <username>] [-P <password>] [--cert] [--key] [--cafile]
|
||||
mqtt command
|
||||
-C, --connect Connect to a broker (flag, no argument)
|
||||
-h, --host=<host> Specify the host uri to connect to
|
||||
-s, --status Displays the status of the mqtt client (flag, no argument)
|
||||
-u, --username=<username> Provide a username to be used for authenticating with the broker
|
||||
-P, --password=<password> Provide a password to be used for authenticating with the broker
|
||||
--cert Define the PEM encoded certificate for this client, if required by the broker (flag, no argument)
|
||||
--key Define the PEM encoded private key for this client, if required by the broker (flag, no argument)
|
||||
--cafile Define the PEM encoded CA certificates that are trusted (flag, no argument)
|
||||
--use-internal-bundle Use the internal certificate bundle for TLS (flag, no argument)
|
||||
-D, --disconnect Disconnect from the broker (flag, no argument)
|
||||
|
||||
mqtt_pub [-t <topic>] [-m <message>]
|
||||
mqtt publish command
|
||||
-t, --topic=<topic> Topic to Subscribe/Publish
|
||||
-m, --message=<message> Message to Publish
|
||||
|
||||
mqtt_sub [-U] [-t <topic>]
|
||||
mqtt subscribe command
|
||||
-t, --topic=<topic> Topic to Subscribe/Publish
|
||||
-U, --unsubscribe Unsubscribe from a topic
|
||||
```
|
||||
|
||||
### Connection:
|
||||
|
||||
#### Connect without Validating the Broker:
|
||||
This option connects to the broker without validating its certificate. It is not secure.
|
||||
```
|
||||
mqtt -h mqtts://test.mosquitto.org -C
|
||||
```
|
||||
or
|
||||
```
|
||||
mqtt -h mqtts://mqtt.eclipseprojects.io -C
|
||||
```
|
||||
|
||||
#### Validate the Broker using the Internal Certificate Bundle:
|
||||
This option uses the ESP-IDF's built-in certificate bundle to verify the broker's identity.
|
||||
```
|
||||
mqtt -h mqtts://mqtt.eclipseprojects.io -C --use-internal-bundle
|
||||
```
|
||||
or
|
||||
```
|
||||
mqtt -h mqtts://test.mosquitto.org -C --use-internal-bundle
|
||||
```
|
||||
|
||||
#### Validate the Broker using a Provided CA Certificate:
|
||||
This option requires you to provide the broker's CA certificate for validation.
|
||||
```
|
||||
mqtt -h mqtts://test.mosquitto.org -C --cafile
|
||||
```
|
||||
|
||||
|
||||
#### SSL Mutual Authentication(encrypted, client certificate required):
|
||||
This option performs client authentication in addition to broker validation. It requires the client certificate, private key, and broker CA certificate.
|
||||
```
|
||||
mqtt -h mqtts://test.mosquitto.org:8884 -C --cert --key --cafile
|
||||
```
|
||||
or
|
||||
```
|
||||
mqtt -h mqtts://test.mosquitto.org:8884 -C --cert --key --use-internal-bundle
|
||||
```
|
||||
|
||||
Note: In this example, the broker's certificate is included in the certificate bundle (refer to sdkconfig.default).
|
||||
|
||||
### Disconnect:
|
||||
```
|
||||
esp> mqtt -D
|
||||
I (1189949) console_mqtt: mqtt client disconnected
|
||||
```
|
||||
|
||||
### Subscribe/Unsubscribe:
|
||||
```
|
||||
esp> mqtt_sub -t test0
|
||||
I (897289) console_mqtt: Subscribe successful, msg_id=57425, topic=test0
|
||||
esp> I (897799) console_mqtt: MQTT_EVENT_SUBSCRIBED, msg_id=57425
|
||||
esp>
|
||||
esp> mqtt_sub -U -t test0
|
||||
I (902009) console_mqtt: Unsubscribe successful, msg_id=27663, topic=test0
|
||||
esp> I (902509) console_mqtt: MQTT_EVENT_UNSUBSCRIBED, msg_id=27663
|
||||
```
|
||||
|
||||
### Publish:
|
||||
```
|
||||
esp> mqtt_pub -t test0 -m "Hello, Testing 123"
|
||||
I (999469) console_mqtt: Publish successful, msg_id=55776, topic=test0, data=Hello, Testing 123
|
||||
I (1000009) console_mqtt: MQTT_EVENT_PUBLISHED, msg_id=55776
|
||||
esp>
|
||||
```
|
||||
|
||||
### Receiving data event:
|
||||
```
|
||||
esp> I (999999) console_mqtt: MQTT_EVENT_DATA
|
||||
I (999999) console_mqtt: TOPIC=test0
|
||||
|
||||
I (999999) console_mqtt: DATA=Hello, Testing 123
|
||||
```
|
@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
|
||||
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
|
||||
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
|
||||
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
|
||||
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
|
||||
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
|
||||
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
|
||||
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
|
||||
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
|
||||
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
|
||||
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
|
||||
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
|
||||
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
|
||||
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
|
||||
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
|
||||
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
|
||||
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
|
||||
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
|
||||
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
|
||||
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mqtt_ssl_auth_console.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
console_cmd_mqtt:
|
||||
version: "*"
|
||||
override_path: '../../../'
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include <netdb.h>
|
||||
#include "console_mqtt.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
// Certs for mqtts://test.mosquitto.org:8884
|
||||
extern const uint8_t g_client_cert_pem_start[] asm("_binary_client_crt_start");
|
||||
extern const uint8_t g_client_cert_pem_end[] asm("_binary_client_crt_end");
|
||||
extern const uint8_t g_client_key_pem_start[] asm("_binary_client_key_start");
|
||||
extern const uint8_t g_client_key_pem_end[] asm("_binary_client_key_end");
|
||||
extern const uint8_t g_broker_cert_pem_start[] asm("_binary_mosquitto_org_pem_start");
|
||||
extern const uint8_t g_broker_cert_pem_end[] asm("_binary_mosquitto_org_pem_end");
|
||||
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_err_t ret = nvs_flash_init(); //Initialize NVS
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* ${IDF_PATH}/examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
// Initialize console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
ESP_ERROR_CHECK(console_cmd_all_register());
|
||||
|
||||
set_mqtt_client_cert(g_client_cert_pem_start, g_client_cert_pem_end);
|
||||
set_mqtt_client_key(g_client_key_pem_start, g_client_key_pem_end);
|
||||
set_mqtt_broker_certs(g_broker_cert_pem_start, g_broker_cert_pem_end);
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
def test_examples_ifconfig_command(dut):
|
||||
dut.expect('esp>', timeout=30)
|
||||
dut.write('help mqtt')
|
||||
dut.expect(r'mqtt \[-CsD\] \[-h <host>\] \[-u <username>\] \[-P <password>\] \[--cert\] \[--key\] \[--cafile\]', timeout=30)
|
||||
|
||||
dut.write('help mqtt_pub')
|
||||
dut.expect(r'mqtt_pub \[-t <topic>\] \[-m <message>\]', timeout=30)
|
||||
|
||||
dut.write('help mqtt_sub')
|
||||
dut.expect(r'mqtt_sub \[-U\] \[-t <topic>\]', timeout=30)
|
@ -0,0 +1,7 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="certs/mosquitto.org.pem"
|
11
components/console_cmd_mqtt/idf_component.yml
Normal file
11
components/console_cmd_mqtt/idf_component.yml
Normal file
@ -0,0 +1,11 @@
|
||||
version: 1.0.0
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_mqtt
|
||||
description: The component provides a console where the 'mqtt' command can be executed.
|
||||
license: Apache-2.0
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.0'
|
||||
espressif/console_simple_init:
|
||||
version: '>=1.1.0'
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(eppp): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py eppp_link
|
||||
tag_format: eppp-v$version
|
||||
version: 0.2.0
|
||||
version: 0.3.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [0.3.0](https://github.com/espressif/esp-protocols/commits/eppp-v0.3.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for TUN interface ([2ff150c3](https://github.com/espressif/esp-protocols/commit/2ff150c3))
|
||||
- Add support for transport via Ethernet link ([a21ce883](https://github.com/espressif/esp-protocols/commit/a21ce883))
|
||||
|
||||
## [0.2.0](https://github.com/espressif/esp-protocols/commits/eppp-v0.2.0)
|
||||
|
||||
### Features
|
||||
|
@ -1,3 +1,33 @@
|
||||
idf_component_register(SRCS eppp_link.c eppp_sdio_slave.c eppp_sdio_host.c
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi)
|
||||
else()
|
||||
set(driver_deps driver)
|
||||
endif()
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_ETH)
|
||||
set(transport_src eppp_eth.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_SPI)
|
||||
set(transport_src eppp_spi.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_UART)
|
||||
set(transport_src eppp_uart.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_SDIO)
|
||||
set(transport_src eppp_sdio.c eppp_sdio_slave.c eppp_sdio_host.c)
|
||||
endif()
|
||||
|
||||
if(NOT CONFIG_EPPP_LINK_USES_PPP)
|
||||
set(netif_src eppp_netif_tun.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS eppp_link.c ${transport_src} ${netif_src}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver)
|
||||
PRIV_REQUIRES esp_netif esp_timer esp_eth ${driver_deps})
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_ETH)
|
||||
idf_component_optional_requires(PRIVATE ethernet_init espressif__ethernet_init)
|
||||
endif()
|
||||
|
@ -1,10 +1,16 @@
|
||||
menu "eppp_link"
|
||||
|
||||
config EPPP_LINK_USES_LWIP
|
||||
bool
|
||||
default "y"
|
||||
config EPPP_LINK_USES_PPP
|
||||
bool "Use PPP network interface"
|
||||
default "n"
|
||||
select LWIP_PPP_SUPPORT
|
||||
select LWIP_PPP_SERVER_SUPPORT
|
||||
help
|
||||
Enable this option to use PPP network interface.
|
||||
This is useful when pairing with another PPP device,
|
||||
e.g. pppd service on Linux.
|
||||
By default EPPP_LINK uses plain TUN interface,
|
||||
relying on transports to split on packet boundaries.
|
||||
|
||||
choice EPPP_LINK_DEVICE
|
||||
prompt "Choose PPP device"
|
||||
@ -28,6 +34,16 @@ menu "eppp_link"
|
||||
help
|
||||
Use SDIO.
|
||||
|
||||
config EPPP_LINK_DEVICE_ETH
|
||||
bool "Ethernet"
|
||||
depends on SOC_EMAC_SUPPORTED
|
||||
help
|
||||
Use Ethernet.
|
||||
This transport could employ a full fledged Ethernet connection
|
||||
between two EPPP nodes via standard Ethernet cable.
|
||||
It could be also effectively connected directly on PCB, EMAC to EMAC,
|
||||
without any Ethernet PHY chips (using eth_dummy_phy driver).
|
||||
|
||||
endchoice
|
||||
|
||||
config EPPP_LINK_CONN_MAX_RETRY
|
||||
@ -67,4 +83,14 @@ menu "eppp_link"
|
||||
|
||||
endchoice
|
||||
|
||||
config EPPP_LINK_ETHERNET_OUR_ADDRESS
|
||||
string "MAC address our local node"
|
||||
default "06:00:00:00:00:01"
|
||||
depends on EPPP_LINK_DEVICE_ETH
|
||||
|
||||
config EPPP_LINK_ETHERNET_THEIR_ADDRESS
|
||||
string "MAC address the remote node"
|
||||
default "06:00:00:00:00:02"
|
||||
depends on EPPP_LINK_DEVICE_ETH
|
||||
|
||||
endmenu
|
||||
|
@ -1,24 +1,45 @@
|
||||
# ESP PPP Link component (eppp_link)
|
||||
|
||||
The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server (slave), the other one as PPP client (host).
|
||||
This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi
|
||||
The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server, the other one as PPP client.
|
||||
This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol (if enabled) to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi.
|
||||
Uses simplified TUN network interface by default to enable faster data transfer on non-UART transports.
|
||||
|
||||
## Typical application
|
||||
|
||||
Using this component we can construct a WiFi connectivity gateway on PPP channel. The below diagram depicts an application where
|
||||
PPP server is running on a WiFi capable chip with NAPT module translating packets between WiFi and PPPoS interface.
|
||||
We usually call this node a SLAVE microcontroller. The "HOST" microcontroller runs PPP client and connects only to the serial line,
|
||||
brings in the WiFi connectivity from the "SLAVE" microcontroller.
|
||||
We usually call this node a communication coprocessor, or a "SLAVE" microcontroller.
|
||||
The main microcontroller (sometimes also called the "HOST") runs PPP client and connects only to the serial line,
|
||||
brings in the WiFi connectivity from the communication coprocessor.
|
||||
|
||||
```
|
||||
SLAVE micro HOST micro
|
||||
\|/ +----------------+ +----------------+
|
||||
| | | serial line | |
|
||||
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO =====| PPPoS client |
|
||||
| (server)| | |
|
||||
+----------------+ +----------------+
|
||||
Communication coprocessor Main microcontroller
|
||||
\|/ +----------------+ +----------------+
|
||||
| | | (serial) line | |
|
||||
+---+ WiFi NAT PPPoS |=== UART / SPI / SDIO / ETH ===| PPPoS client |
|
||||
| (server)| | |
|
||||
+----------------+ +----------------+
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Choose the transport layer
|
||||
|
||||
Use `idf.py menuconfig` to select the transport layer:
|
||||
|
||||
* `CONFIG_EPPP_LINK_UART` -- Use UART transport layer
|
||||
* `CONFIG_EPPP_LINK_SPI` -- Use SPI transport layer
|
||||
* `CONFIG_EPPP_LINK_SDIO` -- Use SDIO transport layer
|
||||
* `CONFIG_EPPP_LINK_ETHERNET` -- Use Ethernet transport
|
||||
- Note: Ethernet creates it's own task, so calling `eppp_perform()` would not work
|
||||
- Note: Add dependency to ethernet_init component to use other Ethernet drivers
|
||||
- Note: You can override functions `eppp_transport_ethernet_deinit()` and `eppp_transport_ethernet_init()` to use your own Ethernet driver
|
||||
|
||||
### Choose the network interface
|
||||
|
||||
Use PPP netif for UART; Keep the default (TUN) for others
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Client
|
||||
@ -55,3 +76,9 @@ Tested with WiFi-NAPT example
|
||||
|
||||
* TCP - 9Mbits/s
|
||||
* UDP - 11Mbits/s
|
||||
|
||||
### Ethernet
|
||||
|
||||
- Internal EMAC with real PHY chip
|
||||
* TCP - 5Mbits/s
|
||||
* UDP - 8Mbits/s
|
||||
|
210
components/eppp_link/eppp_eth.c
Normal file
210
components/eppp_link/eppp_eth.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_eth_spec.h"
|
||||
#include "eppp_transport_eth.h"
|
||||
// Use Ethernet Init component if available
|
||||
// (otherwise use just simple init/deinit with generic MAC/PHY)
|
||||
#if __has_include("ethernet_init.h")
|
||||
#define USE_ETHERNET_INIT_COMPONENT
|
||||
#include "ethernet_init.h"
|
||||
#endif
|
||||
|
||||
typedef struct header {
|
||||
uint8_t dst[ETH_ADDR_LEN];
|
||||
uint8_t src[ETH_ADDR_LEN];
|
||||
uint16_t len;
|
||||
} __attribute__((packed)) header_t;
|
||||
|
||||
static const char *TAG = "eppp_ethernet";
|
||||
static bool s_is_connected = false;
|
||||
static esp_eth_handle_t *s_eth_handles = NULL;
|
||||
static uint8_t s_their_mac[ETH_ADDR_LEN];
|
||||
static uint8_t s_our_mac[ETH_ADDR_LEN];
|
||||
|
||||
#ifndef USE_ETHERNET_INIT_COMPONENT
|
||||
static esp_eth_handle_t s_handles[1] = { NULL };
|
||||
static esp_eth_mac_t *s_mac = NULL;
|
||||
static esp_eth_phy_t *s_phy = NULL;
|
||||
|
||||
static void simple_deinit(esp_eth_handle_t *handle_array[])
|
||||
{
|
||||
if (s_handles[0] != NULL) {
|
||||
esp_eth_driver_uninstall(s_handles[0]);
|
||||
s_handles[0] = NULL;
|
||||
}
|
||||
if (s_mac != NULL) {
|
||||
s_mac->del(s_mac);
|
||||
s_mac = NULL;
|
||||
}
|
||||
if (s_phy != NULL) {
|
||||
s_phy->del(s_phy);
|
||||
s_phy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t simple_init(struct eppp_config_ethernet_s *config, esp_eth_handle_t *handle_array[])
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
esp32_emac_config.smi_gpio.mdc_num = config->mdc_io;
|
||||
esp32_emac_config.smi_gpio.mdio_num = config->mdio_io;
|
||||
s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = config->phy_addr;
|
||||
phy_config.reset_gpio_num = config->rst_io;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
s_phy = esp_eth_phy_new_generic(&phy_config);
|
||||
#else
|
||||
s_phy = esp_eth_phy_new_ip101(&phy_config);
|
||||
#endif
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
|
||||
ESP_GOTO_ON_ERROR(esp_eth_driver_install(ð_config, &s_handles[0]), err, TAG, "Ethernet driver install failed");
|
||||
*handle_array = s_handles;
|
||||
return ESP_OK;
|
||||
err:
|
||||
simple_deinit(handle_array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // USE_ETHERNET_INIT_COMPONENT
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
s_is_connected = true;
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||
s_is_connected = false;
|
||||
break;
|
||||
case ETHERNET_EVENT_START:
|
||||
ESP_LOGI(TAG, "Ethernet Started");
|
||||
break;
|
||||
case ETHERNET_EVENT_STOP:
|
||||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t receive(esp_eth_handle_t h, uint8_t *buffer, uint32_t len, void *netif)
|
||||
{
|
||||
header_t *head = (header_t *)buffer;
|
||||
size_t packet_len = head->len;
|
||||
if (len >= packet_len) {
|
||||
esp_err_t ret = esp_netif_receive(netif, buffer + ETH_HEADER_LEN, packet_len, NULL);
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
__attribute__((weak)) esp_err_t eppp_transport_ethernet_init(struct eppp_config_ethernet_s *config, esp_eth_handle_t *handle_array[])
|
||||
{
|
||||
#ifdef USE_ETHERNET_INIT_COMPONENT
|
||||
uint8_t eth_port_cnt = 0;
|
||||
ESP_RETURN_ON_ERROR(ethernet_init_all(handle_array, ð_port_cnt), TAG, "Failed to init common eth drivers");
|
||||
ESP_RETURN_ON_FALSE(eth_port_cnt == 1, ESP_ERR_INVALID_ARG, TAG, "multiple Ethernet devices detected, please init only one");
|
||||
return ESP_OK;
|
||||
#else
|
||||
return simple_init(config, handle_array);
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((weak)) void eppp_transport_ethernet_deinit(esp_eth_handle_t *handle_array[])
|
||||
{
|
||||
#ifdef USE_ETHERNET_INIT_COMPONENT
|
||||
ethernet_deinit_all(s_eth_handles);
|
||||
#else
|
||||
simple_deinit(handle_array);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t eppp_transport_tx(void *h, void *buffer, size_t len)
|
||||
{
|
||||
static uint8_t out_buffer[ETH_HEADER_LEN];
|
||||
if (!s_is_connected) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// setup Ethernet header
|
||||
header_t *head = (header_t *)out_buffer;
|
||||
memcpy(head->dst, s_their_mac, ETH_ADDR_LEN);
|
||||
memcpy(head->src, s_our_mac, ETH_ADDR_LEN);
|
||||
head->len = len;
|
||||
// support only payloads with len <= ETH_MAX_PAYLOAD_LEN
|
||||
if (len > ETH_MAX_PAYLOAD_LEN) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_eth_transmit_vargs(s_eth_handles[0], 2, out_buffer, ETH_HEADER_LEN, buffer, len);
|
||||
}
|
||||
|
||||
static esp_err_t start_driver(esp_netif_t *esp_netif)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(esp_eth_update_input_path(s_eth_handles[0], receive, esp_netif), TAG, "Failed to set Ethernet Rx callback");
|
||||
sscanf(CONFIG_EPPP_LINK_ETHERNET_OUR_ADDRESS, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8,
|
||||
&s_our_mac[0], &s_our_mac[1], &s_our_mac[2], &s_our_mac[3], &s_our_mac[4], &s_our_mac[5]);
|
||||
|
||||
sscanf(CONFIG_EPPP_LINK_ETHERNET_THEIR_ADDRESS, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8,
|
||||
&s_their_mac[0], &s_their_mac[1], &s_their_mac[2], &s_their_mac[3], &s_their_mac[4], &s_their_mac[5]);
|
||||
esp_eth_ioctl(s_eth_handles[0], ETH_CMD_S_MAC_ADDR, s_our_mac);
|
||||
ESP_RETURN_ON_ERROR(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL), TAG, "Failed to register Ethernet handlers");
|
||||
ESP_RETURN_ON_ERROR(esp_eth_start(s_eth_handles[0]), TAG, "Failed to start Ethernet driver");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
eppp_transport_handle_t h = (eppp_transport_handle_t)args;
|
||||
ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null");
|
||||
h->base.netif = esp_netif;
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = h,
|
||||
.transmit = eppp_transport_tx,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config");
|
||||
ESP_LOGI(TAG, "EPPP Ethernet transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif));
|
||||
ESP_RETURN_ON_ERROR(start_driver(esp_netif), TAG, "Failed to start EPPP ethernet driver");
|
||||
ESP_LOGI(TAG, "EPPP Ethernet driver started");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
eppp_transport_handle_t eppp_eth_init(struct eppp_config_ethernet_s *config)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
|
||||
eppp_transport_handle_t h = calloc(1, sizeof(struct eppp_handle));
|
||||
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
|
||||
ESP_GOTO_ON_ERROR(eppp_transport_ethernet_init(config, &s_eth_handles), err, TAG, "Failed to init Ethernet transport");
|
||||
h->base.post_attach = post_attach;
|
||||
return h;
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void eppp_eth_deinit(eppp_transport_handle_t h)
|
||||
{
|
||||
esp_eth_stop(s_eth_handles[0]);
|
||||
eppp_transport_ethernet_deinit(&s_eth_handles);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -12,16 +12,17 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "eppp_link.h"
|
||||
#include "esp_serial_slave_link/essl_sdio.h"
|
||||
#include "eppp_transport_eth.h"
|
||||
#include "eppp_transport_spi.h"
|
||||
#include "eppp_transport_uart.h"
|
||||
#include "eppp_transport_sdio.h"
|
||||
#include "eppp_transport.h"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_rom_crc.h"
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
#include "driver/uart.h"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_ETH
|
||||
#define EPPP_NEEDS_TASK 0
|
||||
#else
|
||||
#define EPPP_NEEDS_TASK 1
|
||||
#endif
|
||||
|
||||
static const int GOT_IPV4 = BIT0;
|
||||
@ -33,204 +34,54 @@ static const char *TAG = "eppp_link";
|
||||
static int s_retry_num = 0;
|
||||
static int s_eppp_netif_count = 0; // used as a suffix for the netif key
|
||||
|
||||
|
||||
struct packet {
|
||||
size_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
#define MAX_PAYLOAD 1500
|
||||
#define MIN_TRIGGER_US 20
|
||||
#define SPI_HEADER_MAGIC 0x1234
|
||||
|
||||
static void timer_callback(void *arg);
|
||||
|
||||
struct header {
|
||||
uint16_t magic;
|
||||
uint16_t size;
|
||||
uint16_t next_size;
|
||||
uint16_t check;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum blocked_status {
|
||||
NONE,
|
||||
MASTER_BLOCKED,
|
||||
MASTER_WANTS_READ,
|
||||
SLAVE_BLOCKED,
|
||||
SLAVE_WANTS_WRITE,
|
||||
};
|
||||
|
||||
#endif // CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
|
||||
struct eppp_handle {
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
QueueHandle_t out_queue;
|
||||
QueueHandle_t ready_semaphore;
|
||||
spi_device_handle_t spi_device;
|
||||
spi_host_device_t spi_host;
|
||||
int gpio_intr;
|
||||
uint16_t next_size;
|
||||
uint16_t transaction_size;
|
||||
struct packet outbound;
|
||||
enum blocked_status blocked;
|
||||
uint32_t slave_last_edge;
|
||||
esp_timer_handle_t timer;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
QueueHandle_t uart_event_queue;
|
||||
uart_port_t uart_port;
|
||||
#endif
|
||||
esp_netif_t *netif;
|
||||
eppp_type_t role;
|
||||
bool stop;
|
||||
bool exited;
|
||||
bool netif_stop;
|
||||
};
|
||||
|
||||
typedef esp_err_t (*transmit_t)(void *h, void *buffer, size_t len);
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len);
|
||||
esp_err_t eppp_sdio_host_rx(esp_netif_t *netif);
|
||||
esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif);
|
||||
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len);
|
||||
esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config);
|
||||
esp_err_t eppp_sdio_slave_init(void);
|
||||
void eppp_sdio_slave_deinit(void);
|
||||
void eppp_sdio_host_deinit(void);
|
||||
#else
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = h;
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct packet buf = { };
|
||||
uint8_t *current_buffer = buffer;
|
||||
size_t remaining = len;
|
||||
do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform
|
||||
// fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD)
|
||||
size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining;
|
||||
buf.data = malloc(batch);
|
||||
if (buf.data == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate packet");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
buf.len = batch;
|
||||
remaining -= batch;
|
||||
memcpy(buf.data, current_buffer, batch);
|
||||
current_buffer += batch;
|
||||
BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0);
|
||||
if (ret != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to queue packet to slave!");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
} while (remaining > 0);
|
||||
|
||||
if (handle->role == EPPP_SERVER && handle->blocked == SLAVE_BLOCKED) {
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - handle->slave_last_edge;
|
||||
if (diff < MIN_TRIGGER_US) {
|
||||
esp_rom_delay_us(MIN_TRIGGER_US - diff);
|
||||
}
|
||||
gpio_set_level(handle->gpio_intr, 0);
|
||||
}
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_VERBOSE);
|
||||
uart_write_bytes(handle->uart_port, buffer, len);
|
||||
#endif // DEVICE UART or SPI
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void netif_deinit(esp_netif_t *netif)
|
||||
void eppp_netif_deinit(esp_netif_t *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h == NULL) {
|
||||
return;
|
||||
}
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct packet buf = { };
|
||||
while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) {
|
||||
if (buf.len > 0) {
|
||||
free(buf.data);
|
||||
}
|
||||
}
|
||||
vQueueDelete(h->out_queue);
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
#endif
|
||||
free(h);
|
||||
esp_netif_destroy(netif);
|
||||
if (s_eppp_netif_count > 0) {
|
||||
s_eppp_netif_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config)
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
#define NETSTACK_CONFIG() ESP_NETIF_NETSTACK_DEFAULT_PPP
|
||||
#else
|
||||
#define NETSTACK_CONFIG() g_eppp_netif_config_tun
|
||||
extern esp_netif_netstack_config_t *g_eppp_netif_config_tun;
|
||||
#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \
|
||||
{ \
|
||||
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \
|
||||
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \
|
||||
.flags = ESP_NETIF_FLAG_EVENT_IP_MODIFIED, \
|
||||
.get_ip_event = IP_EVENT_PPP_GOT_IP, \
|
||||
.lost_ip_event = IP_EVENT_PPP_LOST_IP, \
|
||||
.if_key = "EPPP_TUN", \
|
||||
.if_desc = "eppp", \
|
||||
.route_prio = 1, \
|
||||
.bridge_info = NULL \
|
||||
};
|
||||
#endif // CONFIG_EPPP_LINK_USES_PPP
|
||||
|
||||
esp_netif_t *eppp_netif_init(eppp_type_t role, eppp_transport_handle_t h, eppp_config_t *eppp_config)
|
||||
{
|
||||
if (s_eppp_netif_count > 9) { // Limit to max 10 netifs, since we use "EPPPx" as the unique key (where x is 0-9)
|
||||
ESP_LOGE(TAG, "Cannot create more than 10 instances");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create the object first
|
||||
struct eppp_handle *h = calloc(1, sizeof(struct eppp_handle));
|
||||
if (!h) {
|
||||
ESP_LOGE(TAG, "Failed to allocate eppp_handle");
|
||||
return NULL;
|
||||
}
|
||||
h->role = role;
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet));
|
||||
if (!h->out_queue) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
if (role == EPPP_CLIENT) {
|
||||
h->ready_semaphore = xSemaphoreCreateBinary();
|
||||
if (!h->ready_semaphore) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
vQueueDelete(h->out_queue);
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
h->transaction_size = 0;
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
if (role == EPPP_SERVER) {
|
||||
esp_timer_create_args_t args = {
|
||||
.callback = &timer_callback,
|
||||
.arg = h,
|
||||
.name = "timer"
|
||||
};
|
||||
if (esp_timer_create(&args, &h->timer) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
vQueueDelete(h->out_queue);
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = h,
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
.transmit = role == EPPP_CLIENT ? eppp_sdio_host_tx : eppp_sdio_slave_tx,
|
||||
#else
|
||||
.transmit = transmit,
|
||||
#endif
|
||||
};
|
||||
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
|
||||
#else
|
||||
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_SLIP();
|
||||
esp_netif_ip_info_t slip_ip4 = {};
|
||||
slip_ip4.ip.addr = eppp_config->ppp.our_ip4_addr.addr;
|
||||
slip_ip4.gw.addr = eppp_config->ppp.their_ip4_addr.addr;
|
||||
slip_ip4.netmask.addr = ESP_IP4TOADDR(255, 255, 255, 0);
|
||||
base_netif_cfg.ip_info = &slip_ip4;
|
||||
#endif
|
||||
char if_key[] = "EPPP0"; // netif key needs to be unique
|
||||
if_key[sizeof(if_key) - 2 /* 2 = two chars before the terminator */ ] += s_eppp_netif_count++;
|
||||
base_netif_cfg.if_key = if_key;
|
||||
@ -242,25 +93,27 @@ static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config)
|
||||
if (eppp_config->ppp.netif_prio) {
|
||||
base_netif_cfg.route_prio = eppp_config->ppp.netif_prio;
|
||||
}
|
||||
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
|
||||
.driver = ppp_driver_cfg,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
|
||||
};
|
||||
esp_netif_config_t netif_config = { .base = &base_netif_cfg,
|
||||
.driver = NULL,
|
||||
.stack = NETSTACK_CONFIG(),
|
||||
};
|
||||
|
||||
esp_netif_t *netif = esp_netif_new(&netif_ppp_config);
|
||||
if (!netif) {
|
||||
ESP_LOGE(TAG, "Failed to create esp_netif");
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
vQueueDelete(h->out_queue);
|
||||
if (h->ready_semaphore) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
#endif
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
esp_netif_t *netif = esp_netif_new(&netif_config);
|
||||
esp_netif_ppp_config_t netif_params;
|
||||
ESP_GOTO_ON_ERROR(esp_netif_ppp_get_params(netif, &netif_params), err, TAG, "Failed to get PPP params");
|
||||
netif_params.ppp_our_ip4_addr = eppp_config->ppp.our_ip4_addr;
|
||||
netif_params.ppp_their_ip4_addr = eppp_config->ppp.their_ip4_addr;
|
||||
netif_params.ppp_error_event_enabled = true;
|
||||
ESP_GOTO_ON_ERROR(esp_netif_ppp_set_params(netif, &netif_params), err, TAG, "Failed to set PPP params");
|
||||
return netif;
|
||||
|
||||
err:
|
||||
esp_netif_destroy(netif);
|
||||
return NULL;
|
||||
#else
|
||||
return esp_netif_new(&netif_config);
|
||||
#endif // CONFIG_EPPP_LINK_USES_PPP
|
||||
}
|
||||
|
||||
esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms)
|
||||
@ -285,7 +138,12 @@ esp_err_t eppp_netif_start(esp_netif_t *netif)
|
||||
{
|
||||
esp_netif_action_start(netif, 0, 0, 0);
|
||||
esp_netif_action_connected(netif, 0, 0, 0);
|
||||
#ifndef CONFIG_EPPP_LINK_USES_PPP
|
||||
// PPP provides address negotiation, if not PPP, we need to check connection manually
|
||||
return eppp_check_connection(netif);
|
||||
#else
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int get_netif_num(esp_netif_t *netif)
|
||||
@ -308,7 +166,6 @@ static int get_netif_num(esp_netif_t *netif)
|
||||
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
esp_netif_t **netif = data;
|
||||
ESP_LOGD(TAG, "PPP status event: %" PRId32, event_id);
|
||||
if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
|
||||
ESP_LOGI(TAG, "Disconnected %d", get_netif_num(*netif));
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(*netif);
|
||||
@ -341,356 +198,7 @@ static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
|
||||
#define SPI_ALIGN(size) (((size) + 3U) & ~(3U))
|
||||
#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6))
|
||||
#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */
|
||||
|
||||
static void IRAM_ATTR timer_callback(void *arg)
|
||||
{
|
||||
struct eppp_handle *h = arg;
|
||||
if (h->blocked == SLAVE_WANTS_WRITE) {
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void *arg)
|
||||
{
|
||||
static uint32_t s_last_time;
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - s_last_time;
|
||||
if (diff < MIN_TRIGGER_US) { // debounce
|
||||
return;
|
||||
}
|
||||
s_last_time = now;
|
||||
struct eppp_handle *h = arg;
|
||||
BaseType_t yield = false;
|
||||
|
||||
// Positive edge means SPI slave prepared the data
|
||||
if (gpio_get_level(h->gpio_intr) == 1) {
|
||||
xSemaphoreGiveFromISR(h->ready_semaphore, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Negative edge (when master blocked) means that slave wants to transmit
|
||||
if (h->blocked == MASTER_BLOCKED) {
|
||||
struct packet buf = { .data = NULL, .len = -1 };
|
||||
xQueueSendFromISR(h->out_queue, &buf, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t deinit_master(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI bus");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_master(struct eppp_config_spi_s *config, esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.max_transfer_sz = TRANSFER_SIZE;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
// TODO: Init and deinit SPI bus separately (per Kconfig?)
|
||||
if (spi_bus_initialize(config->host, &bus_cfg, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
spi_device_interface_config_t dev_cfg = {};
|
||||
dev_cfg.clock_speed_hz = config->freq;
|
||||
dev_cfg.mode = 0;
|
||||
dev_cfg.spics_io_num = config->cs;
|
||||
dev_cfg.cs_ena_pretrans = config->cs_ena_pretrans;
|
||||
dev_cfg.cs_ena_posttrans = config->cs_ena_posttrans;
|
||||
dev_cfg.duty_cycle_pos = 128;
|
||||
dev_cfg.input_delay_ns = config->input_delay_ns;
|
||||
dev_cfg.pre_cb = NULL;
|
||||
dev_cfg.post_cb = NULL;
|
||||
dev_cfg.queue_size = 3;
|
||||
|
||||
if (spi_bus_add_device(config->host, &dev_cfg, &h->spi_device) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//GPIO config for the handshake line.
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_ANYEDGE,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(0);
|
||||
gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE);
|
||||
gpio_isr_handler_add(config->intr, gpio_isr_handler, esp_netif_get_io_driver(netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void post_setup(spi_slave_transaction_t *trans)
|
||||
{
|
||||
struct eppp_handle *h = trans->user;
|
||||
h->slave_last_edge = esp_timer_get_time();
|
||||
gpio_set_level(h->gpio_intr, 1);
|
||||
if (h->transaction_size == 0) { // If no transaction planned:
|
||||
if (h->outbound.len == 0) { // we're blocked if we don't have any data
|
||||
h->blocked = SLAVE_BLOCKED;
|
||||
} else {
|
||||
h->blocked = SLAVE_WANTS_WRITE; // we notify the master that we want to write
|
||||
esp_timer_start_once(h->timer, MIN_TRIGGER_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void post_trans(spi_slave_transaction_t *trans)
|
||||
{
|
||||
struct eppp_handle *h = trans->user;
|
||||
h->blocked = NONE;
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
|
||||
static esp_err_t deinit_slave(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_ERROR(spi_slave_free(h->spi_host), TAG, "Failed to free SPI slave host");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI device");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_slave(struct eppp_config_spi_s *config, esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
//Configuration for the SPI slave interface
|
||||
spi_slave_interface_config_t slvcfg = {
|
||||
.mode = 0,
|
||||
.spics_io_num = config->cs,
|
||||
.queue_size = 3,
|
||||
.flags = 0,
|
||||
.post_setup_cb = post_setup,
|
||||
.post_trans_cb = post_trans,
|
||||
};
|
||||
|
||||
//Configuration for the handshake line
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_pull_mode(config->mosi, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->sclk, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->cs, GPIO_PULLUP_ONLY);
|
||||
|
||||
//Initialize SPI slave interface
|
||||
if (spi_slave_initialize(config->host, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef esp_err_t (*perform_transaction_t)(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer);
|
||||
|
||||
static esp_err_t perform_transaction_master(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_transaction_t t = {};
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_device_transmit(h->spi_device, &t);
|
||||
}
|
||||
|
||||
static esp_err_t perform_transaction_slave(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_slave_transaction_t t = {};
|
||||
t.user = h;
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_slave_transmit(h->spi_host, &t, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
static WORD_ALIGNED_ATTR uint8_t out_buf[TRANSFER_SIZE] = {};
|
||||
static WORD_ALIGNED_ATTR uint8_t in_buf[TRANSFER_SIZE] = {};
|
||||
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
|
||||
// Perform transaction for master and slave
|
||||
const perform_transaction_t perform_transaction = h->role == EPPP_CLIENT ? perform_transaction_master : perform_transaction_slave;
|
||||
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
BaseType_t tx_queue_stat;
|
||||
bool allow_test_tx = false;
|
||||
uint16_t next_tx_size = 0;
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
// SPI MASTER only code
|
||||
if (xSemaphoreTake(h->ready_semaphore, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
// slave might not be ready, but maybe we just missed an interrupt
|
||||
allow_test_tx = true;
|
||||
}
|
||||
if (h->outbound.len == 0 && h->transaction_size == 0 && h->blocked == NONE) {
|
||||
h->blocked = MASTER_BLOCKED;
|
||||
xQueueReceive(h->out_queue, &h->outbound, portMAX_DELAY);
|
||||
h->blocked = NONE;
|
||||
if (h->outbound.len == -1) {
|
||||
h->outbound.len = 0;
|
||||
h->blocked = MASTER_WANTS_READ;
|
||||
}
|
||||
} else if (h->blocked == MASTER_WANTS_READ) {
|
||||
h->blocked = NONE;
|
||||
}
|
||||
}
|
||||
struct header *head = (void *)out_buf;
|
||||
if (h->outbound.len <= h->transaction_size && allow_test_tx == false) {
|
||||
// sending outbound
|
||||
head->size = h->outbound.len;
|
||||
if (h->outbound.len > 0) {
|
||||
memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len);
|
||||
free(h->outbound.data);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, out_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
do {
|
||||
tx_queue_stat = xQueueReceive(h->out_queue, &h->outbound, 0);
|
||||
} while (tx_queue_stat == pdTRUE && h->outbound.len == -1);
|
||||
if (h->outbound.len == -1) { // used as a signal only, no actual data
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
} else {
|
||||
// outbound is bigger, need to transmit in another transaction (keep this empty)
|
||||
head->size = 0;
|
||||
}
|
||||
next_tx_size = head->next_size = h->outbound.len;
|
||||
head->magic = SPI_HEADER_MAGIC;
|
||||
head->check = esp_rom_crc16_le(0, out_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
esp_err_t ret = perform_transaction(h, sizeof(struct header) + h->transaction_size, out_buf, in_buf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "spi_device_transmit failed");
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
return ESP_FAIL;
|
||||
}
|
||||
head = (void *)in_buf;
|
||||
uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
if (check != head->check || head->magic != SPI_HEADER_MAGIC) {
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
if (allow_test_tx) {
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Wrong checksum or magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
|
||||
}
|
||||
h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
#define BUF_SIZE (1024)
|
||||
|
||||
static esp_err_t init_uart(struct eppp_handle *h, eppp_config_t *config)
|
||||
{
|
||||
h->uart_port = config->uart.port;
|
||||
uart_config_t uart_config = {};
|
||||
uart_config.baud_rate = config->uart.baud;
|
||||
uart_config.data_bits = UART_DATA_8_BITS;
|
||||
uart_config.parity = UART_PARITY_DISABLE;
|
||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->uart.rx_buffer_size, 0, config->uart.queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART");
|
||||
ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->uart.tx_io, config->uart.rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
|
||||
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void deinit_uart(struct eppp_handle *h)
|
||||
{
|
||||
uart_driver_delete(h->uart_port);
|
||||
}
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
static uint8_t buffer[BUF_SIZE] = {};
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
uart_event_t event = {};
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (event.type == UART_DATA) {
|
||||
size_t len;
|
||||
uart_get_buffered_data_len(h->uart_port, &len);
|
||||
if (len) {
|
||||
len = uart_read_bytes(h->uart_port, buffer, BUF_SIZE, 0);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, buffer, len, NULL);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Received UART event: %d", event.type);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if (h->role == EPPP_SERVER) {
|
||||
return eppp_sdio_slave_rx(netif);
|
||||
} else {
|
||||
return eppp_sdio_host_rx(netif);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART
|
||||
|
||||
#if EPPP_NEEDS_TASK
|
||||
static void ppp_task(void *args)
|
||||
{
|
||||
esp_netif_t *netif = args;
|
||||
@ -699,6 +207,7 @@ static void ppp_task(void *args)
|
||||
h->exited = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool have_some_eppp_netif(esp_netif_t *netif, void *ctx)
|
||||
{
|
||||
@ -709,7 +218,7 @@ static void remove_handlers(void)
|
||||
{
|
||||
esp_netif_t *netif = esp_netif_find_if(have_some_eppp_netif, NULL);
|
||||
if (netif == NULL) {
|
||||
// if EPPP netif in the system, we cleanup the statics
|
||||
// if EPPP netif in the system, we clean up the statics
|
||||
vEventGroupDelete(s_event_group);
|
||||
s_event_group = NULL;
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event);
|
||||
@ -722,67 +231,33 @@ void eppp_deinit(esp_netif_t *netif)
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
deinit_master(netif);
|
||||
} else {
|
||||
deinit_slave(netif);
|
||||
}
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
deinit_uart(esp_netif_get_io_driver(netif));
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
eppp_sdio_host_deinit();
|
||||
} else {
|
||||
eppp_sdio_slave_deinit();
|
||||
}
|
||||
#endif
|
||||
netif_deinit(netif);
|
||||
EPPP_TRANSPORT_DEINIT(h);
|
||||
eppp_netif_deinit(netif);
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
esp_netif_t *netif = NULL;
|
||||
if (config == NULL || (role != EPPP_SERVER && role != EPPP_CLIENT)) {
|
||||
ESP_LOGE(TAG, "Invalid configuration or role");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_netif_t *netif = netif_init(role, config);
|
||||
if (!netif) {
|
||||
ESP_LOGE(TAG, "Failed to initialize PPP netif");
|
||||
return NULL;
|
||||
}
|
||||
esp_netif_ppp_config_t netif_params;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_get_params(netif, &netif_params));
|
||||
netif_params.ppp_our_ip4_addr = config->ppp.our_ip4_addr;
|
||||
netif_params.ppp_their_ip4_addr = config->ppp.their_ip4_addr;
|
||||
netif_params.ppp_error_event_enabled = true;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_set_params(netif, &netif_params));
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
if (role == EPPP_CLIENT) {
|
||||
init_master(&config->spi, netif);
|
||||
} else {
|
||||
init_slave(&config->spi, netif);
|
||||
|
||||
}
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
init_uart(esp_netif_get_io_driver(netif), config);
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
esp_err_t ret;
|
||||
if (role == EPPP_SERVER) {
|
||||
ret = eppp_sdio_slave_init();
|
||||
} else {
|
||||
ret = eppp_sdio_host_init(&config->sdio);
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
eppp_transport_handle_t h = EPPP_TRANSPORT_INIT(config);
|
||||
ESP_GOTO_ON_FALSE(h, ESP_ERR_NO_MEM, err, TAG, "Failed to init EPPP transport");
|
||||
ESP_GOTO_ON_FALSE(netif = eppp_netif_init(role, h, config), ESP_ERR_NO_MEM, err, TAG, "Failed to init EPPP netif");
|
||||
ESP_GOTO_ON_ERROR(esp_netif_attach(netif, h), err, TAG, "Failed to attach netif to EPPP transport");
|
||||
return netif;
|
||||
err:
|
||||
if (h) {
|
||||
EPPP_TRANSPORT_DEINIT(h);
|
||||
}
|
||||
if (netif) {
|
||||
eppp_netif_deinit(netif);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms)
|
||||
@ -834,12 +309,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time
|
||||
}
|
||||
|
||||
eppp_netif_start(netif);
|
||||
|
||||
#if EPPP_NEEDS_TASK
|
||||
if (xTaskCreate(ppp_task, "ppp connect", config->task.stack_size, netif, config->task.priority, NULL) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to create a ppp connection task");
|
||||
eppp_deinit(netif);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
int netif_cnt = get_netif_num(netif);
|
||||
if (netif_cnt < 0) {
|
||||
eppp_close(netif);
|
||||
|
187
components/eppp_link/eppp_netif_tun.c
Normal file
187
components/eppp_link/eppp_netif_tun.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "lwip/esp_netif_net_stack.h"
|
||||
#include "lwip/prot/ethernet.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "ping/ping_sock.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
static const char *TAG = "eppp_tun_netif";
|
||||
|
||||
static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
struct netif *netif = h;
|
||||
struct pbuf *p = NULL;
|
||||
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_VERBOSE);
|
||||
// need to alloc extra space for the ETH header to support possible packet forwarding
|
||||
ESP_GOTO_ON_FALSE(p = pbuf_alloc(PBUF_RAW, len + SIZEOF_ETH_HDR, PBUF_RAM), ESP_ERR_NO_MEM, err, TAG, "pbuf_alloc failed");
|
||||
ESP_GOTO_ON_FALSE(pbuf_remove_header(p, SIZEOF_ETH_HDR) == 0, ESP_FAIL, err, TAG, "pbuf_remove_header failed");
|
||||
memcpy(p->payload, buffer, len);
|
||||
ESP_GOTO_ON_FALSE(netif->input(p, netif) == ERR_OK, ESP_FAIL, err, TAG, "failed to input packet to lwip");
|
||||
return;
|
||||
err:
|
||||
if (p) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static err_t tun_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
|
||||
LWIP_ASSERT("p != NULL", (p != NULL));
|
||||
esp_err_t ret = esp_netif_transmit(netif->state, p->payload, p->len);
|
||||
switch (ret) {
|
||||
case ESP_OK:
|
||||
return ERR_OK;
|
||||
case ESP_ERR_NO_MEM:
|
||||
return ERR_MEM;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
case ESP_ERR_ESP_NETIF_TX_FAILED:
|
||||
return ERR_BUF;
|
||||
#endif
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
return ERR_ARG;
|
||||
default:
|
||||
return ERR_IF;
|
||||
}
|
||||
}
|
||||
|
||||
static err_t tun_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
|
||||
{
|
||||
LWIP_UNUSED_ARG(ipaddr);
|
||||
return tun_output(netif, p);
|
||||
}
|
||||
static err_t tun_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
|
||||
{
|
||||
LWIP_UNUSED_ARG(ipaddr);
|
||||
return tun_output(netif, p);
|
||||
}
|
||||
|
||||
static err_t tun_init(struct netif *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return ERR_IF;
|
||||
}
|
||||
netif->name[0] = 't';
|
||||
netif->name[1] = 'u';
|
||||
#if LWIP_IPV4
|
||||
netif->output = tun_output_v4;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = tun_output_v6;
|
||||
#endif
|
||||
netif->mtu = 1500;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static const struct esp_netif_netstack_config s_config_tun = {
|
||||
.lwip = {
|
||||
.init_fn = tun_init,
|
||||
.input_fn = tun_input,
|
||||
}
|
||||
};
|
||||
|
||||
const esp_netif_netstack_config_t *g_eppp_netif_config_tun = &s_config_tun;
|
||||
|
||||
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
uint8_t ttl;
|
||||
uint16_t seqno;
|
||||
uint32_t elapsed_time, recv_len;
|
||||
ip_addr_t target_addr;
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
|
||||
ESP_LOGD(TAG, "%" PRIu32 " bytes from %s icmp_seq=%" PRIu16 " ttl=%" PRIu16 " time=%" PRIu32 " ms\n",
|
||||
recv_len, ipaddr_ntoa((ip_addr_t *)&target_addr), seqno, ttl, elapsed_time);
|
||||
if (esp_ping_stop(hdl) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to stop ping session");
|
||||
// continue to delete the session
|
||||
}
|
||||
esp_ping_delete_session(hdl);
|
||||
ESP_LOGI(TAG, "PING success -> stop and post IP");
|
||||
esp_netif_t *netif = (esp_netif_t *)args;
|
||||
esp_netif_ip_info_t ip = {0};
|
||||
esp_netif_get_ip_info(netif, &ip);
|
||||
esp_netif_set_ip_info(netif, &ip);
|
||||
}
|
||||
|
||||
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
uint16_t seqno;
|
||||
ip_addr_t target_addr;
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
|
||||
ESP_LOGD(TAG, "From %s icmp_seq=%" PRIu16 "timeout", ipaddr_ntoa((ip_addr_t *)&target_addr), seqno);
|
||||
}
|
||||
|
||||
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
ip_addr_t target_addr;
|
||||
uint32_t transmitted;
|
||||
uint32_t received;
|
||||
uint32_t total_time_ms;
|
||||
uint32_t loss;
|
||||
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
|
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
|
||||
|
||||
if (transmitted > 0) {
|
||||
loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
|
||||
} else {
|
||||
loss = 0;
|
||||
}
|
||||
if (IP_IS_V4(&target_addr)) {
|
||||
ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
|
||||
}
|
||||
ESP_LOGI(TAG, "%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
|
||||
transmitted, received, loss, total_time_ms);
|
||||
esp_ping_delete_session(hdl);
|
||||
}
|
||||
|
||||
esp_err_t eppp_check_connection(esp_netif_t *netif)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
|
||||
config.count = 100;
|
||||
ESP_LOGI(TAG, "Checking connection on EPPP interface #%" PRIu32, config.interface);
|
||||
ip_addr_t target_addr = {0};
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_netif_get_ip_info(netif, &ip);
|
||||
target_addr.u_addr.ip4.addr = ip.gw.addr;
|
||||
config.target_addr = target_addr;
|
||||
esp_ping_callbacks_t cbs = {
|
||||
.cb_args = netif,
|
||||
.on_ping_success = cmd_ping_on_ping_success,
|
||||
.on_ping_timeout = cmd_ping_on_ping_timeout,
|
||||
.on_ping_end = cmd_ping_on_ping_end
|
||||
};
|
||||
esp_ping_handle_t ping;
|
||||
ESP_GOTO_ON_ERROR(esp_ping_new_session(&config, &cbs, &ping), err, TAG, "Failed to create ping session");
|
||||
ESP_GOTO_ON_ERROR(esp_ping_start(ping), err, TAG, "Failed to start ping session");
|
||||
ESP_LOGI(TAG, "Ping started");
|
||||
return ret;
|
||||
err:
|
||||
ESP_LOGE(TAG, "Failed to start connection check");
|
||||
return ret;
|
||||
}
|
88
components/eppp_link/eppp_sdio.c
Normal file
88
components/eppp_link/eppp_sdio.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_mac.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
#include "eppp_transport_sdio.h"
|
||||
|
||||
#define TAG "eppp_sdio"
|
||||
|
||||
struct eppp_sdio {
|
||||
struct eppp_handle parent;
|
||||
bool is_host;
|
||||
};
|
||||
|
||||
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len);
|
||||
esp_err_t eppp_sdio_host_rx(esp_netif_t *netif);
|
||||
esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif);
|
||||
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len);
|
||||
esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config);
|
||||
esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *config);
|
||||
void eppp_sdio_slave_deinit(void);
|
||||
void eppp_sdio_host_deinit(void);
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
struct eppp_sdio *handle = __containerof(h, struct eppp_sdio, parent);;
|
||||
if (handle->is_host) {
|
||||
return eppp_sdio_host_rx(netif);
|
||||
} else {
|
||||
return eppp_sdio_slave_rx(netif);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
eppp_transport_handle_t h = (eppp_transport_handle_t)args;
|
||||
ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null");
|
||||
struct eppp_sdio *sdio = __containerof(h, struct eppp_sdio, parent);
|
||||
h->base.netif = esp_netif;
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = h,
|
||||
.transmit = sdio->is_host ? eppp_sdio_host_tx : eppp_sdio_slave_tx,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config");
|
||||
ESP_LOGI(TAG, "EPPP SDIO transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
|
||||
struct eppp_sdio *h = calloc(1, sizeof(struct eppp_sdio));
|
||||
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
|
||||
h->parent.base.post_attach = post_attach;
|
||||
h->is_host = config->is_host;
|
||||
esp_err_t (*init_fn)(struct eppp_config_sdio_s * eppp_config) = h->is_host ? eppp_sdio_host_init : eppp_sdio_slave_init;
|
||||
ESP_GOTO_ON_ERROR(init_fn(config), err, TAG, "Failed to init SDIO");
|
||||
return &h->parent;
|
||||
err:
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void eppp_sdio_deinit(eppp_transport_handle_t h)
|
||||
{
|
||||
struct eppp_sdio *sdio = __containerof(h, struct eppp_sdio, parent);
|
||||
if (sdio->is_host) {
|
||||
eppp_sdio_host_deinit();
|
||||
} else {
|
||||
eppp_sdio_slave_deinit();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -9,6 +9,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_sdio.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
@ -109,7 +110,7 @@ static void event_cb(uint8_t pos)
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_slave_init(void)
|
||||
esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *eppp_config)
|
||||
{
|
||||
sdio_slave_config_t config = {
|
||||
.sending_mode = SDIO_SLAVE_SEND_PACKET,
|
||||
@ -166,7 +167,7 @@ void eppp_sdio_slave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_slave_init(void)
|
||||
esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *config)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
470
components/eppp_link/eppp_spi.c
Normal file
470
components/eppp_link/eppp_spi.c
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_mac.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
#include "eppp_transport_spi.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_rom_crc.h"
|
||||
|
||||
#define TAG "eppp_spi"
|
||||
|
||||
#define MAX_PAYLOAD 1500
|
||||
#define MIN_TRIGGER_US 20
|
||||
#define SPI_HEADER_MAGIC 0x1234
|
||||
#define SPI_ALIGN(size) (((size) + 3U) & ~(3U))
|
||||
#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6))
|
||||
#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */
|
||||
|
||||
struct packet {
|
||||
size_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct header {
|
||||
uint16_t magic;
|
||||
uint16_t size;
|
||||
uint16_t next_size;
|
||||
uint16_t check;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum blocked_status {
|
||||
NONE,
|
||||
MASTER_BLOCKED,
|
||||
MASTER_WANTS_READ,
|
||||
SLAVE_BLOCKED,
|
||||
SLAVE_WANTS_WRITE,
|
||||
};
|
||||
|
||||
struct eppp_spi {
|
||||
struct eppp_handle parent;
|
||||
bool is_master;
|
||||
QueueHandle_t out_queue;
|
||||
QueueHandle_t ready_semaphore;
|
||||
spi_device_handle_t spi_device;
|
||||
spi_host_device_t spi_host;
|
||||
int gpio_intr;
|
||||
uint16_t next_size;
|
||||
uint16_t transaction_size;
|
||||
struct packet outbound;
|
||||
enum blocked_status blocked;
|
||||
uint32_t slave_last_edge;
|
||||
esp_timer_handle_t timer;
|
||||
};
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *common = h;
|
||||
struct eppp_spi *handle = __containerof(common, struct eppp_spi, parent);;
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct packet buf = { };
|
||||
uint8_t *current_buffer = buffer;
|
||||
size_t remaining = len;
|
||||
do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform
|
||||
// fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD)
|
||||
size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining;
|
||||
buf.data = malloc(batch);
|
||||
if (buf.data == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate packet");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
buf.len = batch;
|
||||
remaining -= batch;
|
||||
memcpy(buf.data, current_buffer, batch);
|
||||
current_buffer += batch;
|
||||
BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0);
|
||||
if (ret != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to queue packet to slave!");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
} while (remaining > 0);
|
||||
|
||||
if (!handle->is_master && handle->blocked == SLAVE_BLOCKED) {
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - handle->slave_last_edge;
|
||||
if (diff < MIN_TRIGGER_US) {
|
||||
esp_rom_delay_us(MIN_TRIGGER_US - diff);
|
||||
}
|
||||
gpio_set_level(handle->gpio_intr, 0);
|
||||
}
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_WARN);
|
||||
uart_write_bytes(handle->uart_port, buffer, len);
|
||||
#endif // DEVICE UART or SPI
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR timer_callback(void *arg)
|
||||
{
|
||||
struct eppp_spi *h = arg;
|
||||
if (h->blocked == SLAVE_WANTS_WRITE) {
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void *arg)
|
||||
{
|
||||
static uint32_t s_last_time;
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - s_last_time;
|
||||
if (diff < MIN_TRIGGER_US) { // debounce
|
||||
return;
|
||||
}
|
||||
s_last_time = now;
|
||||
struct eppp_spi *h = arg;
|
||||
BaseType_t yield = false;
|
||||
|
||||
// Positive edge means SPI slave prepared the data
|
||||
if (gpio_get_level(h->gpio_intr) == 1) {
|
||||
xSemaphoreGiveFromISR(h->ready_semaphore, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Negative edge (when master blocked) means that slave wants to transmit
|
||||
if (h->blocked == MASTER_BLOCKED) {
|
||||
struct packet buf = { .data = NULL, .len = -1 };
|
||||
xQueueSendFromISR(h->out_queue, &buf, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t deinit_master(struct eppp_spi *h)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI bus");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_master(struct eppp_config_spi_s *config, struct eppp_spi *h)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.max_transfer_sz = TRANSFER_SIZE;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
// TODO(IDF-13351): Init and deinit SPI bus separately (per Kconfig?)
|
||||
ESP_RETURN_ON_ERROR(spi_bus_initialize(config->host, &bus_cfg, SPI_DMA_CH_AUTO), TAG, "Failed to init SPI bus");
|
||||
|
||||
spi_device_interface_config_t dev_cfg = {};
|
||||
dev_cfg.clock_speed_hz = config->freq;
|
||||
dev_cfg.mode = 0;
|
||||
dev_cfg.spics_io_num = config->cs;
|
||||
dev_cfg.cs_ena_pretrans = config->cs_ena_pretrans;
|
||||
dev_cfg.cs_ena_posttrans = config->cs_ena_posttrans;
|
||||
dev_cfg.duty_cycle_pos = 128;
|
||||
dev_cfg.input_delay_ns = config->input_delay_ns;
|
||||
dev_cfg.pre_cb = NULL;
|
||||
dev_cfg.post_cb = NULL;
|
||||
dev_cfg.queue_size = 3;
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_bus_add_device(config->host, &dev_cfg, &h->spi_device), err, TAG, "Failed to add SPI device");
|
||||
|
||||
//GPIO config for the handshake line.
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_ANYEDGE,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err_dev, TAG, "Failed to config interrupt GPIO");
|
||||
ESP_GOTO_ON_ERROR(gpio_install_isr_service(0), err_dev, TAG, "Failed to install GPIO ISR");
|
||||
ESP_GOTO_ON_ERROR(gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE), err_dev, TAG, "Failed to set ISR type");
|
||||
ESP_GOTO_ON_ERROR(gpio_isr_handler_add(config->intr, gpio_isr_handler, h), err_dev, TAG, "Failed to add ISR handler");
|
||||
return ESP_OK;
|
||||
err_dev:
|
||||
spi_bus_remove_device(h->spi_device);
|
||||
err:
|
||||
spi_bus_free(config->host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void post_setup(spi_slave_transaction_t *trans)
|
||||
{
|
||||
struct eppp_spi *h = trans->user;
|
||||
h->slave_last_edge = esp_timer_get_time();
|
||||
gpio_set_level(h->gpio_intr, 1);
|
||||
if (h->transaction_size == 0) { // If no transaction planned:
|
||||
if (h->outbound.len == 0) { // we're blocked if we don't have any data
|
||||
h->blocked = SLAVE_BLOCKED;
|
||||
} else {
|
||||
h->blocked = SLAVE_WANTS_WRITE; // we notify the master that we want to write
|
||||
esp_timer_start_once(h->timer, MIN_TRIGGER_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void post_transaction(spi_slave_transaction_t *transaction)
|
||||
{
|
||||
struct eppp_spi *h = transaction->user;
|
||||
h->blocked = NONE;
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
|
||||
static esp_err_t deinit_slave(struct eppp_spi *h)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(spi_slave_free(h->spi_host), TAG, "Failed to free SPI slave host");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI device");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_slave(struct eppp_config_spi_s *config, struct eppp_spi *h)
|
||||
{
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
//Configuration for the SPI slave interface
|
||||
spi_slave_interface_config_t slvcfg = {
|
||||
.mode = 0,
|
||||
.spics_io_num = config->cs,
|
||||
.queue_size = 3,
|
||||
.flags = 0,
|
||||
.post_setup_cb = post_setup,
|
||||
.post_trans_cb = post_transaction,
|
||||
};
|
||||
|
||||
//Configuration for the handshake line
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_pull_mode(config->mosi, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->sclk, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->cs, GPIO_PULLUP_ONLY);
|
||||
|
||||
//Initialize SPI slave interface
|
||||
if (spi_slave_initialize(config->host, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef esp_err_t (*perform_transaction_t)(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer);
|
||||
|
||||
static esp_err_t perform_transaction_master(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_transaction_t t = {};
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_device_transmit(h->spi_device, &t);
|
||||
}
|
||||
|
||||
static esp_err_t perform_transaction_slave(struct eppp_spi *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_slave_transaction_t t = {};
|
||||
t.user = h;
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_slave_transmit(h->spi_host, &t, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
static WORD_ALIGNED_ATTR uint8_t out_buf[TRANSFER_SIZE] = {};
|
||||
static WORD_ALIGNED_ATTR uint8_t in_buf[TRANSFER_SIZE] = {};
|
||||
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_spi *h = __containerof(handle, struct eppp_spi, parent);
|
||||
|
||||
// Perform transaction for master and slave
|
||||
const perform_transaction_t perform_transaction = h->is_master ? perform_transaction_master : perform_transaction_slave;
|
||||
|
||||
if (h->parent.stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
BaseType_t tx_queue_stat;
|
||||
bool allow_test_tx = false;
|
||||
uint16_t next_tx_size = 0;
|
||||
if (h->is_master) {
|
||||
// SPI MASTER only code
|
||||
if (xSemaphoreTake(h->ready_semaphore, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
// slave might not be ready, but maybe we just missed an interrupt
|
||||
allow_test_tx = true;
|
||||
}
|
||||
if (h->outbound.len == 0 && h->transaction_size == 0 && h->blocked == NONE) {
|
||||
h->blocked = MASTER_BLOCKED;
|
||||
xQueueReceive(h->out_queue, &h->outbound, portMAX_DELAY);
|
||||
h->blocked = NONE;
|
||||
if (h->outbound.len == -1) {
|
||||
h->outbound.len = 0;
|
||||
h->blocked = MASTER_WANTS_READ;
|
||||
}
|
||||
} else if (h->blocked == MASTER_WANTS_READ) {
|
||||
h->blocked = NONE;
|
||||
}
|
||||
}
|
||||
struct header *head = (void *)out_buf;
|
||||
if (h->outbound.len <= h->transaction_size && allow_test_tx == false) {
|
||||
// sending outbound
|
||||
head->size = h->outbound.len;
|
||||
if (h->outbound.len > 0) {
|
||||
memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len);
|
||||
free(h->outbound.data);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, out_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
do {
|
||||
tx_queue_stat = xQueueReceive(h->out_queue, &h->outbound, 0);
|
||||
} while (tx_queue_stat == pdTRUE && h->outbound.len == -1);
|
||||
if (h->outbound.len == -1) { // used as a signal only, no actual data
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
} else {
|
||||
// outbound is bigger, need to transmit in another transaction (keep this empty)
|
||||
head->size = 0;
|
||||
}
|
||||
next_tx_size = head->next_size = h->outbound.len;
|
||||
head->magic = SPI_HEADER_MAGIC;
|
||||
head->check = esp_rom_crc16_le(0, out_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
esp_err_t ret = perform_transaction(h, sizeof(struct header) + h->transaction_size, out_buf, in_buf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "spi_device_transmit failed");
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
return ESP_FAIL;
|
||||
}
|
||||
head = (void *)in_buf;
|
||||
uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
if (check != head->check || head->magic != SPI_HEADER_MAGIC) {
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
if (allow_test_tx) {
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Wrong checksum or magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
|
||||
}
|
||||
h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t init_driver(struct eppp_spi *h, struct eppp_config_spi_s *config)
|
||||
{
|
||||
if (config->is_master) {
|
||||
return init_master(config, h);
|
||||
}
|
||||
return init_slave(config, h);
|
||||
}
|
||||
|
||||
static esp_err_t post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
eppp_transport_handle_t h = (eppp_transport_handle_t)args;
|
||||
ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null");
|
||||
h->base.netif = esp_netif;
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = h,
|
||||
.transmit = transmit,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config");
|
||||
ESP_LOGI(TAG, "EPPP SPI transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
|
||||
struct eppp_spi *h = calloc(1, sizeof(struct eppp_spi));
|
||||
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
|
||||
h->is_master = config->is_master;
|
||||
h->parent.base.post_attach = post_attach;
|
||||
h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet));
|
||||
ESP_GOTO_ON_FALSE(h->out_queue, ESP_FAIL, err, TAG, "Failed to create the packet queue");
|
||||
if (h->is_master) {
|
||||
ESP_GOTO_ON_FALSE(h->ready_semaphore = xSemaphoreCreateBinary(), ESP_FAIL, err, TAG, "Failed to create the semaphore");
|
||||
}
|
||||
h->transaction_size = 0;
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
if (!h->is_master) {
|
||||
esp_timer_create_args_t args = {
|
||||
.callback = &timer_callback,
|
||||
.arg = h,
|
||||
.name = "spi_slave_tmr"
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(esp_timer_create(&args, &h->timer), err, TAG, "Failed to create timer");
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(init_driver(h, config), err, TAG, "Failed to init SPI driver");
|
||||
return &h->parent;
|
||||
err:
|
||||
if (h->out_queue) {
|
||||
vQueueDelete(h->out_queue);
|
||||
}
|
||||
if (h->ready_semaphore) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
if (h->out_queue) {
|
||||
vQueueDelete(h->out_queue);
|
||||
}
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
void eppp_spi_deinit(eppp_transport_handle_t handle)
|
||||
{
|
||||
struct eppp_spi *h = __containerof(handle, struct eppp_spi, parent);;
|
||||
if (h->is_master) {
|
||||
deinit_master(h);
|
||||
} else {
|
||||
deinit_slave(h);
|
||||
}
|
||||
struct packet buf = { };
|
||||
while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) {
|
||||
if (buf.len > 0) {
|
||||
free(buf.data);
|
||||
}
|
||||
}
|
||||
vQueueDelete(h->out_queue);
|
||||
if (h->is_master) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
free(h);
|
||||
}
|
17
components/eppp_link/eppp_transport.h
Normal file
17
components/eppp_link/eppp_transport.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_netif_types.h"
|
||||
|
||||
struct eppp_handle {
|
||||
esp_netif_driver_base_t base;
|
||||
eppp_type_t role;
|
||||
bool stop;
|
||||
bool exited;
|
||||
bool netif_stop;
|
||||
};
|
||||
|
||||
esp_err_t eppp_check_connection(esp_netif_t *netif);
|
252
components/eppp_link/eppp_uart.c
Normal file
252
components/eppp_link/eppp_uart.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_event.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#define TAG "eppp_uart"
|
||||
|
||||
struct eppp_uart {
|
||||
struct eppp_handle parent;
|
||||
QueueHandle_t uart_event_queue;
|
||||
uart_port_t uart_port;
|
||||
};
|
||||
|
||||
#define MAX_PAYLOAD (1500)
|
||||
#define HEADER_MAGIC (0x7E)
|
||||
#define HEADER_SIZE (4)
|
||||
#define MAX_PACKET_SIZE (MAX_PAYLOAD + HEADER_SIZE)
|
||||
/* Maximum size of a packet sent over UART, including header and payload */
|
||||
#define UART_BUF_SIZE (MAX_PACKET_SIZE)
|
||||
|
||||
struct header {
|
||||
uint8_t magic;
|
||||
uint8_t check;
|
||||
uint16_t size;
|
||||
} __attribute__((packed));
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *common = h;
|
||||
struct eppp_uart *handle = __containerof(common, struct eppp_uart, parent);
|
||||
#ifndef CONFIG_EPPP_LINK_USES_PPP
|
||||
static uint8_t out_buf[MAX_PACKET_SIZE] = {};
|
||||
struct header *head = (void *)out_buf;
|
||||
head->magic = HEADER_MAGIC;
|
||||
head->check = 0;
|
||||
head->size = len;
|
||||
head->check = (0xFF & len) ^ (len >> 8);
|
||||
memcpy(out_buf + sizeof(struct header), buffer, len);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", out_buf, len + sizeof(struct header), ESP_LOG_DEBUG);
|
||||
uart_write_bytes(handle->uart_port, out_buf, len + sizeof(struct header));
|
||||
#else
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_DEBUG);
|
||||
uart_write_bytes(handle->uart_port, buffer, len);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_uart(struct eppp_uart *h, struct eppp_config_uart_s *config)
|
||||
{
|
||||
h->uart_port = config->port;
|
||||
uart_config_t uart_config = {};
|
||||
uart_config.baud_rate = config->baud;
|
||||
uart_config.data_bits = UART_DATA_8_BITS;
|
||||
uart_config.parity = UART_PARITY_DISABLE;
|
||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->rx_buffer_size, 0, config->queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART");
|
||||
ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->tx_io, config->rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
|
||||
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void deinit_uart(struct eppp_uart *h)
|
||||
{
|
||||
uart_driver_delete(h->uart_port);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_EPPP_LINK_USES_PPP
|
||||
/**
|
||||
* @brief Process incoming UART data and extract packets
|
||||
*/
|
||||
static void process_packet(esp_netif_t *netif, uart_port_t uart_port, size_t available_data)
|
||||
{
|
||||
static uint8_t in_buf[2 * UART_BUF_SIZE] = {};
|
||||
static size_t buf_start = 0;
|
||||
static size_t buf_end = 0;
|
||||
struct header *head;
|
||||
|
||||
// Read data directly into our buffer
|
||||
size_t available_space = sizeof(in_buf) - buf_end;
|
||||
size_t read_size = (available_data < available_space) ? available_data : available_space;
|
||||
if (read_size > 0) {
|
||||
size_t len = uart_read_bytes(uart_port, in_buf + buf_end, read_size, 0);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", in_buf + buf_end, len, ESP_LOG_DEBUG);
|
||||
|
||||
if (buf_end + len <= sizeof(in_buf)) {
|
||||
buf_end += len;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Buffer overflow, discarding data");
|
||||
buf_start = buf_end = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Process while we have enough data for at least a header
|
||||
while ((buf_end - buf_start) >= sizeof(struct header)) {
|
||||
head = (void *)(in_buf + buf_start);
|
||||
|
||||
if (head->magic != HEADER_MAGIC) {
|
||||
goto recover;
|
||||
}
|
||||
|
||||
uint8_t calculated_check = (head->size & 0xFF) ^ (head->size >> 8);
|
||||
if (head->check != calculated_check) {
|
||||
ESP_LOGW(TAG, "Checksum mismatch: expected 0x%04x, got 0x%04x", calculated_check, head->check);
|
||||
goto recover;
|
||||
}
|
||||
|
||||
// Check if we have the complete packet
|
||||
uint16_t payload_size = head->size;
|
||||
size_t total_packet_size = sizeof(struct header) + payload_size;
|
||||
|
||||
if (payload_size > MAX_PAYLOAD) {
|
||||
ESP_LOGW(TAG, "Invalid payload size: %d", payload_size);
|
||||
goto recover;
|
||||
}
|
||||
|
||||
// If we don't have the complete packet yet, wait for more data
|
||||
if ((buf_end - buf_start) < total_packet_size) {
|
||||
ESP_LOGD(TAG, "Incomplete packet: got %d bytes, need %d bytes", (buf_end - buf_start), total_packet_size);
|
||||
break;
|
||||
}
|
||||
|
||||
// Got a complete packet, pass it to network
|
||||
esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL);
|
||||
|
||||
// Advance start pointer past this packet
|
||||
buf_start += total_packet_size;
|
||||
|
||||
// compact if we don't have enough space for 1x UART_BUF_SIZE
|
||||
if (buf_start > (sizeof(in_buf) / 2) || (sizeof(in_buf) - buf_end) < UART_BUF_SIZE) {
|
||||
if (buf_start < buf_end) {
|
||||
size_t remaining_data = buf_end - buf_start;
|
||||
memmove(in_buf, in_buf + buf_start, remaining_data);
|
||||
buf_end = remaining_data;
|
||||
} else {
|
||||
buf_end = 0;
|
||||
}
|
||||
buf_start = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
recover:
|
||||
// Search for next HEADER_MAGIC occurrence
|
||||
uint8_t *next_magic = memchr(in_buf + buf_start + 1, HEADER_MAGIC, buf_end - buf_start - 1);
|
||||
if (next_magic) {
|
||||
// Found next potential header, advance start to that position
|
||||
buf_start = next_magic - in_buf;
|
||||
|
||||
// Check if we need to compact after recovery too
|
||||
if (buf_start > (sizeof(in_buf) / 2) || (sizeof(in_buf) - buf_end) < UART_BUF_SIZE) {
|
||||
if (buf_start < buf_end) {
|
||||
size_t remaining_data = buf_end - buf_start;
|
||||
memmove(in_buf, in_buf + buf_start, remaining_data);
|
||||
buf_end = remaining_data;
|
||||
} else {
|
||||
buf_end = 0;
|
||||
}
|
||||
buf_start = 0;
|
||||
}
|
||||
} else {
|
||||
// No more HEADER_MAGIC found, discard all data
|
||||
buf_start = buf_end = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent);
|
||||
|
||||
uart_event_t event = {};
|
||||
if (h->parent.stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (event.type == UART_DATA) {
|
||||
size_t len;
|
||||
uart_get_buffered_data_len(h->uart_port, &len);
|
||||
if (len) {
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
static uint8_t buffer[UART_BUF_SIZE] = {};
|
||||
len = uart_read_bytes(h->uart_port, buffer, UART_BUF_SIZE, 0);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_DEBUG);
|
||||
esp_netif_receive(netif, buffer, len, NULL);
|
||||
#else
|
||||
// Read directly in process_packet to save one buffer
|
||||
process_packet(netif, h->uart_port, len);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Received UART event: %d", event.type);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t post_attach(esp_netif_t *esp_netif, void *args)
|
||||
{
|
||||
eppp_transport_handle_t h = (eppp_transport_handle_t)args;
|
||||
ESP_RETURN_ON_FALSE(h, ESP_ERR_INVALID_ARG, TAG, "Transport handle cannot be null");
|
||||
h->base.netif = esp_netif;
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_ifconfig = {
|
||||
.handle = h,
|
||||
.transmit = transmit,
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_netif_set_driver_config(esp_netif, &driver_ifconfig), TAG, "Failed to set driver config");
|
||||
ESP_LOGI(TAG, "EPPP UART transport attached to EPPP netif %s", esp_netif_get_desc(esp_netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(config, NULL, TAG, "Config cannot be null");
|
||||
struct eppp_uart *h = calloc(1, sizeof(struct eppp_uart));
|
||||
ESP_RETURN_ON_FALSE(h, NULL, TAG, "Failed to allocate eppp_handle");
|
||||
h->parent.base.post_attach = post_attach;
|
||||
ESP_GOTO_ON_ERROR(init_uart(h, config), err, TAG, "Failed to init UART");
|
||||
return &h->parent;
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void eppp_uart_deinit(eppp_transport_handle_t handle)
|
||||
{
|
||||
struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent);
|
||||
deinit_uart(h);
|
||||
free(h);
|
||||
}
|
@ -26,10 +26,66 @@ menu "Example Configuration"
|
||||
help
|
||||
Init and run iperf console.
|
||||
|
||||
config EXAMPLE_SPI_HOST
|
||||
int "SPI Host"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 1
|
||||
range 0 2
|
||||
help
|
||||
SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2).
|
||||
|
||||
config EXAMPLE_SPI_MOSI_PIN
|
||||
int "MOSI Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 23
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI MOSI.
|
||||
|
||||
config EXAMPLE_SPI_MISO_PIN
|
||||
int "MISO Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 19
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI MISO.
|
||||
|
||||
config EXAMPLE_SPI_SCLK_PIN
|
||||
int "SCLK Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 18
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI SCLK.
|
||||
|
||||
config EXAMPLE_SPI_CS_PIN
|
||||
int "CS Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 5
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI CS.
|
||||
|
||||
config EXAMPLE_SPI_INTR_PIN
|
||||
int "Interrupt Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 17
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI interrupt.
|
||||
|
||||
config EXAMPLE_SPI_FREQUENCY
|
||||
int "SPI Frequency (Hz)"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 4000000
|
||||
range 100000 80000000
|
||||
help
|
||||
SPI frequency in Hz.
|
||||
|
||||
config EXAMPLE_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 10
|
||||
default 17
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
@ -37,7 +93,7 @@ menu "Example Configuration"
|
||||
config EXAMPLE_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 11
|
||||
default 18
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
@ -45,7 +101,7 @@ menu "Example Configuration"
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 2000000
|
||||
default 921600
|
||||
range 0 4000000
|
||||
help
|
||||
Baudrate used by the PPP over UART
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -104,13 +104,24 @@ void app_main(void)
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
config.transport = EPPP_TRANSPORT_SPI;
|
||||
config.spi.is_master = true;
|
||||
config.spi.host = CONFIG_EXAMPLE_SPI_HOST;
|
||||
config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN;
|
||||
config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN;
|
||||
config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN;
|
||||
config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN;
|
||||
config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN;
|
||||
config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN;
|
||||
config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN;
|
||||
config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_ETH
|
||||
config.transport = EPPP_TRANSPORT_ETHERNET;
|
||||
#else
|
||||
config.transport = EPPP_TRANSPORT_SDIO;
|
||||
config.sdio.is_host = true;
|
||||
#endif
|
||||
esp_netif_t *eppp_netif = eppp_connect(&config);
|
||||
if (eppp_netif == NULL) {
|
||||
|
2
components/eppp_link/examples/host/sdkconfig.ci.eth
Normal file
2
components/eppp_link/examples/host/sdkconfig.ci.eth
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_EPPP_LINK_DEVICE_ETH=y
|
2
components/eppp_link/examples/host/sdkconfig.ci.sdio
Normal file
2
components/eppp_link/examples/host/sdkconfig.ci.sdio
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
CONFIG_EPPP_LINK_DEVICE_SDIO=y
|
@ -18,10 +18,66 @@ menu "Example Configuration"
|
||||
help
|
||||
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
|
||||
|
||||
config EXAMPLE_SPI_HOST
|
||||
int "SPI Host"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 1
|
||||
range 0 2
|
||||
help
|
||||
SPI host to use (SPI1_HOST=0, SPI2_HOST=1, SPI3_HOST=2).
|
||||
|
||||
config EXAMPLE_SPI_MOSI_PIN
|
||||
int "MOSI Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 23
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI MOSI.
|
||||
|
||||
config EXAMPLE_SPI_MISO_PIN
|
||||
int "MISO Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 19
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI MISO.
|
||||
|
||||
config EXAMPLE_SPI_SCLK_PIN
|
||||
int "SCLK Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 18
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI SCLK.
|
||||
|
||||
config EXAMPLE_SPI_CS_PIN
|
||||
int "CS Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 5
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI CS.
|
||||
|
||||
config EXAMPLE_SPI_INTR_PIN
|
||||
int "Interrupt Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 17
|
||||
range 0 39
|
||||
help
|
||||
Pin number of SPI interrupt.
|
||||
|
||||
config EXAMPLE_SPI_FREQUENCY
|
||||
int "SPI Frequency (Hz)"
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
default 1000000
|
||||
range 100000 80000000
|
||||
help
|
||||
SPI frequency in Hz.
|
||||
|
||||
config EXAMPLE_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 11
|
||||
default 18
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
@ -29,7 +85,7 @@ menu "Example Configuration"
|
||||
config EXAMPLE_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 10
|
||||
default 17
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
@ -37,7 +93,7 @@ menu "Example Configuration"
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 2000000
|
||||
default 921600
|
||||
range 0 4000000
|
||||
help
|
||||
Baudrate used by the PPP over UART
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -55,9 +55,6 @@ void init_network_interface(void)
|
||||
{
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
@ -66,15 +63,15 @@ void init_network_interface(void)
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_any_id));
|
||||
ESP_EVENT_ANY_ID,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
@ -82,9 +79,9 @@ void init_network_interface(void)
|
||||
.password = CONFIG_ESP_WIFI_PASSWORD,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
@ -127,13 +124,21 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
init_network_interface(); // WiFi station if withing SoC capabilities (otherwise a placeholder)
|
||||
// ESP_ERROR_CHECK(esp_netif_init());
|
||||
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
config.transport = EPPP_TRANSPORT_SPI;
|
||||
config.spi.is_master = false;
|
||||
config.spi.host = CONFIG_EXAMPLE_SPI_HOST;
|
||||
config.spi.mosi = CONFIG_EXAMPLE_SPI_MOSI_PIN;
|
||||
config.spi.miso = CONFIG_EXAMPLE_SPI_MISO_PIN;
|
||||
config.spi.sclk = CONFIG_EXAMPLE_SPI_SCLK_PIN;
|
||||
config.spi.cs = CONFIG_EXAMPLE_SPI_CS_PIN;
|
||||
config.spi.intr = CONFIG_EXAMPLE_SPI_INTR_PIN;
|
||||
config.spi.freq = CONFIG_EXAMPLE_SPI_FREQUENCY;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = CONFIG_EXAMPLE_UART_TX_PIN;
|
||||
|
2
components/eppp_link/examples/slave/sdkconfig.ci.eth
Normal file
2
components/eppp_link/examples/slave/sdkconfig.ci.eth
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_EPPP_LINK_DEVICE_ETH=y
|
2
components/eppp_link/examples/slave/sdkconfig.ci.sdio
Normal file
2
components/eppp_link/examples/slave/sdkconfig.ci.sdio
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c6"
|
||||
CONFIG_EPPP_LINK_DEVICE_SDIO=y
|
@ -1,4 +1,4 @@
|
||||
version: 0.2.0
|
||||
version: 0.3.0
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
|
||||
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
|
||||
dependencies:
|
||||
|
@ -1,14 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1)
|
||||
#define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2)
|
||||
|
||||
#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \
|
||||
.transport = EPPP_TRANSPORT_UART, \
|
||||
#define EPPP_DEFAULT_SPI_CONFIG() \
|
||||
.spi = { \
|
||||
.host = 1, \
|
||||
.mosi = 11, \
|
||||
@ -21,6 +21,8 @@
|
||||
.cs_ena_pretrans = 0, \
|
||||
.cs_ena_posttrans = 0, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_UART_CONFIG() \
|
||||
.uart = { \
|
||||
.port = 1, \
|
||||
.baud = 921600, \
|
||||
@ -29,15 +31,54 @@
|
||||
.queue_size = 16, \
|
||||
.rx_buffer_size = 1024, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_SDIO_CONFIG() \
|
||||
.sdio = { \
|
||||
.width = 4, \
|
||||
.clk = 18, \
|
||||
.cmd = 19, \
|
||||
.d0 = 49, \
|
||||
.d1 = 50, \
|
||||
.d0 = 14, \
|
||||
.d1 = 15, \
|
||||
.d2 = 16, \
|
||||
.d3 = 17, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_ETH_CONFIG() \
|
||||
.ethernet = { \
|
||||
.mdc_io = 23, \
|
||||
.mdio_io = 18, \
|
||||
.phy_addr = 1, \
|
||||
.rst_io= 5, \
|
||||
}, \
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_SPI_CONFIG()
|
||||
#define EPPP_TRANSPORT_INIT(cfg) eppp_spi_init(&cfg->spi)
|
||||
#define EPPP_TRANSPORT_DEINIT(handle) eppp_spi_deinit(handle)
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_UART_CONFIG()
|
||||
#define EPPP_TRANSPORT_INIT(cfg) eppp_uart_init(&cfg->uart)
|
||||
#define EPPP_TRANSPORT_DEINIT(handle) eppp_uart_deinit(handle)
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_SDIO_CONFIG()
|
||||
#define EPPP_TRANSPORT_INIT(cfg) eppp_sdio_init(&cfg->sdio)
|
||||
#define EPPP_TRANSPORT_DEINIT(handle) eppp_sdio_deinit(handle)
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_ETH
|
||||
#define EPPP_DEFAULT_TRANSPORT_CONFIG() EPPP_DEFAULT_ETH_CONFIG()
|
||||
#define EPPP_TRANSPORT_INIT(cfg) eppp_eth_init(&cfg->ethernet)
|
||||
#define EPPP_TRANSPORT_DEINIT(handle) eppp_eth_deinit(handle)
|
||||
|
||||
#else
|
||||
#error Unexpeted transport
|
||||
#endif
|
||||
|
||||
|
||||
#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \
|
||||
.transport = EPPP_TRANSPORT_UART, \
|
||||
EPPP_DEFAULT_TRANSPORT_CONFIG() \
|
||||
. task = { \
|
||||
.run_task = true, \
|
||||
.stack_size = 4096, \
|
||||
@ -63,6 +104,7 @@ typedef enum eppp_transport {
|
||||
EPPP_TRANSPORT_UART,
|
||||
EPPP_TRANSPORT_SPI,
|
||||
EPPP_TRANSPORT_SDIO,
|
||||
EPPP_TRANSPORT_ETHERNET,
|
||||
} eppp_transport_t;
|
||||
|
||||
|
||||
@ -71,6 +113,7 @@ typedef struct eppp_config_t {
|
||||
|
||||
struct eppp_config_spi_s {
|
||||
int host;
|
||||
bool is_master;
|
||||
int mosi;
|
||||
int miso;
|
||||
int sclk;
|
||||
@ -92,6 +135,7 @@ typedef struct eppp_config_t {
|
||||
} uart;
|
||||
|
||||
struct eppp_config_sdio_s {
|
||||
bool is_host;
|
||||
int width;
|
||||
int clk;
|
||||
int cmd;
|
||||
@ -101,6 +145,13 @@ typedef struct eppp_config_t {
|
||||
int d3;
|
||||
} sdio;
|
||||
|
||||
struct eppp_config_ethernet_s {
|
||||
int mdc_io;
|
||||
int mdio_io;
|
||||
int phy_addr;
|
||||
int rst_io;
|
||||
} ethernet;
|
||||
|
||||
struct eppp_config_task_s {
|
||||
bool run_task;
|
||||
int stack_size;
|
||||
@ -116,20 +167,39 @@ typedef struct eppp_config_t {
|
||||
|
||||
} eppp_config_t;
|
||||
|
||||
typedef struct eppp_handle* eppp_transport_handle_t;
|
||||
|
||||
esp_netif_t *eppp_connect(eppp_config_t *config);
|
||||
|
||||
esp_netif_t *eppp_listen(eppp_config_t *config);
|
||||
|
||||
void eppp_close(esp_netif_t *netif);
|
||||
|
||||
/**
|
||||
* @brief Initialize the EPPP link layer
|
||||
* @param role
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config);
|
||||
|
||||
void eppp_deinit(esp_netif_t *netif);
|
||||
|
||||
esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms);
|
||||
|
||||
esp_netif_t *eppp_netif_init(eppp_type_t role, eppp_transport_handle_t h, eppp_config_t *eppp_config);
|
||||
|
||||
esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms);
|
||||
|
||||
esp_err_t eppp_netif_start(esp_netif_t *netif);
|
||||
|
||||
void eppp_netif_deinit(esp_netif_t *netif);
|
||||
|
||||
/**
|
||||
* @brief Performs EPPP link task operation (Used for task-less implementation)
|
||||
* @param netif
|
||||
* @return
|
||||
* - ESP_OK on success, ESP_FAIL on failure: the operation shall continue
|
||||
* - ESP_ERR_TIMEOUT indicates that the operation was requested to stop
|
||||
*/
|
||||
esp_err_t eppp_perform(esp_netif_t *netif);
|
||||
|
11
components/eppp_link/include/eppp_transport_eth.h
Normal file
11
components/eppp_link/include/eppp_transport_eth.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "eppp_link.h"
|
||||
|
||||
eppp_transport_handle_t eppp_eth_init(struct eppp_config_ethernet_s *config);
|
||||
void eppp_eth_deinit(eppp_transport_handle_t h);
|
11
components/eppp_link/include/eppp_transport_sdio.h
Normal file
11
components/eppp_link/include/eppp_transport_sdio.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "eppp_link.h"
|
||||
|
||||
eppp_transport_handle_t eppp_sdio_init(struct eppp_config_sdio_s *config);
|
||||
void eppp_sdio_deinit(eppp_transport_handle_t h);
|
11
components/eppp_link/include/eppp_transport_spi.h
Normal file
11
components/eppp_link/include/eppp_transport_spi.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "eppp_link.h"
|
||||
|
||||
eppp_transport_handle_t eppp_spi_init(struct eppp_config_spi_s *config);
|
||||
void eppp_spi_deinit(eppp_transport_handle_t h);
|
11
components/eppp_link/include/eppp_transport_uart.h
Normal file
11
components/eppp_link/include/eppp_transport_uart.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "eppp_link.h"
|
||||
|
||||
eppp_transport_handle_t eppp_uart_init(struct eppp_config_uart_s *config);
|
||||
void eppp_uart_deinit(eppp_transport_handle_t h);
|
8
components/esp_dns/.cz.yaml
Normal file
8
components/esp_dns/.cz.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(dns): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_dns
|
||||
tag_format: esp_dns-v$version
|
||||
version: 1.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
7
components/esp_dns/CHANGELOG.md
Normal file
7
components/esp_dns/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/esp_dns-v1.0.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add ESP DNS module with support for UDP, TCP, DoT, and DoH protocols ([57cd6080](https://github.com/espressif/esp-protocols/commit/57cd6080))
|
15
components/esp_dns/CMakeLists.txt
Normal file
15
components/esp_dns/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
idf_component_register(SRCS
|
||||
"esp_dns_udp.c"
|
||||
"esp_dns_tcp.c"
|
||||
"esp_dns_dot.c"
|
||||
"esp_dns_doh.c"
|
||||
"esp_dns.c"
|
||||
"esp_dns_lwip.c"
|
||||
"esp_dns_utils.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES nvs_flash lwip esp_event esp-tls esp_http_client esp-tls tcp_transport)
|
||||
|
||||
if(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
|
||||
target_link_libraries(${COMPONENT_LIB} "-u lwip_hook_netconn_external_resolve")
|
||||
endif()
|
201
components/esp_dns/LICENSE
Normal file
201
components/esp_dns/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
223
components/esp_dns/README.md
Normal file
223
components/esp_dns/README.md
Normal file
@ -0,0 +1,223 @@
|
||||
# ESP DNS Component
|
||||
|
||||
This component provides a flexible DNS resolution system for ESP32 devices with support for multiple DNS protocols. It allows applications to resolve domain names using various transport methods, including standard UDP/TCP DNS, and securely resolve them using DNS over TLS (DoT) and DNS over HTTPS (DoH).
|
||||
|
||||
## Table of Contents
|
||||
- [Features](#features)
|
||||
- [Requirements](#requirements)
|
||||
- [How to Use](#how-to-use)
|
||||
- [Configuration](#configuration)
|
||||
- [Certificate Options](#certificate-options)
|
||||
- [Limitations](#limitations)
|
||||
- [Performance Considerations](#performance-considerations)
|
||||
- [How It Works](#how-it-works)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Protocol Support** Choose from various DNS protocols:
|
||||
- Standard UDP DNS (Port 53)
|
||||
- TCP DNS (Port 53)
|
||||
- DNS over TLS (DoT) (Port 853)
|
||||
- DNS over HTTPS (DoH) (Port 443)
|
||||
|
||||
- **Secure DNS Resolution**: Supports encrypted DNS queries using TLS and HTTPS to protect privacy and prevent DNS spoofing.
|
||||
|
||||
- **Flexible Configuration**: Easily configure DNS servers, ports, timeouts, and protocol-specific options.
|
||||
|
||||
- **LWIP Integration**: Seamlessly integrates with the ESP-IDF networking stack through LWIP hooks.
|
||||
|
||||
- **Standard getaddrinfo() Interface**: Use the standard `getaddrinfo()` function to resolve domain names.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- ESP-IDF v5.0 or newer
|
||||
- Network connectivity (Wi-Fi or Ethernet)
|
||||
- For DoT/DoH: Sufficient RAM for TLS operations
|
||||
|
||||
|
||||
## How to Use
|
||||
|
||||
### 1. Enable custom DNS resolution
|
||||
|
||||
To enable custom DNS resolution, configure the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` setting either through menuconfig or by adding `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM=y` to your `sdkconfig.defaults` file to pre-set the configuration during the build process.
|
||||
|
||||
### 2. Configure DNS Settings
|
||||
|
||||
Initialize the DNS component with your preferred configuration:
|
||||
```C
|
||||
#include "esp_dns.h"
|
||||
|
||||
/* Configure DNS over HTTPS */
|
||||
esp_dns_config_t dns_config = {
|
||||
.dns_server = "dns.google", /* DNS server hostname or IP address */
|
||||
.port = ESP_DNS_DEFAULT_DOH_PORT, /* Optional: Server port (443 is default for HTTPS) */
|
||||
.timeout_ms = ESP_DNS_DEFAULT_TIMEOUT_MS, /* Optional: Request timeout in milliseconds (10000ms default) */
|
||||
.tls_config = {
|
||||
/* Optional: Use ESP-IDF certificate bundle for validating popular DNS providers */
|
||||
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||
|
||||
/* Or provide a custom certificate in PEM format (string) for your DNS server */
|
||||
/* Note: Only PEM format is supported; DER format certificates are not supported yet */
|
||||
.cert_pem = server_root_cert_pem_start,
|
||||
|
||||
/* Note: If both crt_bundle_attach and cert_pem are provided,
|
||||
crt_bundle_attach is preferred over cert_pem */
|
||||
},
|
||||
.protocol_config.doh_config = {
|
||||
.url_path = "/dns-query", /* Optional: DoH endpoint path on the server ("/dns-query" default) */
|
||||
}
|
||||
};
|
||||
|
||||
/* Initialize DNS component based on protocol */
|
||||
esp_dns_handle_t dns_handle = NULL;
|
||||
|
||||
/* Call esp_dns_init_doh() to use DNS over HTTPS */
|
||||
dns_handle = esp_dns_init_doh(&dns_config);
|
||||
|
||||
/* or Call esp_dns_init_dot() to use DNS over TLS */
|
||||
dns_handle = esp_dns_init_dot(&dns_config);
|
||||
|
||||
/* or Call esp_dns_init_tcp() to use DNS over TCP */
|
||||
dns_handle = esp_dns_init_tcp(&dns_config);
|
||||
|
||||
/* or Call esp_dns_init_udp() to use DNS over UDP */
|
||||
dns_handle = esp_dns_init_udp(&dns_config);
|
||||
|
||||
if (dns_handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize DNS");
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Resolve Domain Names
|
||||
|
||||
Once initialized, the component automatically handles DNS resolution through the standard `getaddrinfo()` function:
|
||||
|
||||
```C
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
};
|
||||
struct addrinfo res;
|
||||
int err = getaddrinfo("www.example.com", "80", &hints, &res);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "DNS lookup failed: %d", err);
|
||||
return;
|
||||
}
|
||||
/* Use the resolved addresses */
|
||||
/* ... */
|
||||
|
||||
/* Free the address info when done */
|
||||
freeaddrinfo(res);
|
||||
```
|
||||
|
||||
### 4. Cleanup
|
||||
|
||||
When you're done using the DNS component, clean up resources based on the protocol used:
|
||||
|
||||
```C
|
||||
int ret = 0;
|
||||
/* Call esp_dns_cleanup_doh() to cleanup DNS over HTTPS */
|
||||
ret = esp_dns_cleanup_doh(dns_handle);
|
||||
|
||||
/* or Call esp_dns_cleanup_dot() to cleanup DNS over TLS */
|
||||
ret = esp_dns_cleanup_dot(dns_handle);
|
||||
|
||||
/* or Call esp_dns_cleanup_tcp() to cleanup DNS over TCP */
|
||||
ret = esp_dns_cleanup_tcp(dns_handle);
|
||||
|
||||
/* or Call esp_dns_cleanup_udp() to cleanup DNS over UDP */
|
||||
ret = esp_dns_cleanup_udp(dns_handle);
|
||||
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to cleanup DNS");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
### Setting Up the ESP DNS Component
|
||||
|
||||
1. Navigate to your project directory.
|
||||
2. Execute `idf.py menuconfig`.
|
||||
3. Locate the **Component config -> LWIP -> Hooks -> Netconn external resolve Hook** section.
|
||||
4. Change the setting to `Custom implementation`.
|
||||
|
||||
### Common Settings
|
||||
|
||||
| Parameter | Description | Default Value |
|
||||
|-----------|-------------|---------------|
|
||||
| `dns_server` | IP address or hostname of DNS server | `"8.8.8.8"` (Google DNS) |
|
||||
| `port` | Server port number | Protocol-dependent (53, 853, or 443) |
|
||||
| `timeout_ms` | Query timeout in milliseconds | `10000` (10 seconds) |
|
||||
|
||||
### TLS Configuration (for DoT and DoH)
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `crt_bundle_attach` | Function pointer to attach certificate bundle |
|
||||
| `server_cert` | SSL server certificate in PEM format |
|
||||
| `alpn_protos` | ALPN protocols for DoH (typically `"h2"`) |
|
||||
|
||||
### Protocol-Specific Options
|
||||
|
||||
#### DoH Options
|
||||
- **URL Path**: URL path for DoH service (e.g., "/dns-query")
|
||||
|
||||
|
||||
## Certificate Options
|
||||
|
||||
When using secure DNS protocols (DoT and DoH), you have two certificate options:
|
||||
|
||||
1. **Certificate Bundle**: Use ESP-IDF's certificate bundle for validating connections to popular DNS providers.
|
||||
2. **Custom Certificate**: Provide your own certificate in PEM format for custom DNS servers.
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
- The UDP DNS protocol implementation relies on the native LWIP DNS resolver.
|
||||
- Transport protocol selection must be configured through `esp_dns_init_xxx()` rather than `getaddrinfo()` parameters due to LWIP resolver hook limitations.
|
||||
- Maximum response size is limited by the buffer size (default: 512 bytes) for DNS over TLS (DOT) and TCP protocols.
|
||||
- Only one DNS protocol can be active at a time.
|
||||
|
||||
- **Resolution Speed**:
|
||||
- UDP DNS is fastest but least secure
|
||||
- DoH typically has the highest latency but offers the best security
|
||||
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Memory Usage**: DoH and DoT require more memory due to TLS overhead:
|
||||
|
||||
TBD: Fill in the memory usage for each protocol
|
||||
|
||||
|
||||
## How It Works
|
||||
This component utilizes the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` hook to override the core DNS functionality of LWIP and implement custom DNS over HTTPS resolution. To enable this, ensure that the configuration option `Component config → LWIP → Hooks → Netconn external resolve Hook` is set to `Custom implementation`.
|
||||
|
||||
Once you add this component to your project, it will replace the default LWIP DNS resolution automatically.
|
||||
|
||||
**⚠️ Warning:** This component cannot work alongside other components that use the `CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM` hook.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Connection Issues**:
|
||||
- Ensure network connectivity and correct DNS server configuration
|
||||
- Verify that your network allows the required ports (53, 853, or 443)
|
||||
|
||||
- **Certificate Errors**:
|
||||
- Verify that the correct certificate is provided for secure protocols
|
||||
- For public DNS servers, use the certificate bundle approach
|
||||
|
||||
- **Timeout Errors**:
|
||||
- Increase the timeout value for slow network connections
|
||||
- Try a different DNS server that might be geographically closer
|
||||
|
||||
- **Memory Issues**:
|
||||
- If you encounter memory errors, consider increasing the task stack size
|
||||
- For memory-constrained devices, prefer UDP DNS.
|
157
components/esp_dns/esp_dns.c
Normal file
157
components/esp_dns/esp_dns.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file esp_dns.c
|
||||
* @brief Custom DNS module for ESP32 with multiple protocol support
|
||||
*
|
||||
* This module provides DNS resolution capabilities with support for various protocols:
|
||||
* - Standard UDP/TCP DNS (Port 53)
|
||||
* - DNS over TLS (DoT) (Port 853)
|
||||
* - DNS over HTTPS (DoH) (Port 443)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_dns_priv.h"
|
||||
#include "esp_dns.h"
|
||||
|
||||
#define TAG "ESP_DNS"
|
||||
|
||||
/* Global DNS handle instance */
|
||||
esp_dns_handle_t g_dns_handle = NULL;
|
||||
|
||||
/* Mutex for protecting global handle access */
|
||||
static SemaphoreHandle_t s_dns_global_mutex = NULL;
|
||||
|
||||
/**
|
||||
* @brief Creates or returns a singleton DNS handle instance
|
||||
*
|
||||
* This function implements a singleton pattern for the DNS handle. It creates
|
||||
* a static instance of the dns_handle structure on first call and initializes
|
||||
* it to zeros. On subsequent calls, it returns a pointer to the same instance.
|
||||
*
|
||||
* The function ensures that only one DNS handle exists throughout the application
|
||||
* lifecycle, which helps manage resources efficiently.
|
||||
*
|
||||
* @return Pointer to the singleton DNS handle instance
|
||||
*/
|
||||
static esp_dns_handle_t esp_dns_create_handle(void)
|
||||
{
|
||||
static struct esp_dns_handle instance;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
memset(&instance, 0, sizeof(instance));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the DNS module with provided configuration
|
||||
*
|
||||
* @param config DNS configuration parameters
|
||||
*
|
||||
* @return On success, returns a handle to the initialized DNS module
|
||||
* On failure, returns NULL
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init(const esp_dns_config_t *config)
|
||||
{
|
||||
/* Create global mutex if it doesn't exist */
|
||||
if (s_dns_global_mutex == NULL) {
|
||||
s_dns_global_mutex = xSemaphoreCreateMutex();
|
||||
if (s_dns_global_mutex == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create global mutex");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take the global mutex */
|
||||
if (xSemaphoreTake(s_dns_global_mutex, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to take global mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if we need to clean up an existing handle */
|
||||
if (g_dns_handle != NULL) {
|
||||
ESP_LOGE(TAG, "DNS handle already initialized. Call esp_dns_cleanup() before reinitializing");
|
||||
xSemaphoreGive(s_dns_global_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate memory for the new handle */
|
||||
esp_dns_handle_t handle = esp_dns_create_handle();
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for DNS handle");
|
||||
xSemaphoreGive(s_dns_global_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy configuration */
|
||||
memcpy(&handle->config, config, sizeof(esp_dns_config_t));
|
||||
|
||||
/* Create mutex for this handle */
|
||||
handle->lock = xSemaphoreCreateMutex();
|
||||
if (handle->lock == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create handle mutex");
|
||||
free(handle);
|
||||
xSemaphoreGive(s_dns_global_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set global handle */
|
||||
g_dns_handle = handle;
|
||||
handle->initialized = true;
|
||||
|
||||
/* Release global mutex */
|
||||
xSemaphoreGive(s_dns_global_mutex);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup and release resources associated with a DNS module handle
|
||||
*
|
||||
* @param handle DNS module handle previously obtained from esp_dns_init()
|
||||
*
|
||||
* @return 0 on success, non-zero error code on failure
|
||||
*/
|
||||
int esp_dns_cleanup(esp_dns_handle_t handle)
|
||||
{
|
||||
/* Take the handle mutex */
|
||||
if (xSemaphoreTake(handle->lock, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to take handle mutex during cleanup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Release and delete mutex */
|
||||
xSemaphoreGive(handle->lock);
|
||||
vSemaphoreDelete(handle->lock);
|
||||
|
||||
/* Take global mutex before modifying global handle */
|
||||
if (s_dns_global_mutex != NULL && xSemaphoreTake(s_dns_global_mutex, portMAX_DELAY) == pdTRUE) {
|
||||
/* Clear global handle if it matches this one */
|
||||
if (g_dns_handle == handle) {
|
||||
g_dns_handle = NULL;
|
||||
}
|
||||
|
||||
xSemaphoreGive(s_dns_global_mutex);
|
||||
}
|
||||
|
||||
/* Mark as uninitialized */
|
||||
handle->initialized = false;
|
||||
|
||||
return 0;
|
||||
}
|
309
components/esp_dns/esp_dns_doh.c
Normal file
309
components/esp_dns/esp_dns_doh.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_dns_utils.h"
|
||||
#include "esp_dns_priv.h"
|
||||
#include "esp_dns.h"
|
||||
|
||||
#define TAG "ESP_DNS_DOH"
|
||||
|
||||
#define SERVER_URL_MAX_SZ 256
|
||||
|
||||
/**
|
||||
* @brief Initializes the DNS over HTTPS (DoH) module
|
||||
*
|
||||
* Sets up the DoH service using the provided configuration. Validates the parameters,
|
||||
* sets the protocol, and initializes the DNS module. Returns a handle for further use.
|
||||
*
|
||||
* @param config Pointer to the DNS configuration structure, which must be initialized
|
||||
*
|
||||
* @return On success, returns a handle to the initialized DoH module; returns NULL on failure
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init_doh(esp_dns_config_t *config)
|
||||
{
|
||||
ESP_LOGD(TAG, "Initializing DNS over HTTPS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid configuration (NULL)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config->protocol = ESP_DNS_PROTOCOL_DOH;
|
||||
|
||||
esp_dns_handle_t handle = esp_dns_init(config);
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize DNS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over HTTPS(%d)", config->protocol);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleans up the DNS over HTTPS (DoH) module
|
||||
*
|
||||
* Releases resources allocated for the DoH service. Validates the parameters,
|
||||
* checks the protocol, and cleans up the DNS module.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle to be cleaned up
|
||||
*
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
int esp_dns_cleanup_doh(esp_dns_handle_t handle)
|
||||
{
|
||||
ESP_LOGD(TAG, "Cleaning up DNS over HTTPS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid handle (NULL)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handle->config.protocol != ESP_DNS_PROTOCOL_DOH) {
|
||||
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = esp_dns_cleanup(handle);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to cleanup DNS");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Empty the handle */
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
|
||||
ESP_LOGD(TAG, "DNS module cleaned up DNS Over HTTPS successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HTTP event handler for DNS over HTTPS requests
|
||||
*
|
||||
* Handles HTTP events during DNS over HTTPS communication, including data reception,
|
||||
* connection status, and error conditions.
|
||||
*
|
||||
* @param evt Pointer to the HTTP client event structure
|
||||
*
|
||||
* @return ESP_OK on success, or an error code on failure
|
||||
*/
|
||||
esp_err_t esp_dns_http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
char *temp_buff = NULL;
|
||||
size_t temp_buff_len = 0;
|
||||
esp_dns_handle_t handle = (esp_dns_handle_t)evt->user_data;
|
||||
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_HEADER_SENT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
||||
/* Check if buffer is null, if yes, initialize it */
|
||||
if (handle->response_buffer.buffer == NULL) {
|
||||
if (evt->data_len == 0) {
|
||||
ESP_LOGW(TAG, "Received empty HTTP data");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
temp_buff = malloc(evt->data_len);
|
||||
if (temp_buff) {
|
||||
handle->response_buffer.buffer = temp_buff;
|
||||
handle->response_buffer.length = evt->data_len;
|
||||
memcpy(handle->response_buffer.buffer, evt->data, evt->data_len);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Buffer allocation error");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
} else {
|
||||
/* Reallocate buffer to hold the new data chunk */
|
||||
int new_len = handle->response_buffer.length + evt->data_len;
|
||||
if (new_len == 0) {
|
||||
ESP_LOGW(TAG, "New data length is zero after receiving HTTP data");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
temp_buff = realloc(handle->response_buffer.buffer, new_len);
|
||||
if (temp_buff) {
|
||||
handle->response_buffer.buffer = temp_buff;
|
||||
memcpy(handle->response_buffer.buffer + handle->response_buffer.length, evt->data, evt->data_len);
|
||||
handle->response_buffer.length = new_len;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Buffer allocation error");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
||||
/* Entire response received, process it here */
|
||||
ESP_LOGD(TAG, "Received full response, length: %d", handle->response_buffer.length);
|
||||
|
||||
/* Check if the buffer indicates an HTTP error response */
|
||||
if (HttpStatus_Ok == esp_http_client_get_status_code(evt->client)) {
|
||||
/* Parse the DNS response */
|
||||
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
|
||||
handle->response_buffer.length,
|
||||
&handle->response_buffer.dns_response);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "HTTP Error: %d", esp_http_client_get_status_code(evt->client));
|
||||
temp_buff_len = handle->response_buffer.length > ESP_DNS_BUFFER_SIZE ? ESP_DNS_BUFFER_SIZE : handle->response_buffer.length;
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, handle->response_buffer.buffer, temp_buff_len, ESP_LOG_ERROR);
|
||||
handle->response_buffer.dns_response.status_code = ERR_VAL; /* TBD: Not handled properly yet */
|
||||
}
|
||||
|
||||
free(handle->response_buffer.buffer);
|
||||
handle->response_buffer.buffer = NULL;
|
||||
handle->response_buffer.length = 0;
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
ESP_LOGE(TAG, "HTTP_EVENT_REDIRECT: Not supported(%d)", esp_http_client_get_status_code(evt->client));
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resolves a hostname using DNS over HTTPS
|
||||
*
|
||||
* This function generates a DNS request, sends it via HTTPS, and processes
|
||||
* the response to extract IP addresses.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle
|
||||
* @param name The hostname to resolve
|
||||
* @param addr Pointer to store the resolved IP addresses
|
||||
* @param rrtype The address RR type (A or AAAA)
|
||||
*
|
||||
* @return ERR_OK on success, or an error code on failure
|
||||
*/
|
||||
err_t dns_resolve_doh(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
|
||||
{
|
||||
uint8_t buffer_qry[ESP_DNS_BUFFER_SIZE];
|
||||
|
||||
/* Initialize error status */
|
||||
err_t err = ERR_OK;
|
||||
const char *prefix = "https://";
|
||||
|
||||
/* Set default values for DoH configuration if not specified */
|
||||
const char *url_path = handle->config.protocol_config.doh_config.url_path ?
|
||||
handle->config.protocol_config.doh_config.url_path : "dns-query";
|
||||
int port = handle->config.port ?
|
||||
handle->config.port : ESP_DNS_DEFAULT_DOH_PORT;
|
||||
|
||||
/* Calculate required URL length: https:// + server + / + path + null terminator */
|
||||
size_t url_len = strlen(prefix) + \
|
||||
strlen(handle->config.dns_server) + 1 + \
|
||||
strlen(url_path) + 1; /* 1 for '/' and 1 for '\0' */
|
||||
|
||||
/* Allocate memory for the full server URL */
|
||||
char *dns_server_url = malloc(url_len);
|
||||
if (dns_server_url == NULL) {
|
||||
ESP_LOGE(TAG, "Memory allocation failed");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Construct the complete server URL by combining prefix, server and path */
|
||||
snprintf(dns_server_url, url_len, "%s%s/%s", prefix,
|
||||
handle->config.dns_server,
|
||||
url_path);
|
||||
|
||||
/* Configure the HTTP client with base settings */
|
||||
esp_http_client_config_t config = {
|
||||
.url = dns_server_url,
|
||||
.event_handler = esp_dns_http_event_handler,
|
||||
.method = HTTP_METHOD_POST,
|
||||
.user_data = handle,
|
||||
.port = port,
|
||||
};
|
||||
|
||||
/* Configure TLS certificate settings - either using bundle or PEM cert */
|
||||
if (handle->config.tls_config.crt_bundle_attach) {
|
||||
config.crt_bundle_attach = handle->config.tls_config.crt_bundle_attach;
|
||||
} else {
|
||||
config.cert_pem = handle->config.tls_config.cert_pem; /* Use the root certificate for dns.google if needed */
|
||||
}
|
||||
|
||||
/* Clear the response buffer to ensure no residual data remains */
|
||||
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
|
||||
|
||||
/* Create DNS query in wire format */
|
||||
size_t query_size = esp_dns_create_query(buffer_qry, sizeof(buffer_qry), name, rrtype, &handle->response_buffer.dns_response.id);
|
||||
if (query_size == -1) {
|
||||
ESP_LOGE(TAG, "Error: Hostname too big");
|
||||
err = ERR_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Initialize HTTP client with the configuration */
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
if (client == NULL) {
|
||||
ESP_LOGE(TAG, "Error initializing HTTP client");
|
||||
err = ERR_VAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Set Content-Type header for DNS-over-HTTPS */
|
||||
esp_err_t ret = esp_http_client_set_header(client, "Content-Type", "application/dns-message");
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error setting HTTP header: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
/* Set the DNS query as POST data */
|
||||
ret = esp_http_client_set_post_field(client, (const char *)buffer_qry, query_size);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error setting POST field: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
/* Execute the HTTP request */
|
||||
ret = esp_http_client_perform(client);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGD(TAG, "HTTP POST Status = %d, content_length = %lld",
|
||||
esp_http_client_get_status_code(client),
|
||||
esp_http_client_get_content_length(client));
|
||||
|
||||
/* Verify HTTP status code and DNS response status */
|
||||
if ((HttpStatus_Ok != esp_http_client_get_status_code(client)) ||
|
||||
(handle->response_buffer.dns_response.status_code != ERR_OK)) {
|
||||
err = ERR_ARG;
|
||||
goto client_cleanup;
|
||||
}
|
||||
|
||||
/* Extract IP addresses from DNS response */
|
||||
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(ret));
|
||||
err = ERR_VAL;
|
||||
}
|
||||
|
||||
/* Clean up HTTP client */
|
||||
client_cleanup:
|
||||
esp_http_client_cleanup(client);
|
||||
|
||||
/* Free allocated memory */
|
||||
cleanup:
|
||||
free(dns_server_url);
|
||||
|
||||
return err;
|
||||
}
|
205
components/esp_dns/esp_dns_dot.c
Normal file
205
components/esp_dns/esp_dns_dot.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_transport.h"
|
||||
#include "esp_transport_ssl.h"
|
||||
#include "esp_dns_priv.h"
|
||||
#include "esp_dns.h"
|
||||
|
||||
|
||||
#define TAG "ESP_DNS_DOT"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the DNS over TLS (DoT) module
|
||||
*
|
||||
* Sets up the DoT service using the provided configuration. Validates the parameters,
|
||||
* sets the protocol, and initializes the DNS module. Returns a handle for further use.
|
||||
*
|
||||
* @param config Pointer to the DNS configuration structure, which must be initialized
|
||||
*
|
||||
* @return On success, returns a handle to the initialized DoT module; returns NULL on failure
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init_dot(esp_dns_config_t *config)
|
||||
{
|
||||
ESP_LOGD(TAG, "Initializing DNS over TLS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid configuration (NULL)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config->protocol = ESP_DNS_PROTOCOL_DOT;
|
||||
|
||||
esp_dns_handle_t handle = esp_dns_init(config);
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize DNS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over TLS(%d)", config->protocol);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Cleans up the DNS over TLS (DoT) module
|
||||
*
|
||||
* Releases resources allocated for the DoT service. Validates the parameters,
|
||||
* checks the protocol, and cleans up the DNS module.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle to be cleaned up
|
||||
*
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
int esp_dns_cleanup_dot(esp_dns_handle_t handle)
|
||||
{
|
||||
ESP_LOGD(TAG, "Cleaning up DNS over TLS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid handle (NULL)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handle->config.protocol != ESP_DNS_PROTOCOL_DOT) {
|
||||
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = esp_dns_cleanup(handle);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to cleanup DNS");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Empty the handle */
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
|
||||
ESP_LOGD(TAG, "DNS module cleaned up DNS Over TLS successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resolves a hostname using DNS over TLS (DoT)
|
||||
*
|
||||
* Performs DNS resolution over a TLS-encrypted connection. Creates a DNS query,
|
||||
* establishes a TLS connection, sends the query, and processes the response.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store the resolved IP address
|
||||
* @param rrtype DNS record type to query
|
||||
*
|
||||
* @return ERR_OK on success, or an error code on failure
|
||||
*/
|
||||
err_t dns_resolve_dot(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
|
||||
{
|
||||
int err = ERR_OK;
|
||||
esp_transport_handle_t transport = NULL;
|
||||
int len = 0;
|
||||
char dot_buffer[ESP_DNS_BUFFER_SIZE];
|
||||
size_t query_size;
|
||||
int timeout_ms;
|
||||
int dot_port;
|
||||
|
||||
if (addr == NULL) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
/* Set timeout and port values, using defaults if not specified in config */
|
||||
timeout_ms = handle->config.timeout_ms ? : ESP_DNS_DEFAULT_TIMEOUT_MS;
|
||||
dot_port = handle->config.port ? : ESP_DNS_DEFAULT_DOT_PORT;
|
||||
|
||||
/* Clear the response buffer to ensure no residual data remains */
|
||||
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
|
||||
|
||||
/* Create DNS query in wire format, leaving 2 bytes at start for length prefix as required by RFC 7858 */
|
||||
memset(dot_buffer, 0, ESP_DNS_BUFFER_SIZE);
|
||||
query_size = esp_dns_create_query((uint8_t *)(dot_buffer + 2), sizeof(dot_buffer) - 2,
|
||||
name, rrtype, &handle->response_buffer.dns_response.id);
|
||||
if (query_size == -1) {
|
||||
ESP_LOGE(TAG, "Error: Hostname too big");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Prepends the 2-byte length field to DNS messages as required by RFC 7858 */
|
||||
dot_buffer[0] = (query_size >> 8) & 0xFF;
|
||||
dot_buffer[1] = query_size & 0xFF;
|
||||
|
||||
transport = esp_transport_ssl_init();
|
||||
if (!transport) {
|
||||
ESP_LOGE(TAG, "Failed to initialize transport");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Configure TLS certificate settings - either using bundle or PEM cert */
|
||||
if (handle->config.tls_config.crt_bundle_attach) {
|
||||
esp_transport_ssl_crt_bundle_attach(transport, handle->config.tls_config.crt_bundle_attach);
|
||||
} else {
|
||||
if (handle->config.tls_config.cert_pem == NULL) {
|
||||
ESP_LOGE(TAG, "Certificate PEM data is null");
|
||||
err = ERR_VAL;
|
||||
goto cleanup;
|
||||
}
|
||||
esp_transport_ssl_set_cert_data(transport,
|
||||
handle->config.tls_config.cert_pem,
|
||||
strlen(handle->config.tls_config.cert_pem));
|
||||
}
|
||||
|
||||
if (esp_transport_connect(transport, handle->config.dns_server, dot_port, timeout_ms) < 0) {
|
||||
ESP_LOGE(TAG, "TLS connection failed");
|
||||
err = ERR_CONN;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Send DNS query */
|
||||
len = esp_transport_write(transport,
|
||||
dot_buffer,
|
||||
query_size + 2,
|
||||
timeout_ms);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "Failed to send DNS query");
|
||||
err = ERR_ABRT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Read response */
|
||||
memset(dot_buffer, 0, ESP_DNS_BUFFER_SIZE);
|
||||
len = esp_transport_read(transport,
|
||||
dot_buffer,
|
||||
sizeof(dot_buffer),
|
||||
timeout_ms);
|
||||
if (len > 0) {
|
||||
/* Skip the 2-byte length field that prepends DNS messages as required by RFC 7858 */
|
||||
handle->response_buffer.buffer = dot_buffer + 2;
|
||||
handle->response_buffer.length = len - 2;
|
||||
|
||||
/* Parse the DNS response */
|
||||
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
|
||||
handle->response_buffer.length,
|
||||
&handle->response_buffer.dns_response);
|
||||
|
||||
/* Extract IP addresses from DNS response */
|
||||
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
|
||||
if (err != ERR_OK) {
|
||||
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to receive response");
|
||||
err = ERR_ABRT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (transport) {
|
||||
esp_transport_close(transport);
|
||||
esp_transport_destroy(transport);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
111
components/esp_dns/esp_dns_lwip.c
Normal file
111
components/esp_dns/esp_dns_lwip.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file esp_dns_lwip.c
|
||||
* @brief Custom DNS module for ESP32 with multiple protocol support
|
||||
*
|
||||
* Provides DNS resolution capabilities with support for various protocols:
|
||||
* - Standard UDP/TCP DNS (Port 53)
|
||||
* - DNS over TLS (DoT) (Port 853)
|
||||
* - DNS over HTTPS (DoH) (Port 443)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_dns.h"
|
||||
#include "esp_dns_priv.h"
|
||||
|
||||
#define TAG "ESP_DNS_LWIP"
|
||||
|
||||
/* Global DNS handle instance */
|
||||
extern esp_dns_handle_t g_dns_handle;
|
||||
|
||||
/* ========================= LWIP HOOK FUNCTIONS ========================= */
|
||||
|
||||
#if defined(CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM)
|
||||
/**
|
||||
* @brief Custom DNS resolution hook for lwIP network connections
|
||||
*
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store resolved IP address
|
||||
* @param addrtype Type of address to resolve (IPv4/IPv6)
|
||||
* @param err Pointer to store error code
|
||||
*
|
||||
* @return int 0 if resolution should be handled by lwIP, 1 if handled by this module
|
||||
*/
|
||||
int lwip_hook_netconn_external_resolve(const char *name, ip_addr_t *addr, u8_t addrtype, err_t *err)
|
||||
{
|
||||
if (g_dns_handle == NULL) {
|
||||
ESP_LOGD(TAG, "ESP_DNS module not initialized, resolving through native DNS");
|
||||
*err = ERR_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name == NULL || addr == NULL || err == NULL) {
|
||||
if (err) {
|
||||
*err = ERR_ARG;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if name is already an IP address */
|
||||
if (ipaddr_aton(name, addr)) {
|
||||
*err = ERR_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if DNS server name matches or if it's localhost */
|
||||
if ((strcmp(name, g_dns_handle->config.dns_server) == 0) ||
|
||||
#if LWIP_HAVE_LOOPIF
|
||||
(strcmp(name, "localhost") == 0) ||
|
||||
#endif
|
||||
ipaddr_aton(name, addr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8_t rrtype;
|
||||
if ((addrtype == NETCONN_DNS_IPV4) || (addrtype == NETCONN_DNS_IPV4_IPV6)) {
|
||||
rrtype = DNS_RRTYPE_A;
|
||||
} else if ((addrtype == NETCONN_DNS_IPV6) || (addrtype == NETCONN_DNS_IPV6_IPV4)) {
|
||||
rrtype = DNS_RRTYPE_AAAA;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid address type");
|
||||
*err = ERR_VAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Resolve based on configured transport type */
|
||||
switch (g_dns_handle->config.protocol) {
|
||||
case ESP_DNS_PROTOCOL_UDP:
|
||||
/* Return zero as lwIP DNS can handle UDP DNS */
|
||||
return 0;
|
||||
case ESP_DNS_PROTOCOL_TCP:
|
||||
*err = dns_resolve_tcp(g_dns_handle, name, addr, rrtype);
|
||||
break;
|
||||
case ESP_DNS_PROTOCOL_DOT:
|
||||
*err = dns_resolve_dot(g_dns_handle, name, addr, rrtype);
|
||||
break;
|
||||
case ESP_DNS_PROTOCOL_DOH:
|
||||
*err = dns_resolve_doh(g_dns_handle, name, addr, rrtype);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid transport type");
|
||||
*err = ERR_VAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
#error "CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not defined. Please enable it in your menuconfig"
|
||||
#endif /* CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM */
|
116
components/esp_dns/esp_dns_priv.h
Normal file
116
components/esp_dns/esp_dns_priv.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file esp_dns_priv.h
|
||||
* @brief Private header for ESP DNS module
|
||||
*
|
||||
* This module provides DNS resolution capabilities with support for various protocols:
|
||||
* - Standard UDP/TCP DNS (Port 53)
|
||||
* - DNS over TLS (DoT)
|
||||
* - DNS over HTTPS (DoH)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lwip/prot/dns.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_dns.h"
|
||||
#include "esp_dns_utils.h"
|
||||
|
||||
/**
|
||||
* @brief Opaque handle type for DNS module instances
|
||||
*/
|
||||
struct esp_dns_handle {
|
||||
/* Configuration */
|
||||
esp_dns_config_t config; /* Copy of user configuration */
|
||||
|
||||
/* Connection state */
|
||||
bool initialized; /* Flag indicating successful initialization */
|
||||
|
||||
response_buffer_t response_buffer; /* Buffer for storing DNS response data during processing */
|
||||
|
||||
/* Thread safety */
|
||||
SemaphoreHandle_t lock; /* Mutex for synchronization */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize DNS module with configuration
|
||||
*
|
||||
* @param config DNS configuration parameters
|
||||
*
|
||||
* @return esp_dns_handle_t Handle to DNS module instance
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init(const esp_dns_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Clean up DNS module resources
|
||||
*
|
||||
* @param handle DNS module handle
|
||||
*
|
||||
* @return int 0 on success, negative error code on failure
|
||||
*/
|
||||
int esp_dns_cleanup(esp_dns_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Resolve hostname using DNS over HTTPS
|
||||
*
|
||||
* @param handle DNS module handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store resolved IP address
|
||||
* @param rrtype Record type (A or AAAA)
|
||||
*
|
||||
* @return err_t Error code
|
||||
*/
|
||||
err_t dns_resolve_doh(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
|
||||
|
||||
/**
|
||||
* @brief Resolve hostname using DNS over TLS
|
||||
*
|
||||
* @param handle DNS module handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store resolved IP address
|
||||
* @param rrtype Record type (A or AAAA)
|
||||
*
|
||||
* @return err_t Error code
|
||||
*/
|
||||
err_t dns_resolve_dot(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
|
||||
|
||||
/**
|
||||
* @brief Resolve hostname using TCP DNS
|
||||
*
|
||||
* @param handle DNS module handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store resolved IP address
|
||||
* @param rrtype Record type (A or AAAA)
|
||||
*
|
||||
* @return err_t Error code
|
||||
*/
|
||||
err_t dns_resolve_tcp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
|
||||
|
||||
/**
|
||||
* @brief Resolve hostname using UDP DNS
|
||||
*
|
||||
* @param handle DNS module handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store resolved IP address
|
||||
* @param rrtype Record type (A or AAAA)
|
||||
*
|
||||
* @return err_t Error code
|
||||
*/
|
||||
err_t dns_resolve_udp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype);
|
188
components/esp_dns/esp_dns_tcp.c
Normal file
188
components/esp_dns/esp_dns_tcp.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_transport.h"
|
||||
#include "esp_transport_tcp.h"
|
||||
#include "esp_dns_priv.h"
|
||||
#include "esp_dns.h"
|
||||
|
||||
#define TAG "ESP_DNS_TCP"
|
||||
|
||||
/**
|
||||
* @brief Initializes the TCP DNS module
|
||||
*
|
||||
* Sets up the TCP DNS service using the provided configuration. Validates the parameters,
|
||||
* sets the protocol, and initializes the DNS module.
|
||||
*
|
||||
* @param config Pointer to the DNS configuration structure
|
||||
*
|
||||
* @return Handle to the initialized TCP module on success, NULL on failure
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init_tcp(esp_dns_config_t *config)
|
||||
{
|
||||
ESP_LOGD(TAG, "Initializing TCP DNS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid configuration (NULL)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config->protocol = ESP_DNS_PROTOCOL_TCP;
|
||||
|
||||
esp_dns_handle_t handle = esp_dns_init(config);
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize DNS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over TCP(%d)", config->protocol);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleans up the TCP DNS module
|
||||
*
|
||||
* Releases resources allocated for the TCP DNS service. Validates the parameters,
|
||||
* checks the protocol, and cleans up the DNS module.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle to be cleaned up
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int esp_dns_cleanup_tcp(esp_dns_handle_t handle)
|
||||
{
|
||||
ESP_LOGD(TAG, "Cleaning up TCP DNS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid handle (NULL)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handle->config.protocol != ESP_DNS_PROTOCOL_TCP) {
|
||||
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = esp_dns_cleanup(handle);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to cleanup DNS");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Empty the handle */
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
|
||||
ESP_LOGD(TAG, "DNS module cleaned up DNS Over TCP successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resolves a hostname using TCP DNS
|
||||
*
|
||||
* Performs DNS resolution over TCP for the given hostname. Creates a TCP connection,
|
||||
* sends the DNS query, and processes the response.
|
||||
*
|
||||
* @param handle DNS handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store the resolved IP address
|
||||
* @param rrtype DNS record type
|
||||
*
|
||||
* @return ERR_OK on success, error code on failure
|
||||
*/
|
||||
err_t dns_resolve_tcp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
|
||||
{
|
||||
int err = ERR_OK;
|
||||
esp_transport_handle_t transport = NULL;
|
||||
int len = 0;
|
||||
char tcp_buffer[ESP_DNS_BUFFER_SIZE];
|
||||
size_t query_size;
|
||||
int timeout_ms;
|
||||
int tcp_port;
|
||||
|
||||
if (addr == NULL) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
/* Set timeout and port values, using defaults if not specified in config */
|
||||
timeout_ms = handle->config.timeout_ms ? : ESP_DNS_DEFAULT_TIMEOUT_MS;
|
||||
tcp_port = handle->config.port ? : ESP_DNS_DEFAULT_TCP_PORT;
|
||||
|
||||
/* Clear the response buffer to ensure no residual data remains */
|
||||
memset(&handle->response_buffer, 0, sizeof(response_buffer_t));
|
||||
|
||||
/* Create DNS query in wire format, leaving 2 bytes at start for length prefix as required by RFC 7858 */
|
||||
memset(tcp_buffer, 0, ESP_DNS_BUFFER_SIZE);
|
||||
query_size = esp_dns_create_query((uint8_t *)(tcp_buffer + 2), sizeof(tcp_buffer) - 2,
|
||||
name, rrtype, &handle->response_buffer.dns_response.id);
|
||||
if (query_size == -1) {
|
||||
ESP_LOGE(TAG, "Error: Hostname too big");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Prepends the 2-byte length field to DNS messages as required by RFC 7858 */
|
||||
tcp_buffer[0] = (query_size >> 8) & 0xFF;
|
||||
tcp_buffer[1] = query_size & 0xFF;
|
||||
|
||||
transport = esp_transport_tcp_init();
|
||||
if (!transport) {
|
||||
ESP_LOGE(TAG, "Failed to initialize transport");
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
if (esp_transport_connect(transport, handle->config.dns_server, tcp_port, timeout_ms) < 0) {
|
||||
ESP_LOGE(TAG, "TCP connection failed");
|
||||
err = ERR_CONN;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Send DNS query */
|
||||
len = esp_transport_write(transport,
|
||||
tcp_buffer,
|
||||
query_size + 2,
|
||||
timeout_ms);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "Failed to send DNS query");
|
||||
err = ERR_ABRT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Read response */
|
||||
memset(tcp_buffer, 0, ESP_DNS_BUFFER_SIZE);
|
||||
len = esp_transport_read(transport,
|
||||
tcp_buffer,
|
||||
sizeof(tcp_buffer),
|
||||
timeout_ms);
|
||||
if (len > 0) {
|
||||
/* Skip the 2-byte length field that prepends DNS messages as required by RFC 7858 */
|
||||
handle->response_buffer.buffer = tcp_buffer + 2;
|
||||
handle->response_buffer.length = len - 2;
|
||||
|
||||
/* Parse the DNS response */
|
||||
esp_dns_parse_response((uint8_t *)handle->response_buffer.buffer,
|
||||
handle->response_buffer.length,
|
||||
&handle->response_buffer.dns_response);
|
||||
|
||||
/* Extract IP addresses from DNS response */
|
||||
err = esp_dns_extract_ip_addresses_from_response(&handle->response_buffer.dns_response, addr);
|
||||
if (err != ERR_OK) {
|
||||
ESP_LOGE(TAG, "Failed to extract IP address from DNS response");
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to receive response");
|
||||
err = ERR_ABRT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (transport) {
|
||||
esp_transport_close(transport);
|
||||
esp_transport_destroy(transport);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
110
components/esp_dns/esp_dns_udp.c
Normal file
110
components/esp_dns/esp_dns_udp.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_dns_priv.h"
|
||||
#include "esp_dns.h"
|
||||
|
||||
|
||||
#define TAG "ESP_DNS_UDP"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the UDP DNS module
|
||||
*
|
||||
* Sets up the UDP DNS service using the provided configuration. Validates the parameters,
|
||||
* sets the protocol, and initializes the DNS module.
|
||||
*
|
||||
* @param config Pointer to the DNS configuration structure
|
||||
*
|
||||
* @return Handle to the initialized UDP module on success, NULL on failure
|
||||
*/
|
||||
esp_dns_handle_t esp_dns_init_udp(esp_dns_config_t *config)
|
||||
{
|
||||
ESP_LOGD(TAG, "Initializing UDP DNS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid configuration (NULL)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config->protocol = ESP_DNS_PROTOCOL_UDP;
|
||||
|
||||
esp_dns_handle_t handle = esp_dns_init(config);
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize DNS");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "DNS module initialized successfully with protocol DNS Over UDP(%d)", config->protocol);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Cleans up the UDP DNS module
|
||||
*
|
||||
* Releases resources allocated for the UDP DNS service. Validates the parameters,
|
||||
* checks the protocol, and cleans up the DNS module.
|
||||
*
|
||||
* @param handle Pointer to the DNS handle to be cleaned up
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int esp_dns_cleanup_udp(esp_dns_handle_t handle)
|
||||
{
|
||||
ESP_LOGD(TAG, "Cleaning up UDP DNS");
|
||||
|
||||
/* Validate parameters */
|
||||
if (handle == NULL) {
|
||||
ESP_LOGE(TAG, "Invalid handle (NULL)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handle->config.protocol != ESP_DNS_PROTOCOL_UDP) {
|
||||
ESP_LOGW(TAG, "Unknown protocol during cleanup: %d", handle->config.protocol);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = esp_dns_cleanup(handle);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to cleanup DNS");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Empty the handle */
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
|
||||
ESP_LOGD(TAG, "DNS module cleaned up DNS Over UDP successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Resolves a hostname using UDP DNS
|
||||
*
|
||||
* Performs DNS resolution over UDP for the given hostname. Creates a UDP connection,
|
||||
* sends the DNS query, and processes the response.
|
||||
*
|
||||
* @note This function is a placeholder and does not contain the actual implementation
|
||||
* for UDP DNS resolution. The implementation needs to be added.
|
||||
* As of now the resolution is performed by lwip dns module.
|
||||
*
|
||||
* @param handle DNS handle
|
||||
* @param name Hostname to resolve
|
||||
* @param addr Pointer to store the resolved IP address
|
||||
* @param rrtype DNS record type
|
||||
*
|
||||
* @return ERR_OK on success, error code on failure
|
||||
*/
|
||||
err_t dns_resolve_udp(const esp_dns_handle_t handle, const char *name, ip_addr_t *addr, u8_t rrtype)
|
||||
{
|
||||
// TBD: Implement UDP DNS resolution
|
||||
if (addr == NULL) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
242
components/esp_dns/esp_dns_utils.c
Normal file
242
components/esp_dns/esp_dns_utils.c
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_random.h"
|
||||
#include "esp_dns_utils.h"
|
||||
|
||||
/**
|
||||
* @brief Creates a DNS query packet in the provided buffer
|
||||
*
|
||||
* @param buffer Buffer to store the DNS query
|
||||
* @param buffer_size Size of the buffer
|
||||
* @param hostname Domain name to query
|
||||
* @param addrtype Type of address to query (A or AAAA)
|
||||
* @param id_o Pointer to store the generated query ID
|
||||
*
|
||||
* @return size_t Size of the created query packet, or -1 on error
|
||||
*/
|
||||
size_t esp_dns_create_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o)
|
||||
{
|
||||
/*
|
||||
* Sample DNS Query for example.com (Type A)
|
||||
* 0x00, 0x00, // Transaction ID
|
||||
* 0x01, 0x00, // Flags: Standard query
|
||||
* 0x00, 0x01, // Questions: 1
|
||||
* 0x00, 0x00, // Answer RRs: 0
|
||||
* 0x00, 0x00, // Authority RRs: 0
|
||||
* 0x00, 0x00, // Additional RRs: 0
|
||||
* 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', // QNAME: example.com
|
||||
* 0x03, 'c', 'o', 'm',
|
||||
* 0x00, // End of QNAME
|
||||
* 0x00, 0x01, // QTYPE: A (host address)
|
||||
* 0x00, 0x01 // QCLASS: IN (internet)
|
||||
*/
|
||||
|
||||
dns_header_t *header = (dns_header_t *)buffer;
|
||||
memset(buffer, 0, buffer_size);
|
||||
|
||||
/* Set header fields */
|
||||
*id_o = (uint16_t)(esp_random() & 0xFFFF); /* Return the id for response validation */
|
||||
header->id = htons(*id_o); /* Random transaction ID */
|
||||
header->flags = htons(0x0100); /* Standard query with recursion */
|
||||
header->qdcount = htons(1); /* One question */
|
||||
|
||||
/* Add the question name */
|
||||
uint8_t *qname = buffer + sizeof(dns_header_t);
|
||||
const char *dot = hostname;
|
||||
while (*dot) {
|
||||
const char *next_dot = strchr(dot, '.');
|
||||
if (!next_dot) {
|
||||
next_dot = dot + strlen(dot);
|
||||
}
|
||||
uint8_t len = next_dot - dot;
|
||||
*qname++ = len;
|
||||
/* Check for buffer overflow */
|
||||
if ((qname - buffer) > buffer_size) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(qname, dot, len);
|
||||
qname += len;
|
||||
dot = (*next_dot) ? next_dot + 1 : next_dot;
|
||||
}
|
||||
*qname++ = 0; /* Null-terminate the question name */
|
||||
|
||||
/* Set question fields */
|
||||
dns_question_t *question = (dns_question_t *)qname;
|
||||
question->qtype = htons(addrtype);
|
||||
question->qclass = htons(DNS_RRCLASS_IN);
|
||||
|
||||
/* Return the total query size */
|
||||
return (qname + sizeof(dns_question_t)) - buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Skips over a DNS name in a DNS reply message and returns the offset to the end of the name.
|
||||
*
|
||||
* This function handles both uncompressed labels and compression pointers according to RFC 1035.
|
||||
* Reference: RFC 1035, sections 3.1 (Name Space Definitions) and 4.1.4 (Message Compression).
|
||||
*
|
||||
* @param ptr Pointer to the start of the DNS name in the DNS message
|
||||
* @param remaining_bytes Number of bytes remaining in the buffer
|
||||
*
|
||||
* @return uint8_t* Pointer to the end of the DNS name, or NULL on error
|
||||
*/
|
||||
static uint8_t *skip_dns_name(uint8_t *ptr, size_t remaining_bytes)
|
||||
{
|
||||
uint8_t offset = 0;
|
||||
|
||||
/* Loop through each part of the name, handling labels and compression pointers */
|
||||
while (ptr[offset] != 0) {
|
||||
if (offset >= remaining_bytes) {
|
||||
return NULL;
|
||||
}
|
||||
/* Check if this part is a compression pointer, indicated by the two high bits set to 1 (0xC0) */
|
||||
/* RFC 1035, Section 4.1.4: Compression pointers */
|
||||
if ((ptr[offset] & 0xC0) == 0xC0) {
|
||||
/* Compression pointer is 2 bytes; move offset by 2 and stop */
|
||||
offset += 2;
|
||||
return ptr + offset; /* End of name processing due to pointer */
|
||||
} else {
|
||||
/* Otherwise, it's a label
|
||||
RFC 1035, Section 3.1: Labels
|
||||
- The first byte is the length of this label
|
||||
- Followed by 'length' bytes of label content */
|
||||
offset += ptr[offset] + 1; /* Move past this label (1 byte for length + label content) */
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 1035, Section 3.1: End of a name is indicated by a zero-length byte (0x00) */
|
||||
offset += 1; /* Move past the terminating zero byte */
|
||||
return ptr + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parses a DNS response message
|
||||
*
|
||||
* @param buffer Buffer containing the DNS response
|
||||
* @param response_size Size of the response buffer
|
||||
*
|
||||
* @param dns_response Structure to store parsed response
|
||||
*/
|
||||
void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response)
|
||||
{
|
||||
/* Validate input buffer */
|
||||
assert(buffer != NULL);
|
||||
|
||||
dns_header_t *header = (dns_header_t *)buffer;
|
||||
|
||||
dns_response->status_code = ERR_OK; /* Initialize DNS response code */
|
||||
|
||||
/* Check if there are answers and Transaction id matches */
|
||||
int answer_count = ntohs(header->ancount);
|
||||
if ((ntohs(header->id) != dns_response->id) || (answer_count == 0)) {
|
||||
dns_response->status_code = ERR_VAL; /* DNS response code */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure only MAX_ANSWERS are processed */
|
||||
dns_response->num_answers = (answer_count < MAX_ANSWERS ? answer_count : MAX_ANSWERS);
|
||||
|
||||
/* Skip the header and question section */
|
||||
uint8_t *ptr = buffer + sizeof(dns_header_t);
|
||||
|
||||
/* Skip the question name */
|
||||
ptr = skip_dns_name(ptr, response_size - (ptr - buffer));
|
||||
if (ptr == NULL) {
|
||||
dns_response->status_code = ERR_VAL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip the question type and class */
|
||||
ptr += sizeof(dns_question_t);
|
||||
|
||||
/* Parse each answer record */
|
||||
for (int i = 0; i < dns_response->num_answers; i++) {
|
||||
|
||||
/* Answer fields */
|
||||
ptr = skip_dns_name(ptr, response_size - (ptr - buffer));
|
||||
if (ptr == NULL) {
|
||||
dns_response->status_code = ERR_VAL;
|
||||
return;
|
||||
}
|
||||
|
||||
dns_answer_t *answer = (dns_answer_t *)ptr;
|
||||
uint16_t type = ntohs(answer->type);
|
||||
uint16_t class = ntohs(answer->class);
|
||||
uint32_t ttl = ntohl(answer->ttl);
|
||||
uint16_t data_len = ntohs(answer->data_len);
|
||||
|
||||
/* Skip fixed parts of answer (type, class, ttl, data_len) */
|
||||
ptr += SIZEOF_DNS_ANSWER_FIXED;
|
||||
|
||||
/* Validate RR class and ttl */
|
||||
if ((class != DNS_RRCLASS_IN) || (ttl > DNS_MAX_TTL)) {
|
||||
dns_response->answers[i].status = ERR_VAL;
|
||||
goto next_answer;
|
||||
}
|
||||
|
||||
/* Initialize status for this answer */
|
||||
dns_response->answers[i].status = ERR_OK;
|
||||
|
||||
/* Check the type of answer */
|
||||
if (type == DNS_RRTYPE_A && data_len == 4) {
|
||||
/* IPv4 Address (A record) */
|
||||
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in_addr));
|
||||
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V4);
|
||||
} else if (type == DNS_RRTYPE_AAAA && data_len == 16) {
|
||||
/* IPv6 Address (AAAA record) */
|
||||
memcpy(&dns_response->answers[i].ip, ptr, sizeof(struct in6_addr));
|
||||
IP_SET_TYPE(&dns_response->answers[i].ip, IPADDR_TYPE_V6);
|
||||
} else {
|
||||
dns_response->answers[i].status = ERR_VAL;
|
||||
}
|
||||
|
||||
next_answer:
|
||||
/* Move pointer to next answer */
|
||||
ptr += data_len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a dns_response_t to an array of IP addresses.
|
||||
*
|
||||
* This function iterates over the DNS response and extracts valid
|
||||
* IPv4 and IPv6 addresses, storing them in the provided array.
|
||||
*
|
||||
* @param response The DNS response to process
|
||||
* @param ipaddr Array to store the extracted IP addresses
|
||||
*
|
||||
* @return err_t Status of DNS response parsing
|
||||
*/
|
||||
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[])
|
||||
{
|
||||
int count = 0;
|
||||
memset(ipaddr, 0, DNS_MAX_HOST_IP * sizeof(ip_addr_t));
|
||||
|
||||
if (response->status_code != ERR_OK) {
|
||||
return response->status_code;
|
||||
}
|
||||
|
||||
/* Iterate over the DNS answers */
|
||||
for (int i = 0; i < response->num_answers && count < DNS_MAX_HOST_IP; i++) {
|
||||
const dns_answer_storage_t *answer = &response->answers[i];
|
||||
|
||||
/* Check if the answer is valid */
|
||||
if (answer->status != ERR_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ipaddr[count] = answer->ip;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/* Store the number of valid IP addresses */
|
||||
return ERR_OK;
|
||||
}
|
139
components/esp_dns/esp_dns_utils.h
Normal file
139
components/esp_dns/esp_dns_utils.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_tls.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "lwip/prot/dns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief DNS header structure
|
||||
*
|
||||
* Contains the basic fields of a DNS message header as defined in RFC 1035
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t id; /* Identification - unique identifier for the query */
|
||||
uint16_t flags; /* Flags - control bits for the DNS message */
|
||||
uint16_t qdcount; /* Number of questions in the question section */
|
||||
uint16_t ancount; /* Number of answers in the answer section */
|
||||
uint16_t nscount; /* Number of authority records in the authority section */
|
||||
uint16_t arcount; /* Number of additional records in the additional section */
|
||||
} dns_header_t;
|
||||
|
||||
/**
|
||||
* @brief DNS question structure
|
||||
*
|
||||
* Represents a single question in the question section of a DNS message
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t qtype; /* Question type (e.g., A, AAAA, MX) */
|
||||
uint16_t qclass; /* Question class (e.g., IN for internet) */
|
||||
} dns_question_t;
|
||||
|
||||
/**
|
||||
* @brief DNS answer message structure
|
||||
*
|
||||
* Represents a single resource record in the answer section of a DNS message
|
||||
* No packing needed as it's only used locally on the stack
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t type; /* Resource record type (e.g., A, AAAA, MX) */
|
||||
uint16_t class; /* Resource record class (e.g., IN for internet) */
|
||||
uint32_t ttl; /* Time-to-live in seconds */
|
||||
uint16_t data_len; /* Length of the resource data */
|
||||
} dns_answer_t;
|
||||
|
||||
#define SIZEOF_DNS_ANSWER_FIXED 10 /* Size of dns_answer_t structure in bytes */
|
||||
|
||||
/** Maximum TTL value for DNS resource records (one week) */
|
||||
#define DNS_MAX_TTL 604800
|
||||
|
||||
#ifndef CONFIG_LWIP_DNS_MAX_HOST_IP
|
||||
#define CONFIG_LWIP_DNS_MAX_HOST_IP 1
|
||||
#endif
|
||||
|
||||
/** Maximum number of answers that can be stored */
|
||||
#define MAX_ANSWERS (CONFIG_LWIP_DNS_MAX_HOST_IP)
|
||||
|
||||
#define ESP_DNS_BUFFER_SIZE 512
|
||||
|
||||
/**
|
||||
* @brief Structure to store a single DNS answer
|
||||
*/
|
||||
typedef struct {
|
||||
err_t status; /* Status of the answer */
|
||||
ip_addr_t ip; /* IP address from the answer */
|
||||
} dns_answer_storage_t;
|
||||
|
||||
/**
|
||||
* @brief Structure to store a complete DNS response
|
||||
*/
|
||||
typedef struct {
|
||||
err_t status_code; /* Overall status of the DNS response */
|
||||
uint16_t id; /* Transaction ID */
|
||||
int num_answers; /* Number of valid answers */
|
||||
dns_answer_storage_t answers[MAX_ANSWERS]; /* Array of answers */
|
||||
} dns_response_t;
|
||||
|
||||
/**
|
||||
* @brief Buffer structure for DNS response processing
|
||||
*/
|
||||
typedef struct {
|
||||
char *buffer; /* Pointer to response data buffer */
|
||||
int length; /* Current length of data in buffer */
|
||||
dns_response_t dns_response; /* Parsed DNS response information */
|
||||
} response_buffer_t;
|
||||
|
||||
/**
|
||||
* @brief Creates a DNS query for A and AAAA records
|
||||
*
|
||||
* @param buffer Buffer to store the query
|
||||
* @param buffer_size Size of the buffer
|
||||
* @param hostname Hostname to query
|
||||
* @param addrtype Address type (A or AAAA)
|
||||
* @param id_o Pointer to store the generated query ID
|
||||
*
|
||||
* @return size_t Size of the created query, or -1 on error
|
||||
*/
|
||||
size_t esp_dns_create_query(uint8_t *buffer, size_t buffer_size, const char *hostname, int addrtype, uint16_t *id_o);
|
||||
|
||||
/**
|
||||
* @brief Parses a DNS response message
|
||||
*
|
||||
* @param buffer Buffer containing the DNS response
|
||||
* @param response_size Size of the response
|
||||
* @param dns_response Structure to store parsed response
|
||||
*/
|
||||
void esp_dns_parse_response(uint8_t *buffer, size_t response_size, dns_response_t *dns_response);
|
||||
|
||||
/**
|
||||
* @brief Converts a dns_response_t to an array of IP addresses.
|
||||
*
|
||||
* This function iterates over the DNS response and extracts valid
|
||||
* IPv4 and IPv6 addresses, storing them in the provided array.
|
||||
*
|
||||
* @param response The DNS response to process.
|
||||
* @param ipaddr An array to store the extracted IP addresses.
|
||||
*
|
||||
* @return err Status of dns response parsing
|
||||
*/
|
||||
err_t esp_dns_extract_ip_addresses_from_response(const dns_response_t *response, ip_addr_t ipaddr[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
8
components/esp_dns/examples/esp_dns_basic/CMakeLists.txt
Normal file
8
components/esp_dns/examples/esp_dns_basic/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp_dns_example)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user