mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-25 16:27:29 +02:00
Compare commits
197 Commits
sock_utils
...
websocket-
Author | SHA1 | Date | |
---|---|---|---|
29f1dec408 | |||
05715d80d7 | |||
75d6845194 | |||
250eebf3fc | |||
84b61dca16 | |||
76e45f7254 | |||
462561b8d9 | |||
ae8cf218c8 | |||
c340f85a90 | |||
b95d8be41d | |||
9302994673 | |||
e5787e3d9f | |||
7cddc8c6f5 | |||
fac2edbe59 | |||
ed0f633418 | |||
a8631eecf5 | |||
3e28a7264c | |||
4ee9360f53 | |||
e9b21ea7a3 | |||
ac1b2b7573 | |||
13b90ad14b | |||
fd41006193 | |||
e71926f6ed | |||
c4169765af | |||
f43dd5012f | |||
b143894874 | |||
d1c912c833 | |||
4fbd86b278 | |||
af8272d441 | |||
8b328a6904 | |||
5f54d9075e | |||
01952f21cb | |||
5dfe076c94 | |||
ea9f29ad14 | |||
217e6d90c8 | |||
077ea0bb1f | |||
a74b01e7e3 | |||
c5a4a51d82 | |||
2ff150c310 | |||
6eceb28f7d | |||
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 | |||
19891d8c3c | |||
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 |
@ -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
|
||||
|
8
.github/workflows/clang-tidy.yml
vendored
8
.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
|
||||
@ -50,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
|
||||
|
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
|
26
.github/workflows/mdns__host-tests.yml
vendored
26
.github/workflows/mdns__host-tests.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
python -m pip install idf-build-apps dnspython pytest pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
|
||||
python -m pip install idf-build-apps==2.10.0 dnspython pytest pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
|
||||
cd $GITHUB_WORKSPACE/protocols
|
||||
# Build host tests app (with all configs and targets supported)
|
||||
python ./ci/build_apps.py components/mdns/tests/host_test/
|
||||
@ -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
|
||||
|
20
.github/workflows/modem__build-host-tests.yml
vendored
20
.github/workflows/modem__build-host-tests.yml
vendored
@ -45,7 +45,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
|
||||
test: ["target", "target_ota", "target_iperf"]
|
||||
test: ["target", "target_ota", "target_iperf", "target_urc"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
@ -91,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
|
||||
|
4
.github/workflows/modem__target-test.yml
vendored
4
.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,7 +75,7 @@ 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
|
||||
|
28
.github/workflows/modem_sim__build.yml
vendored
Normal file
28
.github/workflows/modem_sim__build.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: "modem_sim: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_modem_sim:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem_sim') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build ESP-AT with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd common_components/modem_sim
|
||||
./install.sh
|
||||
source export.sh
|
||||
idf.py build
|
127
.github/workflows/mosq__build.yml
vendored
127
.github/workflows/mosq__build.yml
vendored
@ -13,11 +13,17 @@ jobs:
|
||||
name: Mosquitto build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker", "serverless_mqtt"]
|
||||
exclude:
|
||||
- idf_ver: "release-v5.1"
|
||||
example: "serverless_mqtt" # serverless_mqtt is not supported due to esp-peer
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
TEST_DIR: components/mosquitto/examples/broker
|
||||
TEST_DIR: components/mosquitto/examples/${{ matrix.example }}
|
||||
TARGET_TEST: broker
|
||||
TARGET_TEST_DIR: build_esp32_default
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
@ -29,11 +35,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}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
zip -qur artifacts.zip ${TARGET_TEST_DIR}
|
||||
python ci/build_apps.py -c ${TEST_DIR} -m components/mosquitto/.build-test-rules.yml
|
||||
if [ "${{ matrix.example }}" == "${TARGET_TEST}" ]; then
|
||||
# upload only the target test artifacts
|
||||
cd ${TEST_DIR}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
zip -qur artifacts.zip ${TARGET_TEST_DIR}
|
||||
fi
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.example == 'broker' }}
|
||||
with:
|
||||
name: mosq_target_esp32_${{ matrix.idf_ver }}
|
||||
path: ${{ env.TEST_DIR }}/artifacts.zip
|
||||
@ -71,3 +81,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
|
||||
|
2
.github/workflows/publish-docs-component.yml
vendored
2
.github/workflows/publish-docs-component.yml
vendored
@ -102,5 +102,7 @@ jobs:
|
||||
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
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -94,3 +94,9 @@ docs/html
|
||||
|
||||
# esp-idf managed components
|
||||
**/managed_components/**
|
||||
|
||||
# modem simulator uses esp-at clone
|
||||
common_components/modem_sim/modem_sim_esp32/
|
||||
|
||||
# repository release tools
|
||||
release_notes.txt
|
||||
|
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, modem_sim"
|
||||
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|modem_sim)\)\:)'
|
||||
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
|
||||
}
|
||||
|
11
common_components/modem_sim/export.sh
Executable file
11
common_components/modem_sim/export.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $IDF_PATH/export.sh
|
||||
|
||||
export AT_CUSTOM_COMPONENTS="`pwd`/pppd_cmd"
|
||||
|
||||
cd modem_sim_esp32/esp-at
|
||||
|
||||
python -m pip install -r requirements.txt
|
||||
|
||||
python build.py reconfigure
|
64
common_components/modem_sim/install.sh
Executable file
64
common_components/modem_sim/install.sh
Executable file
@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create directory "modem_sim_esp32", go inside it
|
||||
# Usage: ./install.sh [platform] [module]
|
||||
|
||||
SCRIPT_DIR=$(pwd)
|
||||
mkdir -p modem_sim_esp32
|
||||
cd modem_sim_esp32
|
||||
|
||||
if [ -z "$IDF_PATH" ]; then
|
||||
echo "Error: IDF_PATH environment variable is not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Default ESP_AT_VERSION uses this specific commit from master to support new chips and features
|
||||
ESP_AT_VERSION="aa9d7e0e9b741744f7bf5bec3bbf887cff033d5f"
|
||||
|
||||
# Shallow clone of esp-at.git at $ESP_AT_VERSION
|
||||
if [ ! -d "esp-at" ]; then
|
||||
# cannot shallow clone from a specific commit, so we init, shallow fetch, and checkout
|
||||
mkdir -p esp-at && cd esp-at && git init && git remote add origin https://github.com/espressif/esp-at.git
|
||||
git fetch --depth 1 origin $ESP_AT_VERSION && git checkout $ESP_AT_VERSION
|
||||
else
|
||||
echo "esp-at directory already exists, skipping clone."
|
||||
cd esp-at
|
||||
fi
|
||||
|
||||
# Add esp-idf directory which is a symlink to the $IDF_PATH
|
||||
if [ ! -L "esp-idf" ]; then
|
||||
ln -sf "$IDF_PATH" esp-idf
|
||||
else
|
||||
echo "esp-idf symlink already exists, skipping."
|
||||
fi
|
||||
|
||||
# Create "build" directory
|
||||
mkdir -p build
|
||||
|
||||
# Default values for platform and module
|
||||
platform="PLATFORM_ESP32"
|
||||
module="WROOM-32"
|
||||
|
||||
# Override defaults if parameters are provided
|
||||
if [ ! -z "$1" ]; then
|
||||
platform="$1"
|
||||
fi
|
||||
if [ ! -z "$2" ]; then
|
||||
module="$2"
|
||||
fi
|
||||
|
||||
# Create file "build/module_info.json" with content
|
||||
cat > build/module_info.json << EOF
|
||||
{
|
||||
"platform": "$platform",
|
||||
"module": "$module",
|
||||
"description": "4MB, Wi-Fi + BLE, OTA, TX:17 RX:16",
|
||||
"silence": 0
|
||||
}
|
||||
EOF
|
||||
|
||||
cp "$SCRIPT_DIR/sdkconfig.defaults" "module_config/module_esp32_default/sdkconfig.defaults"
|
||||
|
||||
echo "Installation completed successfully!"
|
||||
echo "Created modem_sim_esp32 directory with esp-at repository and configuration"
|
6
common_components/modem_sim/pppd_cmd/CMakeLists.txt
Normal file
6
common_components/modem_sim/pppd_cmd/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS additional_commands.c
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES at freertos nvs_flash)
|
||||
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE)
|
411
common_components/modem_sim/pppd_cmd/additional_commands.c
Normal file
411
common_components/modem_sim/pppd_cmd/additional_commands.c
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_at.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static uint8_t at_test_cmd_test(uint8_t *cmd_name)
|
||||
{
|
||||
uint8_t buffer[64] = {0};
|
||||
snprintf((char *)buffer, 64, "test command: <AT%s=?> is executed\r\n", cmd_name);
|
||||
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
||||
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_query_cmd_test(uint8_t *cmd_name)
|
||||
{
|
||||
uint8_t buffer[64] = {0};
|
||||
snprintf((char *)buffer, 64, "query command: <AT%s?> is executed\r\n", cmd_name);
|
||||
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
||||
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_setup_cmd_test(uint8_t para_num)
|
||||
{
|
||||
uint8_t index = 0;
|
||||
printf("setup command: <AT%s=%d> is executed\r\n", esp_at_get_current_cmd_name(), para_num);
|
||||
|
||||
// get first parameter, and parse it into a digit
|
||||
int32_t digit = 0;
|
||||
if (esp_at_get_para_as_digit(index++, &digit) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
printf("digit: %d\r\n", digit);
|
||||
|
||||
// get second parameter, and parse it into a string
|
||||
uint8_t *str = NULL;
|
||||
if (esp_at_get_para_as_str(index++, &str) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
printf("string: %s\r\n", str);
|
||||
|
||||
// allocate a buffer and construct the data, then send the data to mcu via interface (uart/spi/sdio/socket)
|
||||
uint8_t *buffer = (uint8_t *)malloc(512);
|
||||
if (!buffer) {
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
int len = snprintf((char *)buffer, 512, "setup command: <AT%s=%d,\"%s\"> is executed\r\n",
|
||||
esp_at_get_current_cmd_name(), digit, str);
|
||||
esp_at_port_write_data(buffer, len);
|
||||
|
||||
// remember to free the buffer
|
||||
free(buffer);
|
||||
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
#define TAG "at_custom_cmd"
|
||||
static esp_netif_t *s_netif = NULL;
|
||||
static httpd_handle_t http_server = NULL;
|
||||
|
||||
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
esp_netif_t **netif = data;
|
||||
if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
|
||||
printf("Disconnected!");
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)data;
|
||||
esp_netif_t *netif = event->esp_netif;
|
||||
if (event_id == IP_EVENT_PPP_GOT_IP) {
|
||||
printf("Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
|
||||
esp_netif_get_ifkey(netif), IP2STR(&event->ip_info.ip));
|
||||
ESP_ERROR_CHECK(esp_netif_napt_enable(s_netif));
|
||||
|
||||
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGI(TAG, "Disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
static SemaphoreHandle_t at_sync_sema = NULL;
|
||||
static void wait_data_callback(void)
|
||||
{
|
||||
static uint8_t buffer[1500] = {0};
|
||||
int len = esp_at_port_read_data(buffer, sizeof(buffer) - 1);
|
||||
|
||||
// Check for the escape sequence "+++" in the received data
|
||||
const uint8_t escape_seq[] = "+++";
|
||||
uint8_t *escape_ptr = memmem(buffer, len, escape_seq, 3);
|
||||
|
||||
if (escape_ptr != NULL) {
|
||||
printf("Found +++ sequence, signal to the command processing thread\n");
|
||||
|
||||
int data_before_escape = escape_ptr - buffer;
|
||||
if (data_before_escape > 0) {
|
||||
esp_netif_receive(s_netif, buffer, data_before_escape, NULL);
|
||||
}
|
||||
|
||||
if (at_sync_sema) {
|
||||
xSemaphoreGive(at_sync_sema);
|
||||
}
|
||||
return;
|
||||
}
|
||||
esp_netif_receive(s_netif, buffer, len, NULL);
|
||||
}
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
printf("transmit: %d bytes\n", len);
|
||||
esp_at_port_write_data(buffer, len);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_exe_cmd_test(uint8_t *cmd_name)
|
||||
{
|
||||
uint8_t buffer[64] = {0};
|
||||
snprintf((char *)buffer, 64, "execute command: <AT%s> is executed\r\n", cmd_name);
|
||||
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
||||
printf("Command <AT%s> executed successfully\r\n", cmd_name);
|
||||
if (!at_sync_sema) {
|
||||
at_sync_sema = xSemaphoreCreateBinary();
|
||||
assert(at_sync_sema != NULL);
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = (void *)1,
|
||||
.transmit = transmit,
|
||||
};
|
||||
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
|
||||
|
||||
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
|
||||
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
|
||||
.driver = ppp_driver_cfg,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
|
||||
};
|
||||
|
||||
s_netif = esp_netif_new(&netif_ppp_config);
|
||||
esp_netif_ppp_config_t netif_params;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_get_params(s_netif, &netif_params));
|
||||
netif_params.ppp_our_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 1);
|
||||
netif_params.ppp_their_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 2);
|
||||
netif_params.ppp_error_event_enabled = true;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_set_params(s_netif, &netif_params));
|
||||
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL) != ESP_OK) {
|
||||
printf("Failed to register IP event handler");
|
||||
}
|
||||
if (esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event, NULL) != ESP_OK) {
|
||||
printf("Failed to register NETIF_PPP_STATUS event handler");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
esp_at_port_write_data((uint8_t *)"CONNECT\r\n", strlen("CONNECT\r\n"));
|
||||
|
||||
// set the callback function which will be called by AT port after receiving the input data
|
||||
esp_at_port_enter_specific(wait_data_callback);
|
||||
esp_netif_action_start(s_netif, 0, 0, 0);
|
||||
esp_netif_action_connected(s_netif, 0, 0, 0);
|
||||
|
||||
while (xSemaphoreTake(at_sync_sema, pdMS_TO_TICKS(1000)) == pdFALSE) {
|
||||
printf(".");
|
||||
}
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_test_cereg(uint8_t *cmd_name)
|
||||
{
|
||||
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_query_cereg(uint8_t *cmd_name)
|
||||
{
|
||||
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
||||
static uint8_t buffer[] = "+CEREG: 7,8\r\n";
|
||||
esp_at_port_write_data(buffer, sizeof(buffer));
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_setup_cereg(uint8_t num)
|
||||
{
|
||||
printf("%s: AT command <AT%d> is executed\r\n", __func__, num);
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_exe_cereg(uint8_t *cmd_name)
|
||||
{
|
||||
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static esp_err_t hello_get_handler(httpd_req_t *req)
|
||||
{
|
||||
const char* resp_str = "Hello from ESP-AT HTTP Server!";
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t root_get_handler(httpd_req_t *req)
|
||||
{
|
||||
const char* resp_str = "ESP-AT HTTP Server is running";
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t test_get_handler(httpd_req_t *req)
|
||||
{
|
||||
const char* resp_str = "{\"status\":\"success\",\"message\":\"Test endpoint working\",\"timestamp\":12345}";
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t async_get_handler(httpd_req_t *req)
|
||||
{
|
||||
printf("Starting async chunked response handler\r\n");
|
||||
|
||||
// Set content type for plain text response
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
|
||||
// Static counter to track requests
|
||||
static uint8_t req_count = 0;
|
||||
req_count++;
|
||||
|
||||
// Send initial response with request count
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "=== Async Response #%d ===\r\n", req_count);
|
||||
httpd_resp_sendstr_chunk(req, buffer);
|
||||
|
||||
// Long message broken into chunks
|
||||
const char* chunks[] = {
|
||||
"This is a simulated slow server response.\r\n",
|
||||
"Chunk 1: The ESP-AT HTTP server is demonstrating...\r\n",
|
||||
"Chunk 2: ...asynchronous chunked transfer encoding...\r\n",
|
||||
"Chunk 3: ...with artificial delays between chunks...\r\n",
|
||||
"Chunk 4: ...to simulate real-world network conditions.\r\n",
|
||||
"Chunk 5: Processing data... please wait...\r\n",
|
||||
"Chunk 6: Still processing... almost done...\r\n",
|
||||
"Chunk 7: Final chunk - transfer complete!\r\n",
|
||||
"=== END OF RESPONSE ===\r\n"
|
||||
};
|
||||
|
||||
int num_chunks = sizeof(chunks) / sizeof(chunks[0]);
|
||||
|
||||
// Send each chunk with delays
|
||||
for (int i = 0; i < num_chunks; i++) {
|
||||
// Add a delay to simulate slow processing
|
||||
vTaskDelay(pdMS_TO_TICKS(1500)); // 1.5 second delay between chunks
|
||||
|
||||
// Add chunk number and timestamp
|
||||
snprintf(buffer, sizeof(buffer), "[%d/%d] [%d ms] %s",
|
||||
i + 1, num_chunks, (int)(esp_timer_get_time() / 1000), chunks[i]);
|
||||
|
||||
printf("Sending chunk %d: %s", i + 1, chunks[i]);
|
||||
httpd_resp_sendstr_chunk(req, buffer);
|
||||
}
|
||||
|
||||
// Add final summary
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
snprintf(buffer, sizeof(buffer), "\r\nTransfer completed in %d chunks with delays.\r\n", num_chunks);
|
||||
httpd_resp_sendstr_chunk(req, buffer);
|
||||
|
||||
// Send NULL to signal end of chunked transfer
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
|
||||
printf("Async chunked response completed\r\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const httpd_uri_t hello = {
|
||||
.uri = "/hello",
|
||||
.method = HTTP_GET,
|
||||
.handler = hello_get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
static const httpd_uri_t root = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = root_get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
static const httpd_uri_t test = {
|
||||
.uri = "/test",
|
||||
.method = HTTP_GET,
|
||||
.handler = test_get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
static const httpd_uri_t async_uri = {
|
||||
.uri = "/async",
|
||||
.method = HTTP_GET,
|
||||
.handler = async_get_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
static esp_err_t start_http_server(void)
|
||||
{
|
||||
if (http_server != NULL) {
|
||||
printf("HTTP server already running\r\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.server_port = 8080;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
printf("Starting HTTP server on port: %d\r\n", config.server_port);
|
||||
if (httpd_start(&http_server, &config) == ESP_OK) {
|
||||
printf("Registering URI handlers\r\n");
|
||||
httpd_register_uri_handler(http_server, &hello);
|
||||
httpd_register_uri_handler(http_server, &root);
|
||||
httpd_register_uri_handler(http_server, &test);
|
||||
httpd_register_uri_handler(http_server, &async_uri);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
printf("Error starting HTTP server!\r\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t stop_http_server(void)
|
||||
{
|
||||
if (http_server != NULL) {
|
||||
httpd_stop(http_server);
|
||||
http_server = NULL;
|
||||
printf("HTTP server stopped\r\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* HTTP Server AT Commands */
|
||||
static uint8_t at_test_httpd(uint8_t *cmd_name)
|
||||
{
|
||||
uint8_t buffer[64] = {0};
|
||||
snprintf((char *)buffer, 64, "AT%s=<0/1> - Start/Stop HTTP server\r\n", cmd_name);
|
||||
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_query_httpd(uint8_t *cmd_name)
|
||||
{
|
||||
uint8_t buffer[64] = {0};
|
||||
snprintf((char *)buffer, 64, "+HTTPD:%d\r\n", http_server != NULL ? 1 : 0);
|
||||
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
|
||||
static uint8_t at_setup_httpd(uint8_t para_num)
|
||||
{
|
||||
int32_t action = 0;
|
||||
if (esp_at_get_para_as_digit(0, &action) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
|
||||
if (action == 1) {
|
||||
if (start_http_server() == ESP_OK) {
|
||||
printf("HTTP server started successfully\r\n");
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
} else if (action == 0) {
|
||||
if (stop_http_server() == ESP_OK) {
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
|
||||
static uint8_t at_exe_httpd(uint8_t *cmd_name)
|
||||
{
|
||||
// Default action: start server
|
||||
if (start_http_server() == ESP_OK) {
|
||||
printf("HTTP server started via execute command\r\n");
|
||||
return ESP_AT_RESULT_CODE_OK;
|
||||
}
|
||||
return ESP_AT_RESULT_CODE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static const esp_at_cmd_struct at_custom_cmd[] = {
|
||||
{"+PPPD", at_test_cmd_test, at_query_cmd_test, at_setup_cmd_test, at_exe_cmd_test},
|
||||
{"+CEREG", at_test_cereg, at_query_cereg, at_setup_cereg, at_exe_cereg},
|
||||
{"+HTTPD", at_test_httpd, at_query_httpd, at_setup_httpd, at_exe_httpd},
|
||||
};
|
||||
|
||||
bool esp_at_custom_cmd_register(void)
|
||||
{
|
||||
return esp_at_custom_cmd_array_regist(at_custom_cmd, sizeof(at_custom_cmd) / sizeof(esp_at_cmd_struct));
|
||||
}
|
||||
|
||||
ESP_AT_CMD_SET_INIT_FN(esp_at_custom_cmd_register, 1);
|
12
common_components/modem_sim/pppd_cmd/include/at_custom_cmd.h
Normal file
12
common_components/modem_sim/pppd_cmd/include/at_custom_cmd.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_at_core.h"
|
||||
#include "esp_at.h"
|
||||
|
||||
/**
|
||||
* @brief You can include more header files here for your own AT commands.
|
||||
*/
|
77
common_components/modem_sim/sdkconfig.defaults
Normal file
77
common_components/modem_sim/sdkconfig.defaults
Normal file
@ -0,0 +1,77 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||
CONFIG_APP_PROJECT_VER_FROM_CONFIG=y
|
||||
CONFIG_APP_PROJECT_VER="v4.1.0.0-dev"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="module_config/module_esp32_default/partitions_at.csv"
|
||||
CONFIG_PARTITION_TABLE_MD5=n
|
||||
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_FILE="module_config/module_esp32_default/at_customize.csv"
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BTU_TASK_STACK_SIZE=5120
|
||||
CONFIG_BT_BLE_BLUFI_ENABLE=y
|
||||
CONFIG_BT_STACK_NO_LOG=y
|
||||
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
CONFIG_BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL=y
|
||||
CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE=200
|
||||
CONFIG_ESP_TLS_PSK_VERIFICATION=y
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=n
|
||||
CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL=y
|
||||
CONFIG_ETH_DMA_RX_BUFFER_NUM=3
|
||||
CONFIG_ETH_DMA_TX_BUFFER_NUM=3
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
|
||||
CONFIG_HTTPD_MAX_URI_LEN=1024
|
||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
|
||||
CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT=y
|
||||
CONFIG_RTC_CLK_CAL_CYCLES=1024
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_SLP_DISABLE_GPIO=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80=y
|
||||
CONFIG_ESP_TASK_WDT_PANIC=y
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
|
||||
CONFIG_ESP_DEBUG_OCDAWARE=n
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=n
|
||||
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
|
||||
CONFIG_ESP_WIFI_SLP_IRAM_OPT=y
|
||||
CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y
|
||||
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0
|
||||
CONFIG_FATFS_LFN_HEAP=y
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=n
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
|
||||
CONFIG_LWIP_MAX_SOCKETS=16
|
||||
CONFIG_LWIP_SO_LINGER=y
|
||||
CONFIG_LWIP_SO_RCVBUF=y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=y
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=y
|
||||
CONFIG_LWIP_IPV6_AUTOCONFIG=y
|
||||
CONFIG_LWIP_TCP_MAXRTX=6
|
||||
CONFIG_LWIP_TCP_SYNMAXRTX=3
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_SNTP_MAX_SERVERS=3
|
||||
CONFIG_LWIP_SNTP_STARTUP_DELAY=n
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
|
||||
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n
|
||||
CONFIG_MBEDTLS_HAVE_TIME_DATE=y
|
||||
CONFIG_MBEDTLS_DHM_C=y
|
||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
||||
CONFIG_VFS_SUPPORT_TERMIOS=n
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
CONFIG_AT_PROCESS_TASK_STACK_SIZE=6144
|
||||
CONFIG_AT_MQTT_COMMAND_SUPPORT=y
|
||||
CONFIG_AT_HTTP_COMMAND_SUPPORT=y
|
||||
CONFIG_AT_BLE_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLE_HID_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLUFI_COMMAND_SUPPORT=n
|
@ -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);
|
||||
}
|
||||
}
|
@ -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: 1.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
@ -1,5 +1,24 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for custom channels ([4ee9360f](https://github.com/espressif/esp-protocols/commit/4ee9360f))
|
||||
|
||||
## [0.3.1](https://github.com/espressif/esp-protocols/commits/eppp-v0.3.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix NETIF_PPP_STATUS link issue if PPP disabled in lwip ([077ea0bb](https://github.com/espressif/esp-protocols/commit/077ea0bb))
|
||||
|
||||
## [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,31 @@ 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
|
||||
|
||||
config EPPP_LINK_CHANNELS_SUPPORT
|
||||
bool "Enable channel support (multiple logical channels)"
|
||||
default n
|
||||
depends on !EPPP_LINK_DEVICE_ETH
|
||||
help
|
||||
Enable support for multiple logical channels in the EPPP link layer.
|
||||
When enabled, you can configure the number of channels used for communication.
|
||||
|
||||
config EPPP_LINK_NR_OF_CHANNELS
|
||||
int "Number of logical channels"
|
||||
depends on EPPP_LINK_CHANNELS_SUPPORT && !EPPP_LINK_DEVICE_ETH
|
||||
range 1 8
|
||||
default 2
|
||||
help
|
||||
Set the number of logical channels for EPPP link communication.
|
||||
Each channel can be used for independent data streams.
|
||||
|
||||
endmenu
|
||||
|
@ -1,24 +1,74 @@
|
||||
# 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 RPC 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)| | |
|
||||
+----------------+ +----------------+
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Network Interface Modes
|
||||
|
||||
Standard PPP Mode (where PPP protocols is preferred) or simple tunnel using TUN Mode.
|
||||
|
||||
### Transport layer
|
||||
|
||||
UART, SPI, SDIO, Ethernet
|
||||
|
||||
### Support for logical channels
|
||||
|
||||
Allows channeling custom data (e.g. 802.11 frames)
|
||||
|
||||
## (Other) usecases
|
||||
|
||||
Besides the communication coprocessor example mentioned above, this component could be used to:
|
||||
* Bring Wi-Fi connectivity to a computer using ESP32 chip.
|
||||
* Connect your microcontroller to the internet via a pppd server (running on a raspberry)
|
||||
* Bridging two networks with two microcontrollers
|
||||
|
||||
## 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
|
||||
|
||||
### Channel support (multiple logical channels)
|
||||
|
||||
* `CONFIG_EPPP_LINK_CHANNELS_SUPPORT` -- Enable support for multiple logical channels (default: disabled)
|
||||
* `CONFIG_EPPP_LINK_NR_OF_CHANNELS` -- Number of logical channels (default: 2, range: 1-8, only visible if channel support is enabled)
|
||||
|
||||
When channel support is enabled, the EPPP link can multiplex multiple logical data streams over the same transport. The number of channels is configurable. Channel support is not available for Ethernet transport.
|
||||
|
||||
To use channels in your application, use the `eppp_add_channels()` API and provide your own channel transmit/receive callbacks. These APIs and related types are only available when channel support is enabled in Kconfig.
|
||||
|
||||
## API
|
||||
|
||||
### Client
|
||||
@ -36,6 +86,9 @@ brings in the WiFi connectivity from the "SLAVE" microcontroller.
|
||||
* `eppp_netif_start()` -- Starts the network, could be called after startup or whenever a connection is lost
|
||||
* `eppp_netif_stop()` -- Stops the network
|
||||
* `eppp_perform()` -- Perform one iteration of the PPP task (need to be called regularly in task-less configuration)
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
* `eppp_add_channels()` -- Register channel transmit/receive callbacks (only available if channel support is enabled)
|
||||
#endif
|
||||
|
||||
## Throughput
|
||||
|
||||
@ -55,3 +108,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
|
||||
|
370
components/eppp_link/detailed_description.md
Normal file
370
components/eppp_link/detailed_description.md
Normal file
@ -0,0 +1,370 @@
|
||||
# ESP PPP Link Component (eppp_link) - Detailed Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The ESP PPP Link component provides a versatile communication bridge between two ESP32 microcontrollers, enabling network connectivity over various physical transport layers. One device acts as a server (typically providing connectivity), while the other acts as a client (consuming connectivity).
|
||||
|
||||
## Network Interface Modes
|
||||
|
||||
### PPP Mode vs TUN Mode
|
||||
|
||||
The component supports two distinct network interface modes:
|
||||
|
||||
#### PPP Mode (`CONFIG_EPPP_LINK_USES_PPP=y`)
|
||||
- **Standard PPP Protocol**: Uses the Point-to-Point Protocol (RFC 1661) with full LCP negotiation
|
||||
- **Compatibility**: Compatible with standard PPP implementations like Linux `pppd`
|
||||
- **Features**:
|
||||
- Automatic IP address negotiation
|
||||
- Link Control Protocol (LCP) for connection establishment
|
||||
- Authentication support (if configured)
|
||||
- Standard PPP framing with escape sequences
|
||||
- **Use Case**: When interfacing with standard PPP-capable systems or when full PPP compatibility is required
|
||||
- **Transport Limitation**: Primarily designed for UART transport due to PPP's serial nature
|
||||
|
||||
#### TUN Mode (`CONFIG_EPPP_LINK_USES_PPP=n`, default)
|
||||
- **Simplified Packet Interface**: Uses a custom packet-based protocol without PPP negotiation
|
||||
- **Performance**: Faster data transfer due to reduced protocol overhead
|
||||
- **Features**:
|
||||
- Direct IP packet transmission
|
||||
- Custom framing for packet boundaries
|
||||
- No negotiation overhead
|
||||
- Static IP address configuration
|
||||
- **Use Case**: Default mode for ESP-to-ESP communication, optimal for non-UART transports
|
||||
- **Transport Support**: Works efficiently with all transport types (UART, SPI, SDIO, Ethernet)
|
||||
|
||||
**Mode Selection**: Configure via `idf.py menuconfig` → `Component config` → `eppp_link` → `Use PPP network interface`
|
||||
|
||||
## Transport Layer Options
|
||||
|
||||
### UART Transport
|
||||
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_UART=y`
|
||||
- **Features**:
|
||||
- Simple serial communication
|
||||
- Configurable baud rate (up to 3Mbps tested)
|
||||
- Hardware flow control support
|
||||
- Custom framing for packet boundaries in TUN mode
|
||||
- **Performance**: ~2 Mbps (TCP/UDP) @ 3 Mbaud
|
||||
- **Use Case**: Basic connectivity, long-distance communication, debugging
|
||||
- **Pins**: TX, RX configurable
|
||||
|
||||
```c
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = 25;
|
||||
config.uart.rx_io = 26;
|
||||
config.uart.baud = 921600;
|
||||
```
|
||||
|
||||
### SPI Transport
|
||||
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SPI=y`
|
||||
- **Features**:
|
||||
- Master/slave configuration
|
||||
- GPIO interrupt signaling for flow control
|
||||
- Configurable clock frequency
|
||||
- Full-duplex communication
|
||||
- Packet queue for transmission
|
||||
- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) @ 16MHz
|
||||
- **Use Case**: High-speed local communication, PCB-level connections
|
||||
- **Pins**: MOSI, MISO, SCLK, CS, interrupt GPIO
|
||||
|
||||
```c
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_SPI;
|
||||
config.spi.is_master = true;
|
||||
config.spi.freq = 16000000;
|
||||
config.spi.mosi = 11;
|
||||
config.spi.miso = 13;
|
||||
config.spi.sclk = 12;
|
||||
config.spi.cs = 10;
|
||||
config.spi.intr = 2;
|
||||
```
|
||||
|
||||
### SDIO Transport
|
||||
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_SDIO=y`
|
||||
- **Features**:
|
||||
- Host/slave configuration
|
||||
- High-speed data transfer
|
||||
- 1-bit or 4-bit bus width
|
||||
- Hardware flow control
|
||||
- **Performance**: ~9 Mbps (TCP), ~11 Mbps (UDP)
|
||||
- **Use Case**: Highest throughput applications, module-to-module communication
|
||||
- **Pins**: CLK, CMD, D0-D3 (configurable width)
|
||||
|
||||
```c
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_SDIO;
|
||||
config.sdio.is_host = true;
|
||||
config.sdio.width = 4;
|
||||
config.sdio.clk = 18;
|
||||
config.sdio.cmd = 19;
|
||||
config.sdio.d0 = 14;
|
||||
// ... additional data pins
|
||||
```
|
||||
|
||||
### Ethernet Transport
|
||||
- **Configuration**: `CONFIG_EPPP_LINK_DEVICE_ETH=y`
|
||||
- **Features**:
|
||||
- Direct MAC-to-MAC communication
|
||||
- Can work with or without PHY chips
|
||||
- Standard Ethernet framing
|
||||
- Automatic task management (no `eppp_perform()` needed)
|
||||
- **Performance**: ~5 Mbps (TCP), ~8 Mbps (UDP) with internal EMAC
|
||||
- **Use Case**: Board-to-board communication, integration with existing Ethernet infrastructure
|
||||
- **Pins**: MDC, MDIO, plus PHY-specific pins
|
||||
|
||||
```c
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_ETHERNET;
|
||||
config.ethernet.mdc_io = 23;
|
||||
config.ethernet.mdio_io = 18;
|
||||
config.ethernet.phy_addr = 1;
|
||||
config.ethernet.rst_io = 5;
|
||||
```
|
||||
|
||||
## Channel Support (Multiple Logical Channels)
|
||||
|
||||
### Overview
|
||||
Channel support allows multiplexing multiple independent data streams over a single transport connection. This enables applications to separate different types of data (e.g., network traffic, control commands, sensor data) into distinct logical channels.
|
||||
|
||||
### Configuration
|
||||
- **Enable**: `CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y`
|
||||
- **Count**: `CONFIG_EPPP_LINK_NR_OF_CHANNELS` (1-8 channels, default 2)
|
||||
- **Limitation**: Not available for Ethernet transport
|
||||
|
||||
### Channel Usage
|
||||
```c
|
||||
// Channel callback function type
|
||||
typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len);
|
||||
|
||||
// Register channel callbacks
|
||||
esp_err_t eppp_add_channels(esp_netif_t *netif,
|
||||
eppp_channel_fn_t *tx, // Transmit function pointer (output)
|
||||
const eppp_channel_fn_t rx, // Receive callback (input)
|
||||
void* context); // User context
|
||||
|
||||
// Get user context
|
||||
void* eppp_get_context(esp_netif_t *netif);
|
||||
```
|
||||
|
||||
### Channel Example
|
||||
```c
|
||||
// Channel 0: Default network traffic (handled automatically)
|
||||
// Channel 1: Control/chat messages
|
||||
// Channel 2: WiFi data forwarding
|
||||
|
||||
static esp_err_t channel_receive(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
switch(channel) {
|
||||
case 1: // Control channel
|
||||
process_control_message(buffer, len);
|
||||
break;
|
||||
case 2: // WiFi channel
|
||||
forward_to_wifi(buffer, len);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unknown channel %d", channel);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Register channels
|
||||
eppp_channel_fn_t tx_func;
|
||||
eppp_add_channels(netif, &tx_func, channel_receive, user_context);
|
||||
|
||||
// Transmit on specific channel
|
||||
tx_func(netif, 1, "Hello", 5); // Send to channel 1
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Simple API (Recommended for most use cases)
|
||||
|
||||
#### Client Side
|
||||
```c
|
||||
esp_netif_t *eppp_connect(eppp_config_t *config);
|
||||
```
|
||||
- **Purpose**: Simplified client connection
|
||||
- **Behavior**: Blocks until connection is established
|
||||
- **Returns**: Configured network interface or NULL on failure
|
||||
- **Use Case**: Simple applications that don't need fine-grained control
|
||||
|
||||
#### Server Side
|
||||
```c
|
||||
esp_netif_t *eppp_listen(eppp_config_t *config);
|
||||
```
|
||||
- **Purpose**: Simplified server listening
|
||||
- **Behavior**: Blocks until client connects
|
||||
- **Returns**: Configured network interface or NULL on failure
|
||||
- **Use Case**: Simple applications that don't need fine-grained control
|
||||
|
||||
#### Connection Management
|
||||
```c
|
||||
void eppp_close(esp_netif_t *netif);
|
||||
```
|
||||
- **Purpose**: Close connection and cleanup resources
|
||||
- **Behavior**: Stops tasks, closes transport, destroys network interface
|
||||
|
||||
### Advanced API (Manual Control)
|
||||
|
||||
#### Initialization
|
||||
```c
|
||||
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config);
|
||||
```
|
||||
- **Purpose**: Initialize endpoint without starting communication
|
||||
- **Parameters**:
|
||||
- `role`: `EPPP_SERVER` or `EPPP_CLIENT`
|
||||
- `config`: Transport and network configuration
|
||||
- **Returns**: Network interface handle
|
||||
- **Use Case**: Applications needing manual control over connection lifecycle
|
||||
|
||||
#### Connection Control
|
||||
```c
|
||||
esp_err_t eppp_netif_start(esp_netif_t *netif);
|
||||
esp_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms);
|
||||
```
|
||||
- **Purpose**: Manual network interface start/stop
|
||||
- **Use Case**: Dynamic connection management, error recovery
|
||||
|
||||
#### Task Management
|
||||
```c
|
||||
esp_err_t eppp_perform(esp_netif_t *netif);
|
||||
```
|
||||
- **Purpose**: Single iteration of communication task
|
||||
- **Returns**:
|
||||
- `ESP_OK`: Continue operation
|
||||
- `ESP_FAIL`: Operation failed but should continue
|
||||
- `ESP_ERR_TIMEOUT`: Stop operation requested
|
||||
- **Use Case**: Task-less operation, integration with custom task schedulers
|
||||
- **Note**: Not needed for Ethernet transport (has its own task)
|
||||
|
||||
#### Resource Management
|
||||
```c
|
||||
void eppp_deinit(esp_netif_t *netif);
|
||||
```
|
||||
- **Purpose**: Clean up resources without stopping tasks
|
||||
- **Use Case**: Manual resource management
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Basic Client-Server Setup
|
||||
|
||||
**Client (Host) Configuration:**
|
||||
```c
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = 25;
|
||||
config.uart.rx_io = 26;
|
||||
config.uart.baud = 921600;
|
||||
|
||||
esp_netif_t *netif = eppp_connect(&config);
|
||||
if (netif == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to connect");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Connected successfully");
|
||||
// Use network interface for communication
|
||||
}
|
||||
```
|
||||
|
||||
**Server (Slave) Configuration:**
|
||||
```c
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
// Initialize WiFi or other network interface
|
||||
init_wifi();
|
||||
|
||||
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
config.transport = EPPP_TRANSPORT_UART;
|
||||
config.uart.tx_io = 26; // Crossed with client
|
||||
config.uart.rx_io = 25; // Crossed with client
|
||||
config.uart.baud = 921600;
|
||||
|
||||
esp_netif_t *netif = eppp_listen(&config);
|
||||
if (netif == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to setup server");
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable NAT to share WiFi connection
|
||||
ESP_ERROR_CHECK(esp_netif_napt_enable(netif));
|
||||
ESP_LOGI(TAG, "Server ready");
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Manual Control
|
||||
```c
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.task.run_task = false; // Disable automatic task
|
||||
|
||||
esp_netif_t *netif = eppp_init(EPPP_CLIENT, &config);
|
||||
if (netif == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start network interface
|
||||
ESP_ERROR_CHECK(eppp_netif_start(netif));
|
||||
|
||||
// Custom task loop
|
||||
while (true) {
|
||||
esp_err_t ret = eppp_perform(netif);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGI(TAG, "Operation stopped");
|
||||
break;
|
||||
} else if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Operation failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
// Add custom processing here
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
eppp_deinit(netif);
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Channel Configuration
|
||||
```c
|
||||
typedef struct {
|
||||
eppp_channel_fn_t tx_func;
|
||||
esp_netif_t *netif;
|
||||
} channel_context_t;
|
||||
|
||||
static esp_err_t channel_rx(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
ESP_LOGI(TAG, "Channel %d received %d bytes", channel, len);
|
||||
// Process channel data based on channel number
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void setup_channels(void)
|
||||
{
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
esp_netif_t *netif = eppp_connect(&config);
|
||||
|
||||
channel_context_t *ctx = calloc(1, sizeof(channel_context_t));
|
||||
ctx->netif = netif;
|
||||
|
||||
// Register channel callbacks
|
||||
ESP_ERROR_CHECK(eppp_add_channels(netif, &ctx->tx_func, channel_rx, ctx));
|
||||
|
||||
// Send data on channel 1
|
||||
const char *msg = "Hello on channel 1";
|
||||
ctx->tx_func(netif, 1, (void*)msg, strlen(msg));
|
||||
}
|
||||
```
|
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)
|
||||
@ -305,16 +163,17 @@ static int get_netif_num(esp_netif_t *netif)
|
||||
return netif_cnt;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
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);
|
||||
h->netif_stop = true;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_EPPP_LINK_USES_PPP
|
||||
|
||||
static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
@ -341,356 +200,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 +209,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,11 +220,13 @@ 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);
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
esp_event_handler_unregister(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -722,67 +235,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)
|
||||
@ -821,11 +300,13 @@ esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_time
|
||||
remove_handlers();
|
||||
return NULL;
|
||||
}
|
||||
#ifdef CONFIG_EPPP_LINK_USES_PPP
|
||||
if (esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register PPP status handler");
|
||||
remove_handlers();
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_EPPP_LINK_USES_PPP
|
||||
}
|
||||
esp_netif_t *netif = eppp_init(role, config);
|
||||
if (!netif) {
|
||||
@ -834,12 +315,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);
|
||||
@ -885,3 +367,24 @@ void eppp_close(esp_netif_t *netif)
|
||||
eppp_deinit(netif);
|
||||
remove_handlers();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(netif != NULL && tx != NULL && rx != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_FALSE(h != NULL && h->channel_tx != NULL, ESP_ERR_INVALID_STATE, TAG, "Transport not initialized");
|
||||
*tx = h->channel_tx;
|
||||
h->channel_rx = rx;
|
||||
h->context = context;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void* eppp_get_context(esp_netif_t *netif)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(netif != NULL, NULL, TAG, "Invalid netif");
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_FALSE(h != NULL, NULL, TAG, "EPPP Not initialized");
|
||||
return h->context;
|
||||
}
|
||||
#endif
|
||||
|
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;
|
||||
}
|
92
components/eppp_link/eppp_sdio.c
Normal file
92
components/eppp_link/eppp_sdio.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "eppp_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");
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
h->parent.channel_tx = eppp_sdio_transmit_channel;
|
||||
#endif
|
||||
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
|
||||
*/
|
||||
@ -8,8 +8,10 @@
|
||||
#define MAX_SDIO_PAYLOAD 1500
|
||||
#define SDIO_ALIGN(size) (((size) + 3U) & ~(3U))
|
||||
#define SDIO_PAYLOAD SDIO_ALIGN(MAX_SDIO_PAYLOAD)
|
||||
#define SDIO_PACKET_SIZE SDIO_ALIGN(MAX_SDIO_PAYLOAD + 4)
|
||||
#define PPP_SOF 0x7E
|
||||
|
||||
|
||||
// Interrupts and registers
|
||||
#define SLAVE_INTR 0
|
||||
#define SLAVE_REG_REQ 0
|
||||
@ -17,3 +19,11 @@
|
||||
// Requests from host to slave
|
||||
#define REQ_RESET 1
|
||||
#define REQ_INIT 2
|
||||
|
||||
struct header {
|
||||
uint8_t magic;
|
||||
uint8_t channel;
|
||||
uint16_t size;
|
||||
} __attribute__((packed));
|
||||
|
||||
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len);
|
||||
|
@ -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
|
||||
*/
|
||||
@ -15,6 +15,7 @@
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "esp_check.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO_HOST
|
||||
|
||||
@ -28,22 +29,23 @@ static SemaphoreHandle_t s_essl_mutex = NULL;
|
||||
static essl_handle_t s_essl = NULL;
|
||||
static sdmmc_card_t *s_card = NULL;
|
||||
|
||||
static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PAYLOAD];
|
||||
static DMA_ATTR uint8_t rcv_buffer[SDIO_PAYLOAD];
|
||||
static DRAM_DMA_ALIGNED_ATTR uint8_t send_buffer[SDIO_PACKET_SIZE];
|
||||
static DMA_ATTR uint8_t rcv_buffer[SDIO_PACKET_SIZE];
|
||||
|
||||
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
|
||||
static esp_err_t eppp_sdio_host_tx_generic(int channel, void *buffer, size_t len)
|
||||
{
|
||||
if (s_essl == NULL || s_essl_mutex == NULL) {
|
||||
// silently skip the Tx if the SDIO not fully initialized
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
memcpy(send_buffer, buffer, len);
|
||||
size_t send_len = SDIO_ALIGN(len);
|
||||
if (send_len > len) {
|
||||
// pad with SOF's
|
||||
memset(&send_buffer[len], PPP_SOF, send_len - len);
|
||||
}
|
||||
|
||||
struct header *head = (void *)send_buffer;
|
||||
head->magic = PPP_SOF;
|
||||
head->channel = channel;
|
||||
head->size = len;
|
||||
memcpy(send_buffer + sizeof(struct header), buffer, len);
|
||||
size_t send_len = SDIO_ALIGN(len + sizeof(struct header));
|
||||
xSemaphoreTake(s_essl_mutex, portMAX_DELAY);
|
||||
esp_err_t ret = essl_send_packet(s_essl, send_buffer, send_len, PACKET_TIMEOUT_MS);
|
||||
if (ret != ESP_OK) {
|
||||
@ -56,6 +58,19 @@ esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len)
|
||||
{
|
||||
return eppp_sdio_host_tx_generic(0, buffer, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
return eppp_sdio_host_tx_generic(channel, buffer, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static esp_err_t request_slave_reset(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -145,15 +160,37 @@ esp_err_t eppp_sdio_host_rx(esp_netif_t *netif)
|
||||
if (intr & ESSL_SDIO_DEF_ESP32.new_packet_intr_mask) {
|
||||
esp_err_t ret;
|
||||
do {
|
||||
size_t size_read = SDIO_PAYLOAD;
|
||||
ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PAYLOAD, &size_read, PACKET_TIMEOUT_MS);
|
||||
size_t size_read = SDIO_PACKET_SIZE;
|
||||
ret = essl_get_packet(s_essl, rcv_buffer, SDIO_PACKET_SIZE, &size_read, PACKET_TIMEOUT_MS);
|
||||
if (ret == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "interrupt but no data can be read");
|
||||
break;
|
||||
} else if (ret == ESP_OK) {
|
||||
ESP_LOGD(TAG, "receive data, size: %d", size_read);
|
||||
struct header *head = (void *)rcv_buffer;
|
||||
if (head->magic != PPP_SOF) {
|
||||
ESP_LOGE(TAG, "invalid magic %x", head->magic);
|
||||
break;
|
||||
}
|
||||
if (head->channel > NR_OF_CHANNELS) {
|
||||
ESP_LOGE(TAG, "invalid channel %x", head->channel);
|
||||
break;
|
||||
}
|
||||
if (head->size > SDIO_PAYLOAD || head->size > size_read) {
|
||||
ESP_LOGE(TAG, "invalid size %x", head->size);
|
||||
break;
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, rcv_buffer, size_read, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, rcv_buffer, size_read, NULL);
|
||||
if (head->channel == 0) {
|
||||
esp_netif_receive(netif, rcv_buffer + sizeof(struct header), head->size, NULL);
|
||||
} else {
|
||||
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->channel_rx) {
|
||||
h->channel_rx(netif, head->channel, rcv_buffer + sizeof(struct header), head->size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "rx packet error: %08X", ret);
|
||||
|
@ -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,9 +9,10 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
#include "eppp_sdio.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO_SLAVE
|
||||
#define BUFFER_NUM 4
|
||||
#define BUFFER_SIZE SDIO_PAYLOAD
|
||||
@ -20,19 +21,18 @@ static DMA_ATTR uint8_t sdio_slave_rx_buffer[BUFFER_NUM][BUFFER_SIZE];
|
||||
static DMA_ATTR uint8_t sdio_slave_tx_buffer[SDIO_PAYLOAD];
|
||||
static int s_slave_request = 0;
|
||||
|
||||
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
|
||||
static esp_err_t eppp_sdio_host_tx_generic(int channel, void *buffer, size_t len)
|
||||
{
|
||||
if (s_slave_request != REQ_INIT) {
|
||||
// silently skip the Tx if the SDIO not fully initialized
|
||||
return ESP_OK;
|
||||
}
|
||||
memcpy(sdio_slave_tx_buffer, buffer, len);
|
||||
size_t send_len = SDIO_ALIGN(len);
|
||||
if (send_len > len) {
|
||||
// pad with SOF's if the size is not 4 bytes aligned
|
||||
memset(&sdio_slave_tx_buffer[len], PPP_SOF, send_len - len);
|
||||
}
|
||||
|
||||
struct header *head = (void *)sdio_slave_tx_buffer;
|
||||
head->magic = PPP_SOF;
|
||||
head->channel = channel;
|
||||
head->size = len;
|
||||
memcpy(sdio_slave_tx_buffer + sizeof(struct header), buffer, len);
|
||||
size_t send_len = SDIO_ALIGN(len + sizeof(struct header));
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, sdio_slave_tx_buffer, send_len, ESP_LOG_VERBOSE);
|
||||
esp_err_t ret = sdio_slave_transmit(sdio_slave_tx_buffer, send_len);
|
||||
if (ret != ESP_OK) {
|
||||
@ -43,6 +43,18 @@ esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
|
||||
{
|
||||
return eppp_sdio_host_tx_generic(0, buffer, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
esp_err_t eppp_sdio_transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
return eppp_sdio_host_tx_generic(channel, buffer, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t slave_reset(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -81,7 +93,29 @@ esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif)
|
||||
if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) {
|
||||
again:
|
||||
ptr = sdio_slave_recv_get_buf(handle, &length);
|
||||
esp_netif_receive(netif, ptr, length, NULL);
|
||||
struct header *head = (void *)ptr;
|
||||
if (head->magic != PPP_SOF) {
|
||||
ESP_LOGE(TAG, "invalid magic %x", head->magic);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->channel > NR_OF_CHANNELS) {
|
||||
ESP_LOGE(TAG, "invalid channel %x", head->channel);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->size > SDIO_PAYLOAD || head->size > length) {
|
||||
ESP_LOGE(TAG, "invalid size %x", head->size);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->channel == 0) {
|
||||
esp_netif_receive(netif, ptr + sizeof(struct header), head->size, NULL);
|
||||
} else {
|
||||
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->channel_rx) {
|
||||
h->channel_rx(netif, head->channel, ptr + sizeof(struct header), head->size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (sdio_slave_recv_load_buf(handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to recycle packet buffer");
|
||||
return ESP_FAIL;
|
||||
@ -109,7 +143,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 +200,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;
|
||||
}
|
||||
|
496
components/eppp_link/eppp_spi.c
Normal file
496
components/eppp_link/eppp_spi.c
Normal file
@ -0,0 +1,496 @@
|
||||
/*
|
||||
* 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 "sdkconfig.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 PPP_SOF 0x7E
|
||||
#define SPI_HEADER_MAGIC PPP_SOF
|
||||
#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;
|
||||
int channel;
|
||||
};
|
||||
|
||||
struct header {
|
||||
uint8_t magic;
|
||||
uint8_t channel;
|
||||
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_generic(struct eppp_spi *handle, int channel, void *buffer, size_t len)
|
||||
{
|
||||
|
||||
struct packet buf = { .channel = channel };
|
||||
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);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = h;
|
||||
struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);;
|
||||
return transmit_generic(spi_handle, 0, buffer, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_spi *spi_handle = __containerof(handle, struct eppp_spi, parent);;
|
||||
return transmit_generic(spi_handle, channel, buffer, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
head->channel = h->outbound.channel;
|
||||
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;
|
||||
head->channel = 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 || head->channel > NR_OF_CHANNELS) {
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
if (allow_test_tx) {
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Wrong checksum, magic, or channel: %x %x %x", check, head->magic, head->channel);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
if (head->channel == 0) {
|
||||
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
|
||||
} else {
|
||||
#if defined(CONFIG_EPPP_LINK_CHANNELS_SUPPORT)
|
||||
if (h->parent.channel_rx) {
|
||||
h->parent.channel_rx(netif, head->channel, in_buf + sizeof(struct header), head->size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
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");
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
h->parent.channel_tx = transmit_channel;
|
||||
#endif
|
||||
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);
|
||||
}
|
29
components/eppp_link/eppp_transport.h
Normal file
29
components/eppp_link/eppp_transport.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_netif_types.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
#define NR_OF_CHANNELS CONFIG_EPPP_LINK_NR_OF_CHANNELS
|
||||
#else
|
||||
#define NR_OF_CHANNELS 1
|
||||
#endif
|
||||
|
||||
struct eppp_handle {
|
||||
esp_netif_driver_base_t base;
|
||||
eppp_type_t role;
|
||||
bool stop;
|
||||
bool exited;
|
||||
bool netif_stop;
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
eppp_channel_fn_t channel_tx;
|
||||
eppp_channel_fn_t channel_rx;
|
||||
void* context;
|
||||
#endif
|
||||
};
|
||||
|
||||
esp_err_t eppp_check_connection(esp_netif_t *netif);
|
280
components/eppp_link/eppp_uart.c
Normal file
280
components/eppp_link/eppp_uart.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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 channel;
|
||||
uint8_t check;
|
||||
uint16_t size;
|
||||
} __attribute__((packed));
|
||||
|
||||
static esp_err_t transmit_generic(struct eppp_uart *handle, int channel, void *buffer, size_t len)
|
||||
{
|
||||
#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->channel = channel;
|
||||
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 transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = h;
|
||||
struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent);
|
||||
return transmit_generic(uart_handle, 0, buffer, len);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
static esp_err_t transmit_channel(esp_netif_t *netif, int channel, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_uart *uart_handle = __containerof(handle, struct eppp_uart, parent);
|
||||
return transmit_generic(uart_handle, channel, buffer, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
int channel = head->channel;
|
||||
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
|
||||
if (channel == 0) {
|
||||
esp_netif_receive(netif, in_buf + buf_start + sizeof(struct header), payload_size, NULL);
|
||||
} else {
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
if (h->parent.channel_rx) {
|
||||
h->parent.channel_rx(netif, channel, in_buf + buf_start + sizeof(struct header), payload_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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");
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
h->parent.channel_tx = transmit_channel;
|
||||
#endif
|
||||
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);
|
||||
}
|
@ -1,2 +1,6 @@
|
||||
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
|
||||
set(wifi_over_channels channel_wifi_station.c)
|
||||
endif()
|
||||
idf_component_register(SRCS app_main.c register_iperf.c
|
||||
INCLUDE_DIRS ".")
|
||||
${wifi_over_channels}
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -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,9 +101,19 @@ 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
|
||||
|
||||
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
|
||||
bool "Use WiFi over EPPP channel"
|
||||
default n
|
||||
depends on EPPP_LINK_CHANNELS_SUPPORT && ESP_WIFI_REMOTE_ENABLED
|
||||
help
|
||||
Enable this option to use WiFi over EPPP channel.
|
||||
If this option is enabled, the example will only start the Wi-Fi driver,
|
||||
but the Wi-Fi netif will reside on client's end and will channel
|
||||
the Rx and Tx data via EPPP channels.
|
||||
|
||||
endmenu
|
||||
|
@ -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
|
||||
*/
|
||||
@ -14,6 +14,7 @@
|
||||
#include "esp_netif.h"
|
||||
#include "eppp_link.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "console_ping.h"
|
||||
|
||||
@ -88,6 +89,7 @@ static void mqtt_app_start(void)
|
||||
}
|
||||
#endif // MQTT
|
||||
|
||||
void station_over_eppp_channel(void *arg);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
@ -104,13 +106,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) {
|
||||
@ -145,6 +158,9 @@ void app_main(void)
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
|
||||
station_over_eppp_channel(eppp_netif);
|
||||
#endif
|
||||
#if CONFIG_EXAMPLE_MQTT
|
||||
mqtt_app_start();
|
||||
#endif
|
||||
|
185
components/eppp_link/examples/host/main/channel_wifi_station.c
Normal file
185
components/eppp_link/examples/host/main/channel_wifi_station.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "eppp_link.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_wifi_remote.h"
|
||||
|
||||
#define CHAT_CHANNEL 1
|
||||
#define WIFI_CHANNEL 2
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
HELLO,
|
||||
START,
|
||||
ERROR,
|
||||
} state_t;
|
||||
|
||||
typedef struct context {
|
||||
eppp_channel_fn_t transmit;
|
||||
EventGroupHandle_t flags;
|
||||
state_t state;
|
||||
esp_netif_t *eppp;
|
||||
} context_t;
|
||||
|
||||
#define HELLO_BIT BIT0
|
||||
#define START_BIT BIT1
|
||||
#define CONNECT_BIT BIT2
|
||||
#define SERVER_UP_BIT BIT3
|
||||
|
||||
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | SERVER_UP_BIT)
|
||||
|
||||
static uint8_t s_wifi_mac_addr[6] = { 0 };
|
||||
static const char *TAG = "eppp_host_example_with_channels";
|
||||
|
||||
esp_netif_t* esp_wifi_remote_create_default_sta(void);
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "IP event_handler: event_base=%s event_id=%d", event_base, event_id);
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||
ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_remote_get_mac(wifi_interface_t ifx, uint8_t mac[6])
|
||||
{
|
||||
if (ifx != WIFI_IF_STA) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
|
||||
}
|
||||
for (int i = 0; i < sizeof(s_wifi_mac_addr); i++) {
|
||||
if (s_wifi_mac_addr[i] == 0) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
memcpy(mac, s_wifi_mac_addr, sizeof(s_wifi_mac_addr));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
|
||||
{
|
||||
context_t *ctx = eppp_get_context(netif);
|
||||
if (nr == CHAT_CHANNEL) {
|
||||
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
|
||||
const char hello[] = "Hello client";
|
||||
const char mac[] = "MAC: ";
|
||||
const char connected[] = "Connected";
|
||||
const char server_up[] = "Server up";
|
||||
size_t mac_len = 5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */;
|
||||
if (len == sizeof(server_up) && memcmp(buffer, server_up, len) == 0) {
|
||||
if (ctx->state == UNKNOWN) {
|
||||
ESP_LOGI(TAG, "Server is up");
|
||||
ctx->state = HELLO;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Received server up in unexpected state %d", ctx->state);
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
xEventGroupSetBits(ctx->flags, SERVER_UP_BIT);
|
||||
} else if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
|
||||
if (ctx->state == HELLO) {
|
||||
xEventGroupSetBits(ctx->flags, HELLO_BIT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Received hello in unexpected state %d", ctx->state);
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
} else if (len == mac_len && memcmp(buffer, mac, 5) == 0) {
|
||||
if (ctx->state == HELLO) {
|
||||
uint8_t mac_addr[6];
|
||||
sscanf((char *)buffer + 5, "%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8 ":%2" PRIx8,
|
||||
&mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]);
|
||||
ESP_LOGI(TAG, "Parsed MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
memcpy(s_wifi_mac_addr, mac_addr, sizeof(s_wifi_mac_addr));
|
||||
xEventGroupSetBits(ctx->flags, START_BIT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Received MAC in unexpected state %d", ctx->state);
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
} else if (len == sizeof(connected) && memcmp(buffer, connected, len) == 0) {
|
||||
if (ctx->state == START) {
|
||||
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Received connected in unexpected state %d", ctx->state);
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
}
|
||||
} else if (nr == WIFI_CHANNEL) {
|
||||
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
|
||||
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
|
||||
return esp_wifi_remote_channel_rx(ctx->eppp, buffer, NULL, len);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t wifi_transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
esp_netif_t *eppp = (esp_netif_t *)h;
|
||||
context_t *ctx = eppp_get_context(eppp);
|
||||
ESP_LOG_BUFFER_HEXDUMP("wifi-transmit", buffer, len, ESP_LOG_VERBOSE);
|
||||
return ctx->transmit(eppp, WIFI_CHANNEL, buffer, len);
|
||||
}
|
||||
|
||||
void esp_netif_destroy_wifi_remote(void *esp_netif);
|
||||
|
||||
void station_over_eppp_channel(void *arg)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret;
|
||||
esp_netif_t *wifi = NULL;
|
||||
context_t ctx = {
|
||||
.transmit = NULL,
|
||||
.flags = NULL,
|
||||
.state = UNKNOWN,
|
||||
.eppp = (esp_netif_t *)arg
|
||||
};
|
||||
ESP_GOTO_ON_FALSE(ctx.eppp != NULL, ESP_FAIL, err, TAG, "Incorrect EPPP netif");
|
||||
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
|
||||
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
|
||||
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
|
||||
ESP_GOTO_ON_ERROR(esp_wifi_remote_channel_set(WIFI_IF_STA, ctx.eppp, wifi_transmit), err, TAG, "Failed to set wifi channel tx function");
|
||||
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, &ctx);
|
||||
|
||||
while (1) {
|
||||
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
|
||||
if (bits & HELLO_BIT) {
|
||||
ESP_LOGI(TAG, "Hello done");
|
||||
if (wifi == NULL) {
|
||||
wifi = esp_wifi_remote_create_default_sta();
|
||||
}
|
||||
const char command[] = "Get MAC";
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)command, sizeof(command));
|
||||
} else if (bits & START_BIT) {
|
||||
ctx.state = START;
|
||||
ESP_LOGI(TAG, "Starting WIFI");
|
||||
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_START, NULL, 0, 0);
|
||||
} else if (bits & CONNECT_BIT) {
|
||||
ESP_LOGI(TAG, "WIFI connected");
|
||||
esp_event_post(WIFI_REMOTE_EVENT, WIFI_EVENT_STA_CONNECTED, NULL, 0, 0);
|
||||
} else if ((bits & SERVER_UP_BIT) == SERVER_UP_BIT || ctx.state != START) {
|
||||
if (ctx.state == ERROR) {
|
||||
esp_netif_destroy_wifi_remote(wifi);
|
||||
wifi = NULL;
|
||||
ESP_LOGI(TAG, "WiFi netif has been destroyed");
|
||||
}
|
||||
const char hello[] = "Hello server";
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
|
||||
ctx.state = HELLO;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
vTaskDelete(NULL);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
dependencies:
|
||||
espressif/iperf-cmd: "^0.1.1"
|
||||
espressif/iperf-cmd: ^0.1.1
|
||||
espressif/eppp_link:
|
||||
version: "*"
|
||||
override_path: "../../.."
|
||||
version: '*'
|
||||
override_path: ../../..
|
||||
console_cmd_ping:
|
||||
version: "*"
|
||||
version: '*'
|
||||
|
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
|
@ -1,2 +1,6 @@
|
||||
idf_component_register(SRCS "eppp_slave.c"
|
||||
INCLUDE_DIRS ".")
|
||||
if(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
|
||||
set(wifi_over_channels channel_wifi_station.c)
|
||||
endif()
|
||||
idf_component_register(SRCS eppp_slave.c
|
||||
${wifi_over_channels}
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -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,9 +93,19 @@ 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
|
||||
|
||||
config EXAMPLE_WIFI_OVER_EPPP_CHANNEL
|
||||
bool "Use WiFi over EPPP channel"
|
||||
default n
|
||||
depends on EPPP_LINK_CHANNELS_SUPPORT
|
||||
help
|
||||
Enable this option to use WiFi over EPPP channel.
|
||||
If this option is enabled, the example will only start the Wi-Fi driver,
|
||||
but the Wi-Fi netif will reside on client's end and will channel
|
||||
the Rx and Tx data via EPPP channels.
|
||||
|
||||
endmenu
|
||||
|
169
components/eppp_link/examples/slave/main/channel_wifi_station.c
Normal file
169
components/eppp_link/examples/slave/main/channel_wifi_station.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "eppp_link.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_private/wifi.h"
|
||||
|
||||
#define CHAT_CHANNEL 1
|
||||
#define WIFI_CHANNEL 2
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN,
|
||||
HELLO,
|
||||
START,
|
||||
ERROR,
|
||||
} state_t;
|
||||
|
||||
typedef struct context {
|
||||
eppp_channel_fn_t transmit;
|
||||
EventGroupHandle_t flags;
|
||||
state_t state;
|
||||
esp_netif_t *eppp;
|
||||
} context_t;
|
||||
|
||||
#define HELLO_BIT BIT0
|
||||
#define START_BIT BIT1
|
||||
#define CONNECT_BIT BIT2
|
||||
#define DISCONNECT_BIT BIT3
|
||||
|
||||
#define ALL_BITS (HELLO_BIT | START_BIT | CONNECT_BIT | DISCONNECT_BIT)
|
||||
|
||||
static const char *TAG = "eppp_host_example_with_channels";
|
||||
static context_t *s_eppp_channel_ctx = NULL;
|
||||
|
||||
static esp_err_t eppp_receive(esp_netif_t *netif, int nr, void *buffer, size_t len)
|
||||
{
|
||||
context_t *ctx = eppp_get_context(netif);
|
||||
if (nr == CHAT_CHANNEL) {
|
||||
ESP_LOGI(TAG, "Received channel=%d len=%d %.*s", nr, (int)len, (int)len, (char *)buffer);
|
||||
const char hello[] = "Hello server";
|
||||
const char mac[] = "Get MAC";
|
||||
if (len == sizeof(hello) && memcmp(buffer, hello, len) == 0) {
|
||||
if (ctx->state == HELLO) {
|
||||
xEventGroupSetBits(ctx->flags, HELLO_BIT);
|
||||
} else {
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
} else if (len == sizeof(mac) && memcmp(buffer, mac, 5) == 0) {
|
||||
if (ctx->state == HELLO) {
|
||||
xEventGroupSetBits(ctx->flags, START_BIT);
|
||||
} else {
|
||||
ctx->state = ERROR;
|
||||
}
|
||||
}
|
||||
} else if (nr == WIFI_CHANNEL) {
|
||||
ESP_LOGD(TAG, "Received WIFI channel=%d len=%d", nr, (int)len);
|
||||
ESP_LOG_BUFFER_HEXDUMP("wifi-receive", buffer, len, ESP_LOG_VERBOSE);
|
||||
return esp_wifi_internal_tx(WIFI_IF_STA, buffer, len);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Incorrect channel number %d", nr);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t wifi_receive(void *buffer, uint16_t len, void *eb)
|
||||
{
|
||||
s_eppp_channel_ctx->transmit(s_eppp_channel_ctx->eppp, WIFI_CHANNEL, buffer, len);
|
||||
esp_wifi_internal_free_rx_buffer(eb);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
context_t *ctx = arg;
|
||||
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%d", event_base, event_id);
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
ESP_LOGI(TAG, "WIFI start event");
|
||||
esp_wifi_connect();
|
||||
xEventGroupSetBits(ctx->flags, CONNECT_BIT);
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "connect to the AP fail");
|
||||
xEventGroupSetBits(ctx->flags, DISCONNECT_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void init_wifi_driver(context_t *ctx)
|
||||
{
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
event_handler, ctx));
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = CONFIG_ESP_WIFI_SSID,
|
||||
.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));
|
||||
}
|
||||
|
||||
void station_over_eppp_channel(void *arg)
|
||||
{
|
||||
__attribute__((unused)) esp_err_t ret;
|
||||
context_t ctx = {
|
||||
.transmit = NULL,
|
||||
.flags = NULL,
|
||||
.state = UNKNOWN,
|
||||
.eppp = (esp_netif_t *)arg
|
||||
};
|
||||
ESP_GOTO_ON_FALSE(ctx.flags = xEventGroupCreate(), ESP_FAIL, err, TAG, "Failed to create event group");
|
||||
ESP_GOTO_ON_ERROR(eppp_add_channels(ctx.eppp, &ctx.transmit, eppp_receive, &ctx), err, TAG, "Failed to add channels");
|
||||
ESP_GOTO_ON_FALSE(ctx.transmit, ESP_FAIL, err, TAG, "Channel tx function is not set");
|
||||
init_wifi_driver(&ctx);
|
||||
|
||||
while (1) {
|
||||
EventBits_t bits = xEventGroupWaitBits(ctx.flags, ALL_BITS, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
|
||||
if (bits & HELLO_BIT) {
|
||||
ESP_LOGI(TAG, "Hello from client received");
|
||||
const char hello[] = "Hello client";
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)hello, sizeof(hello));
|
||||
} else if (bits & START_BIT) {
|
||||
ctx.state = START;
|
||||
ESP_LOGI(TAG, "Starting WIFI");
|
||||
uint8_t mac[6];
|
||||
if (esp_wifi_get_mac(WIFI_IF_STA, mac) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_wifi_get_mac failed");
|
||||
ctx.state = ERROR;
|
||||
continue;
|
||||
}
|
||||
char mac_data[5 /* MAC: */ + 6 * 2 /* 6 bytes per char */ + 5 /* : */ + 1 /* \0 */];
|
||||
sprintf(mac_data, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
ESP_LOGI(TAG, "Sending MAC: %.*s", (int)sizeof(mac_data), mac_data);
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)mac_data, sizeof(mac_data));
|
||||
ret = esp_wifi_start();
|
||||
ESP_LOGI(TAG, "WIFI start result: %d", ret);
|
||||
s_eppp_channel_ctx = &ctx;
|
||||
esp_wifi_internal_reg_rxcb(WIFI_IF_STA, wifi_receive);
|
||||
} else if (bits & CONNECT_BIT) {
|
||||
ESP_LOGI(TAG, "WIFI connected");
|
||||
const char connected[] = "Connected";
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)connected, sizeof(connected));
|
||||
} else if (bits & DISCONNECT_BIT) {
|
||||
const char disconnected[] = "Disconnected";
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)disconnected, sizeof(disconnected));
|
||||
} else if (ctx.state != START) {
|
||||
ctx.state = HELLO;
|
||||
const char up[] = "Server up";
|
||||
esp_wifi_disconnect();
|
||||
esp_wifi_stop();
|
||||
ctx.transmit(ctx.eppp, CHAT_CHANNEL, (void*)up, sizeof(up));
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
vTaskDelete(NULL);
|
||||
}
|
@ -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
|
||||
*/
|
||||
@ -11,12 +11,14 @@
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "eppp_link.h"
|
||||
#include "inttypes.h"
|
||||
|
||||
static const char *TAG = "eppp_slave";
|
||||
|
||||
#if CONFIG_SOC_WIFI_SUPPORTED
|
||||
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL)
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected*/
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
@ -27,12 +29,13 @@ static EventGroupHandle_t s_wifi_event_group;
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
|
||||
static int s_retry_num = 0;
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "event_handler: event_base=%s event_id=%" PRIi32, event_base, event_id);
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
ESP_LOGI(TAG, "WIFI start event");
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
|
||||
@ -55,9 +58,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 +66,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 +82,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.");
|
||||
|
||||
@ -110,12 +110,15 @@ void init_network_interface(void)
|
||||
}
|
||||
#else
|
||||
|
||||
// If the SoC does not have WiFi capabilities, we can initialize a different network interface, this function is a placeholder for that purpose.
|
||||
// This function is also a no-op if EXAMPLE_WIFI_OVER_EPPP_CHANNEL==1, since the Wi-Fi network interface will live on the other peer (on the host side).
|
||||
void init_network_interface(void)
|
||||
{
|
||||
// placeholder to initialize any other network interface if WiFi is not available
|
||||
}
|
||||
|
||||
#endif // SoC WiFi capable chip
|
||||
#endif // SoC WiFi capable chip || WiFi over EPPP channel
|
||||
|
||||
void station_over_eppp_channel(void *arg);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
@ -127,13 +130,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;
|
||||
@ -148,5 +159,9 @@ void app_main(void)
|
||||
ESP_LOGE(TAG, "Failed to setup connection");
|
||||
return ;
|
||||
}
|
||||
#ifdef CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
|
||||
station_over_eppp_channel(eppp_netif);
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_netif_napt_enable(eppp_netif));
|
||||
#endif // CONFIG_EXAMPLE_WIFI_OVER_EPPP_CHANNEL
|
||||
}
|
||||
|
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: 1.0.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,18 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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 +25,8 @@
|
||||
.cs_ena_pretrans = 0, \
|
||||
.cs_ena_posttrans = 0, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_UART_CONFIG() \
|
||||
.uart = { \
|
||||
.port = 1, \
|
||||
.baud = 921600, \
|
||||
@ -29,15 +35,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,14 +108,15 @@ typedef enum eppp_transport {
|
||||
EPPP_TRANSPORT_UART,
|
||||
EPPP_TRANSPORT_SPI,
|
||||
EPPP_TRANSPORT_SDIO,
|
||||
EPPP_TRANSPORT_ETHERNET,
|
||||
} eppp_transport_t;
|
||||
|
||||
|
||||
typedef struct eppp_config_t {
|
||||
eppp_transport_t transport;
|
||||
|
||||
struct eppp_config_spi_s {
|
||||
int host;
|
||||
bool is_master;
|
||||
int mosi;
|
||||
int miso;
|
||||
int sclk;
|
||||
@ -92,6 +138,7 @@ typedef struct eppp_config_t {
|
||||
} uart;
|
||||
|
||||
struct eppp_config_sdio_s {
|
||||
bool is_host;
|
||||
int width;
|
||||
int clk;
|
||||
int cmd;
|
||||
@ -101,6 +148,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 +170,51 @@ 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);
|
||||
|
||||
#ifdef CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
typedef esp_err_t (*eppp_channel_fn_t)(esp_netif_t *netif, int nr, void *buffer, size_t len);
|
||||
|
||||
esp_err_t eppp_add_channels(esp_netif_t *netif, eppp_channel_fn_t *tx, const eppp_channel_fn_t rx, void* context);
|
||||
|
||||
void* eppp_get_context(esp_netif_t *netif);
|
||||
#endif // CONFIG_EPPP_LINK_CHANNELS_SUPPORT
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
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;
|
||||
}
|
312
components/esp_dns/esp_dns_doh.c
Normal file
312
components/esp_dns/esp_dns_doh.c
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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;
|
||||
default:
|
||||
ESP_LOGD(TAG, "Other HTTP event: %d", evt->event_id);
|
||||
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)
|
152
components/esp_dns/examples/esp_dns_basic/README.md
Normal file
152
components/esp_dns/examples/esp_dns_basic/README.md
Normal file
@ -0,0 +1,152 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP DNS Example
|
||||
|
||||
This example demonstrates how to use the ESP DNS component in an ESP32 application. The example resolves domain names using various DNS protocols including standard UDP, TCP, DNS over TLS (DoT), and DNS over HTTPS (DoH).
|
||||
|
||||
## Features
|
||||
|
||||
- **Standard UDP DNS**: Traditional DNS resolution over UDP
|
||||
- **DNS over TCP**: DNS resolution using TCP transport
|
||||
- **DNS over TLS (DoT)**: Secure DNS resolution using TLS encryption
|
||||
- **DNS over HTTPS (DoH)**: Secure DNS resolution using HTTPS
|
||||
|
||||
## Certificate Options
|
||||
|
||||
This example provides two certificate options for secure DNS protocols (DoT and DoH):
|
||||
|
||||
1. **Certificate Bundle (Default)**: Uses the ESP-IDF certificate bundle, making it easy to get started with popular DNS providers like Google.
|
||||
2. **Custom Certificate**: Uses a specific certificate for the DNS server. The example includes a Google DNS certificate.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Network Initialization**: The application initializes the network interfaces (Wi-Fi or Ethernet) and establishes a connection.
|
||||
2. **DNS Resolution Tests**: The example performs DNS resolution using different protocols:
|
||||
- Native UDP DNS (system default)
|
||||
- ESP DNS with UDP protocol
|
||||
- ESP DNS with TCP protocol
|
||||
- ESP DNS with DoT protocol (using server certificate)
|
||||
- ESP DNS with DoT protocol (using certificate bundle)
|
||||
- ESP DNS with DoH protocol (using server certificate)
|
||||
- ESP DNS with DoH protocol (using certificate bundle)
|
||||
3. **Domain Resolution**: For each protocol, the application resolves several domain names including:
|
||||
- yahoo.com
|
||||
- www.google.com
|
||||
- IP addresses (0.0.0.0 and IPv6 address)
|
||||
|
||||
## How to use example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Troubleshooting Tips
|
||||
|
||||
* **Connectivity**:
|
||||
Ensure that the network connection details are accurate. For example, verify the Wi-Fi SSID and password or check that the Ethernet connection is secure and not faulty.
|
||||
|
||||
* **Memory Issues**:
|
||||
If you encounter memory-related errors, check the system information output which displays free heap and stack high water mark. You may need to increase task stack sizes for more complex DNS operations.
|
||||
|
||||
* **Certificate Issues**:
|
||||
For DoT and DoH protocols, ensure that the certificates are valid for the DNS server you're using. The example includes Google DNS certificates, but these may need to be updated if they expire.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (4583) example_esp_dns: Executing DNS without initializing ESP_DNS module
|
||||
I (4603) wifi:<ba-add>idx:1 (ifx:0, a0:36:bc:0e:c4:f0), tid:7, ssn:3, winSize:64
|
||||
I (4613) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (4613) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
|
||||
I (4613) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
|
||||
I (4613) wifi:<ba-del>idx:0, tid:6
|
||||
I (4623) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (4623) wifi:<ba-add>idx:0 (ifx:0, a0:36:bc:0e:c4:f0), tid:0, ssn:1, winSize:64
|
||||
I (4643) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (4643) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (4643) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (4653) example_esp_dns: Free Heap: 215292 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (4663) example_esp_dns: Executing UDP DNS
|
||||
I (4673) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (4673) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
|
||||
I (4683) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
|
||||
I (4683) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (4693) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (4703) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (4703) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (4713) example_esp_dns: Free Heap: 215116 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (4723) example_esp_dns: Executing TCP DNS
|
||||
I (4763) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (4763) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
|
||||
I (4763) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
|
||||
I (4763) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (4793) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (4793) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (4793) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (4803) example_esp_dns: Free Heap: 214580 bytes, Min Free Heap: 206008 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (4813) example_esp_dns: Executing DNS over TLS
|
||||
I (5963) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
|
||||
I (5963) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (5963) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (5973) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
|
||||
I (7083) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (7083) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (7083) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (7093) example_esp_dns: Free Heap: 213504 bytes, Min Free Heap: 165308 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (7103) example_esp_dns: Executing DNS over TLS
|
||||
I (7413) esp-x509-crt-bundle: Certificate validated
|
||||
I (8233) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
|
||||
I (8233) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (8233) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (8243) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
|
||||
I (8553) esp-x509-crt-bundle: Certificate validated
|
||||
I (9363) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (9363) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (9363) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (9373) example_esp_dns: Free Heap: 213120 bytes, Min Free Heap: 165308 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (9383) example_esp_dns: Executing DNS over HTTPS
|
||||
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.143.26(IPv4)
|
||||
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.231.20(IPv4)
|
||||
I (10563) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
|
||||
I (10573) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (11713) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (11713) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (11723) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (11723) example_esp_dns: Free Heap: 212664 bytes, Min Free Heap: 162780 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (11733) example_esp_dns: Executing DNS over HTTPS
|
||||
I (12033) esp-x509-crt-bundle: Certificate validated
|
||||
I (12863) example_esp_dns: Hostname: yahoo.com: 74.6.231.21(IPv4)
|
||||
I (12863) example_esp_dns: Hostname: yahoo.com: 98.137.11.163(IPv4)
|
||||
I (12863) example_esp_dns: Hostname: yahoo.com: 98.137.11.164(IPv4)
|
||||
I (12873) example_esp_dns: Hostname: yahoo.com: 74.6.143.25(IPv4)
|
||||
I (13153) esp-x509-crt-bundle: Certificate validated
|
||||
I (13993) example_esp_dns: Hostname: www.google.com: 2404:6800:4015:803::2004(IPv6)
|
||||
I (13993) example_esp_dns: Hostname: 0.0.0.0: 0.0.0.0(IPv4)
|
||||
I (13993) example_esp_dns: Hostname: fe80:0000:0000:0000:5abf:25ff:fee0:4100: FE80::5ABF:25FF:FEE0:4100(IPv6)
|
||||
I (14003) example_esp_dns: Free Heap: 212044 bytes, Min Free Heap: 162780 bytes, Stack High Water Mark: 1220 bytes
|
||||
|
||||
I (14013) main_task: Returned from app_main()
|
||||
```
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "esp_dns_example.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES "cert_google_root.pem")
|
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
|
||||
27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
|
||||
Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
|
||||
TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
|
||||
qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
|
||||
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
|
||||
Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
|
||||
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
|
||||
wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
|
||||
aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
|
||||
VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
|
||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
|
||||
C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
|
||||
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
|
||||
h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
|
||||
7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
|
||||
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
|
||||
MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
|
||||
Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
|
||||
6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
|
||||
0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
|
||||
2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
|
||||
bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
|
||||
-----END CERTIFICATE-----
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user