mirror of
https://github.com/garagetinkering/Video_Game_Mini_Maps.git
synced 2026-01-18 01:57:00 +01:00
initial commit
This commit is contained in:
1
managed_components/espressif__eppp_link/.component_hash
Normal file
1
managed_components/espressif__eppp_link/.component_hash
Normal file
@@ -0,0 +1 @@
|
||||
41f6519edda527ec6a0553c872ebaf8fc6d3812523c9d4c8d1660ad21c720abe
|
||||
8
managed_components/espressif__eppp_link/.cz.yaml
Normal file
8
managed_components/espressif__eppp_link/.cz.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
commitizen:
|
||||
bump_message: 'bump(eppp): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py eppp_link
|
||||
tag_format: eppp-v$version
|
||||
version: 1.1.3
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
99
managed_components/espressif__eppp_link/CHANGELOG.md
Normal file
99
managed_components/espressif__eppp_link/CHANGELOG.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.3](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.3)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix test dependency issue on driver ([1ace92c2](https://github.com/espressif/esp-protocols/commit/1ace92c2))
|
||||
- Fix tun netif to (optionally) return errors ([7a6cf0f9](https://github.com/espressif/esp-protocols/commit/7a6cf0f9))
|
||||
|
||||
## [1.1.2](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.2)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Update uart driver deps per IDF > v5.3 ([92e14607](https://github.com/espressif/esp-protocols/commit/92e14607))
|
||||
|
||||
## [1.1.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix getting context for channel API ([94563cdc](https://github.com/espressif/esp-protocols/commit/94563cdc))
|
||||
- Cover more combinations in build tests ([e0b8de8f](https://github.com/espressif/esp-protocols/commit/e0b8de8f))
|
||||
|
||||
## [1.1.0](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for UART flow control ([cd57f1bb](https://github.com/espressif/esp-protocols/commit/cd57f1bb), [#870](https://github.com/espressif/esp-protocols/issues/870))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix SPI transport to allow already init GPIO ISR ([497ee2d6](https://github.com/espressif/esp-protocols/commit/497ee2d6), [#868](https://github.com/espressif/esp-protocols/issues/868))
|
||||
- Fix stack-overflow in ping task for TUN netif ([b2568a3d](https://github.com/espressif/esp-protocols/commit/b2568a3d), [#867](https://github.com/espressif/esp-protocols/issues/867))
|
||||
|
||||
### Updated
|
||||
|
||||
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
|
||||
|
||||
## [1.0.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.0.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Support for IPv4-only mode ([653328ba](https://github.com/espressif/esp-protocols/commit/653328ba), [#864](https://github.com/espressif/esp-protocols/issues/864))
|
||||
|
||||
## [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
|
||||
|
||||
- Add support for SDIO transport ([085dd790](https://github.com/espressif/esp-protocols/commit/085dd790))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed strict prototype API decl issue in SDIO ([eb09e426](https://github.com/espressif/esp-protocols/commit/eb09e426))
|
||||
- Fix SIDO host to check/clear interrupts atomically ([402176c9](https://github.com/espressif/esp-protocols/commit/402176c9))
|
||||
|
||||
## [0.1.1](https://github.com/espressif/esp-protocols/commits/eppp-v0.1.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Make some common netif options configurable ([7829e8f](https://github.com/espressif/esp-protocols/commit/7829e8f))
|
||||
|
||||
## [0.1.0](https://github.com/espressif/esp-protocols/commits/eppp-v0.1.0)
|
||||
|
||||
### Features
|
||||
|
||||
- Added CI job to build examples and tests ([7eefcf0](https://github.com/espressif/esp-protocols/commit/7eefcf0))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed to select PPP LWIP opts which are OFF by default ([16be2f9](https://github.com/espressif/esp-protocols/commit/16be2f9))
|
||||
- Example to use iperf component from the registry ([bd6b66d](https://github.com/espressif/esp-protocols/commit/bd6b66d))
|
||||
- Fixed defalt config designated init issue in C++ ([8bd4712](https://github.com/espressif/esp-protocols/commit/8bd4712))
|
||||
|
||||
## [0.0.1](https://github.com/espressif/esp-protocols/commits/eppp-v0.0.1)
|
||||
|
||||
### Features
|
||||
|
||||
- Added CI job to build examples and tests ([8686977](https://github.com/espressif/esp-protocols/commit/8686977))
|
||||
- Added support for SPI transport ([18f8452](https://github.com/espressif/esp-protocols/commit/18f8452))
|
||||
- Added support for UART transport ([ad27414](https://github.com/espressif/esp-protocols/commit/ad27414))
|
||||
- Introduced ESP-PPP-Link component ([a761039](https://github.com/espressif/esp-protocols/commit/a761039))
|
||||
33
managed_components/espressif__eppp_link/CMakeLists.txt
Normal file
33
managed_components/espressif__eppp_link/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
|
||||
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_timer esp_eth ${driver_deps})
|
||||
|
||||
if(CONFIG_EPPP_LINK_DEVICE_ETH)
|
||||
idf_component_optional_requires(PRIVATE ethernet_init espressif__ethernet_init)
|
||||
endif()
|
||||
113
managed_components/espressif__eppp_link/Kconfig
Normal file
113
managed_components/espressif__eppp_link/Kconfig
Normal file
@@ -0,0 +1,113 @@
|
||||
menu "eppp_link"
|
||||
|
||||
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"
|
||||
default EPPP_LINK_DEVICE_UART
|
||||
help
|
||||
Select which peripheral to use for PPP link
|
||||
|
||||
config EPPP_LINK_DEVICE_UART
|
||||
bool "UART"
|
||||
help
|
||||
Use UART.
|
||||
|
||||
config EPPP_LINK_DEVICE_SPI
|
||||
bool "SPI"
|
||||
help
|
||||
Use SPI.
|
||||
|
||||
config EPPP_LINK_DEVICE_SDIO
|
||||
bool "SDIO"
|
||||
depends on SOC_SDMMC_HOST_SUPPORTED || SOC_SDIO_SLAVE_SUPPORTED
|
||||
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
|
||||
int "Maximum retry"
|
||||
default 6
|
||||
help
|
||||
Set the Maximum retry to infinitely avoid reconnecting
|
||||
This is used only with the simplified API (eppp_connect()
|
||||
and eppp_listen())
|
||||
|
||||
config EPPP_LINK_PACKET_QUEUE_SIZE
|
||||
int "Packet queue size"
|
||||
default 64
|
||||
depends on EPPP_LINK_DEVICE_SPI
|
||||
help
|
||||
Size of the Tx packet queue.
|
||||
You can decrease the number for slower bit rates.
|
||||
|
||||
choice EPPP_LINK_SDIO_ROLE
|
||||
prompt "Choose SDIO host or slave"
|
||||
depends on EPPP_LINK_DEVICE_SDIO
|
||||
default EPPP_LINK_DEVICE_SDIO_HOST if SOC_SDMMC_HOST_SUPPORTED
|
||||
help
|
||||
Select which either SDIO host or slave
|
||||
|
||||
config EPPP_LINK_DEVICE_SDIO_HOST
|
||||
bool "Host"
|
||||
depends on SOC_SDMMC_HOST_SUPPORTED
|
||||
help
|
||||
Use SDIO host.
|
||||
|
||||
config EPPP_LINK_DEVICE_SDIO_SLAVE
|
||||
bool "SLAVE"
|
||||
depends on SOC_SDIO_SLAVE_SUPPORTED
|
||||
help
|
||||
Use SDIO slave.
|
||||
|
||||
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
|
||||
202
managed_components/espressif__eppp_link/LICENSE
Normal file
202
managed_components/espressif__eppp_link/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
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.
|
||||
116
managed_components/espressif__eppp_link/README.md
Normal file
116
managed_components/espressif__eppp_link/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# ESP PPP Link component (eppp_link)
|
||||
|
||||
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 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.
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
* `eppp_connect()` -- Simplified API. Provides the initialization, starts the task and blocks until we're connected
|
||||
|
||||
### Server
|
||||
|
||||
* `eppp_listen()` -- Simplified API. Provides the initialization, starts the task and blocks until the client connects
|
||||
|
||||
### Manual actions
|
||||
|
||||
* `eppp_init()` -- Initializes one endpoint (client/server).
|
||||
* `eppp_deinit()` -- Destroys the endpoint
|
||||
* `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
|
||||
|
||||
Tested with WiFi-NAPT example
|
||||
|
||||
### UART @ 3Mbauds
|
||||
|
||||
* TCP - 2Mbits/s
|
||||
* UDP - 2Mbits/s
|
||||
|
||||
### SPI @ 16MHz
|
||||
|
||||
* TCP - 5Mbits/s
|
||||
* UDP - 8Mbits/s
|
||||
|
||||
### SDIO
|
||||
|
||||
* TCP - 9Mbits/s
|
||||
* UDP - 11Mbits/s
|
||||
|
||||
### Ethernet
|
||||
|
||||
- Internal EMAC with real PHY chip
|
||||
* TCP - 5Mbits/s
|
||||
* UDP - 8Mbits/s
|
||||
370
managed_components/espressif__eppp_link/detailed_description.md
Normal file
370
managed_components/espressif__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
managed_components/espressif__eppp_link/eppp_eth.c
Normal file
210
managed_components/espressif__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);
|
||||
}
|
||||
390
managed_components/espressif__eppp_link/eppp_link.c
Normal file
390
managed_components/espressif__eppp_link/eppp_link.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-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 "esp_netif_ppp.h"
|
||||
#include "eppp_link.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_ETH
|
||||
#define EPPP_NEEDS_TASK 0
|
||||
#else
|
||||
#define EPPP_NEEDS_TASK 1
|
||||
#endif
|
||||
|
||||
static const int GOT_IPV4 = BIT0;
|
||||
static const int CONNECTION_FAILED = BIT1;
|
||||
#define CONNECT_BITS (GOT_IPV4|CONNECTION_FAILED)
|
||||
|
||||
static EventGroupHandle_t s_event_group = NULL;
|
||||
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
|
||||
|
||||
void eppp_netif_deinit(esp_netif_t *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
esp_netif_destroy(netif);
|
||||
if (s_eppp_netif_count > 0) {
|
||||
s_eppp_netif_count--;
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
h->role = role;
|
||||
#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;
|
||||
if (eppp_config->ppp.netif_description) {
|
||||
base_netif_cfg.if_desc = eppp_config->ppp.netif_description;
|
||||
} else {
|
||||
base_netif_cfg.if_desc = role == EPPP_CLIENT ? "pppos_client" : "pppos_server";
|
||||
}
|
||||
if (eppp_config->ppp.netif_prio) {
|
||||
base_netif_cfg.route_prio = eppp_config->ppp.netif_prio;
|
||||
}
|
||||
esp_netif_config_t netif_config = { .base = &base_netif_cfg,
|
||||
.driver = NULL,
|
||||
.stack = NETSTACK_CONFIG(),
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
esp_netif_action_disconnected(netif, 0, 0, 0);
|
||||
esp_netif_action_stop(netif, 0, 0, 0);
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
for (int wait = 0; wait < 100; wait++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(stop_timeout_ms) / 100);
|
||||
if (h->netif_stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!h->netif_stop) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return -1;
|
||||
}
|
||||
const char *ifkey = esp_netif_get_ifkey(netif);
|
||||
if (strstr(ifkey, "EPPP") == NULL) {
|
||||
return -1; // not our netif
|
||||
}
|
||||
int netif_cnt = ifkey[4] - '0';
|
||||
if (netif_cnt < 0 || netif_cnt > 9) {
|
||||
ESP_LOGE(TAG, "Unexpected netif key %s", ifkey);
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
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)
|
||||
{
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)data;
|
||||
esp_netif_t *netif = event->esp_netif;
|
||||
int netif_cnt = get_netif_num(netif);
|
||||
if (netif_cnt < 0) {
|
||||
return;
|
||||
}
|
||||
if (event_id == IP_EVENT_PPP_GOT_IP) {
|
||||
ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
|
||||
esp_netif_get_ifkey(netif), IP2STR(&event->ip_info.ip));
|
||||
xEventGroupSetBits(s_event_group, GOT_IPV4 << (netif_cnt * 2));
|
||||
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGI(TAG, "Disconnected");
|
||||
s_retry_num++;
|
||||
if (s_retry_num > CONFIG_EPPP_LINK_CONN_MAX_RETRY) {
|
||||
ESP_LOGE(TAG, "PPP Connection failed %d times, stop reconnecting.", s_retry_num);
|
||||
xEventGroupSetBits(s_event_group, CONNECTION_FAILED << (netif_cnt * 2));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "PPP Connection failed %d times, try to reconnect.", s_retry_num);
|
||||
eppp_netif_start(netif);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if EPPP_NEEDS_TASK
|
||||
static void ppp_task(void *args)
|
||||
{
|
||||
esp_netif_t *netif = args;
|
||||
while (eppp_perform(netif) != ESP_ERR_TIMEOUT) {}
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
h->exited = true;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool have_some_eppp_netif(esp_netif_t *netif, void *ctx)
|
||||
{
|
||||
return get_netif_num(netif) > 0;
|
||||
}
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
void eppp_deinit(esp_netif_t *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (config == NULL || (role != EPPP_SERVER && role != EPPP_CLIENT)) {
|
||||
ESP_LOGE(TAG, "Invalid configuration or role");
|
||||
return NULL;
|
||||
}
|
||||
#if CONFIG_EPPP_LINK_DEVICE_UART
|
||||
if (config->transport != EPPP_TRANSPORT_UART) {
|
||||
ESP_LOGE(TAG, "Invalid transport: UART device must be enabled in Kconfig");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
if (config->transport != EPPP_TRANSPORT_SPI) {
|
||||
ESP_LOGE(TAG, "Invalid transport: SPI device must be enabled in Kconfig");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
if (config->transport != EPPP_TRANSPORT_SDIO) {
|
||||
ESP_LOGE(TAG, "Invalid transport: SDIO device must be enabled in Kconfig");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config->task.run_task == false) {
|
||||
ESP_LOGE(TAG, "task.run_task == false is invalid in this API. Please use eppp_init()");
|
||||
return NULL;
|
||||
}
|
||||
if (s_event_group == NULL) {
|
||||
s_event_group = xEventGroupCreate();
|
||||
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register IP event handler");
|
||||
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) {
|
||||
remove_handlers();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Waiting for IP address %d", netif_cnt);
|
||||
EventBits_t bits = xEventGroupWaitBits(s_event_group, CONNECT_BITS << (netif_cnt * 2), pdFALSE, pdFALSE, pdMS_TO_TICKS(connect_timeout_ms));
|
||||
if (bits & (CONNECTION_FAILED << (netif_cnt * 2))) {
|
||||
ESP_LOGE(TAG, "Connection failed!");
|
||||
eppp_close(netif);
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected! %d", netif_cnt);
|
||||
return netif;
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_connect(eppp_config_t *config)
|
||||
{
|
||||
return eppp_open(EPPP_CLIENT, config, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_listen(eppp_config_t *config)
|
||||
{
|
||||
return eppp_open(EPPP_SERVER, config, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void eppp_close(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (eppp_netif_stop(netif, 60000) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Network didn't exit cleanly");
|
||||
}
|
||||
h->stop = true;
|
||||
for (int wait = 0; wait < 100; wait++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
if (h->exited) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!h->exited) {
|
||||
ESP_LOGE(TAG, "Cannot stop ppp_task");
|
||||
}
|
||||
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
|
||||
207
managed_components/espressif__eppp_link/eppp_netif_tun.c
Normal file
207
managed_components/espressif__eppp_link/eppp_netif_tun.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#if defined(CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS)
|
||||
typedef esp_err_t esp_netif_recv_ret_t;
|
||||
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr) expr
|
||||
#else
|
||||
typedef void esp_netif_recv_ret_t;
|
||||
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr)
|
||||
#endif // CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS
|
||||
|
||||
static const char *TAG = "eppp_tun_netif";
|
||||
|
||||
static esp_netif_recv_ret_t 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 ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_OK);
|
||||
err:
|
||||
if (p) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
return ESP_NETIF_OPTIONAL_RETURN_CODE(ret);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
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)));
|
||||
}
|
||||
#if LWIP_IPV6
|
||||
else {
|
||||
ESP_LOGD(TAG, "\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
|
||||
}
|
||||
#endif
|
||||
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();
|
||||
#if CONFIG_LOG_MAXIMUM_LEVEL > 3
|
||||
config.task_stack_size += 1024; // Some additional stack needed for verbose logs
|
||||
#endif
|
||||
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);
|
||||
#if LWIP_IPV6
|
||||
target_addr.u_addr.ip4.addr = ip.gw.addr;
|
||||
#else
|
||||
target_addr.addr = ip.gw.addr;
|
||||
#endif
|
||||
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
managed_components/espressif__eppp_link/eppp_sdio.c
Normal file
92
managed_components/espressif__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();
|
||||
}
|
||||
}
|
||||
29
managed_components/espressif__eppp_link/eppp_sdio.h
Normal file
29
managed_components/espressif__eppp_link/eppp_sdio.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
|
||||
// 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);
|
||||
237
managed_components/espressif__eppp_link/eppp_sdio_host.c
Normal file
237
managed_components/espressif__eppp_link/eppp_sdio_host.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-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 "driver/sdio_slave.h"
|
||||
#include "esp_serial_slave_link/essl_sdio.h"
|
||||
#include "eppp_sdio.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "esp_check.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SDIO_HOST
|
||||
|
||||
// For blocking operations
|
||||
#define TIMEOUT_MAX UINT32_MAX
|
||||
// Short timeout for sending/receiving ESSL packets
|
||||
#define PACKET_TIMEOUT_MS 50
|
||||
|
||||
static const char *TAG = "eppp_sdio_host";
|
||||
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_PACKET_SIZE];
|
||||
static DMA_ATTR uint8_t rcv_buffer[SDIO_PACKET_SIZE];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
ESP_LOGE(TAG, "Slave not ready to receive packet %x", ret);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
ret = ESP_ERR_NO_MEM; // to inform the upper layers
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, send_buffer, send_len, ESP_LOG_VERBOSE);
|
||||
xSemaphoreGive(s_essl_mutex);
|
||||
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;
|
||||
ESP_LOGI(TAG, "send reset to slave...");
|
||||
ESP_GOTO_ON_ERROR(essl_write_reg(s_essl, SLAVE_REG_REQ, REQ_RESET, NULL, TIMEOUT_MAX), err, TAG, "write-reg failed");
|
||||
ESP_GOTO_ON_ERROR(essl_send_slave_intr(s_essl, BIT(SLAVE_INTR), TIMEOUT_MAX), err, TAG, "send-intr failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(PACKET_TIMEOUT_MS));
|
||||
ESP_GOTO_ON_ERROR(essl_wait_for_ready(s_essl, TIMEOUT_MAX), err, TAG, "wait-for-ready failed");
|
||||
ESP_LOGI(TAG, "slave io ready");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *eppp_config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ESP_GOTO_ON_ERROR(sdmmc_host_init(), err, TAG, "sdmmc host init failed");
|
||||
|
||||
// configure SDIO interface and slot
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
slot_config.width = eppp_config->width;
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.clk = eppp_config->clk;
|
||||
slot_config.cmd = eppp_config->cmd;
|
||||
slot_config.d0 = eppp_config->d0;
|
||||
slot_config.d1 = eppp_config->d1;
|
||||
slot_config.d2 = eppp_config->d2;
|
||||
slot_config.d3 = eppp_config->d3;
|
||||
#endif
|
||||
|
||||
ESP_GOTO_ON_ERROR(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config), err, TAG, "init sdmmc host slot failed");
|
||||
|
||||
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
|
||||
config.flags = SDMMC_HOST_FLAG_4BIT;
|
||||
config.flags |= SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF;
|
||||
config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
|
||||
s_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||
ESP_GOTO_ON_FALSE(s_card, ESP_ERR_NO_MEM, err, TAG, "card allocation failed");
|
||||
ESP_GOTO_ON_ERROR(sdmmc_card_init(&config, s_card), err, TAG, "sdmmc card init failed");
|
||||
|
||||
essl_sdio_config_t ser_config = {
|
||||
.card = s_card,
|
||||
.recv_buffer_size = SDIO_PAYLOAD,
|
||||
};
|
||||
ESP_GOTO_ON_FALSE(essl_sdio_init_dev(&s_essl, &ser_config) == ESP_OK && s_essl, ESP_FAIL, err, TAG, "essl_sdio_init_dev failed");
|
||||
ESP_GOTO_ON_ERROR(essl_init(s_essl, TIMEOUT_MAX), err, TAG, "essl-init failed");
|
||||
ESP_GOTO_ON_ERROR(request_slave_reset(), err, TAG, "failed to reset the slave");
|
||||
ESP_GOTO_ON_FALSE((s_essl_mutex = xSemaphoreCreateMutex()), ESP_ERR_NO_MEM, err, TAG, "failed to create semaphore");
|
||||
return ret;
|
||||
|
||||
err:
|
||||
essl_sdio_deinit_dev(s_essl);
|
||||
s_essl = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t get_intr(uint32_t *out_raw)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_ERROR(essl_get_intr(s_essl, out_raw, NULL, 0), err, TAG, "essl-get-int failed");
|
||||
ESP_GOTO_ON_ERROR(essl_clear_intr(s_essl, *out_raw, 0), err, TAG, "essl-clear-int failed");
|
||||
ESP_LOGD(TAG, "intr: %08"PRIX32, *out_raw);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_host_rx(esp_netif_t *netif)
|
||||
{
|
||||
uint32_t intr;
|
||||
esp_err_t err = essl_wait_int(s_essl, TIMEOUT_MAX);
|
||||
if (err == ESP_ERR_TIMEOUT) {
|
||||
return ESP_OK;
|
||||
}
|
||||
xSemaphoreTake(s_essl_mutex, portMAX_DELAY);
|
||||
err = get_intr(&intr);
|
||||
if (err == ESP_ERR_TIMEOUT) {
|
||||
xSemaphoreGive(s_essl_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to check for interrupts %d", err);
|
||||
xSemaphoreGive(s_essl_mutex);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (intr & ESSL_SDIO_DEF_ESP32.new_packet_intr_mask) {
|
||||
esp_err_t ret;
|
||||
do {
|
||||
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);
|
||||
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);
|
||||
if (request_slave_reset() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to request slave reset %x", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (ret == ESP_ERR_NOT_FINISHED);
|
||||
}
|
||||
xSemaphoreGive(s_essl_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void eppp_sdio_host_deinit(void)
|
||||
{
|
||||
essl_sdio_deinit_dev(s_essl);
|
||||
sdmmc_host_deinit();
|
||||
free(s_card);
|
||||
s_card = NULL;
|
||||
s_essl = NULL;
|
||||
}
|
||||
|
||||
#else // SDMMC_HOST NOT-SUPPORTED
|
||||
|
||||
esp_err_t eppp_sdio_host_tx(void *handle, void *buffer, size_t len)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_host_rx(esp_netif_t *netif)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void eppp_sdio_host_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
#endif // CONFIG_SOC_SDIO_SLAVE_SUPPORTED
|
||||
208
managed_components/espressif__eppp_link/eppp_sdio_slave.c
Normal file
208
managed_components/espressif__eppp_link/eppp_sdio_slave.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-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 "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
|
||||
static const char *TAG = "eppp_sdio_slave";
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
ESP_LOGE(TAG, "sdio slave transmit error, ret : 0x%x", ret);
|
||||
// to inform the upper layers
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
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;
|
||||
ESP_LOGI(TAG, "SDIO slave reset");
|
||||
sdio_slave_stop();
|
||||
ESP_GOTO_ON_ERROR(sdio_slave_reset(), err, TAG, "slave reset failed");
|
||||
ESP_GOTO_ON_ERROR(sdio_slave_start(), err, TAG, "slave start failed");
|
||||
|
||||
while (1) {
|
||||
sdio_slave_buf_handle_t handle;
|
||||
ret = sdio_slave_send_get_finished(&handle, 0);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "slave-get-finished failed");
|
||||
ESP_GOTO_ON_ERROR(sdio_slave_recv_load_buf(handle), err, TAG, "slave-load-buf failed");
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif)
|
||||
{
|
||||
if (s_slave_request == REQ_RESET) {
|
||||
ESP_LOGD(TAG, "request: %x", s_slave_request);
|
||||
slave_reset();
|
||||
s_slave_request = REQ_INIT;
|
||||
}
|
||||
sdio_slave_buf_handle_t handle;
|
||||
size_t length;
|
||||
uint8_t *ptr;
|
||||
esp_err_t ret = sdio_slave_recv_packet(&handle, pdMS_TO_TICKS(1000));
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) {
|
||||
again:
|
||||
ptr = sdio_slave_recv_get_buf(handle, &length);
|
||||
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;
|
||||
}
|
||||
if (ret == ESP_ERR_NOT_FINISHED) {
|
||||
ret = sdio_slave_recv_packet(&handle, 0);
|
||||
if (ret == ESP_ERR_NOT_FINISHED || ret == ESP_OK) {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, ptr, length, ESP_LOG_VERBOSE);
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Error when receiving packet %d", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
static void event_cb(uint8_t pos)
|
||||
{
|
||||
ESP_EARLY_LOGI(TAG, "SDIO event: %d", pos);
|
||||
if (pos == SLAVE_INTR) {
|
||||
s_slave_request = sdio_slave_read_reg(SLAVE_REG_REQ);
|
||||
sdio_slave_write_reg(SLAVE_REG_REQ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
.send_queue_size = BUFFER_NUM,
|
||||
.recv_buffer_size = BUFFER_SIZE,
|
||||
.event_cb = event_cb,
|
||||
};
|
||||
esp_err_t ret = sdio_slave_initialize(&config);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BUFFER_NUM; i++) {
|
||||
sdio_slave_buf_handle_t handle = sdio_slave_recv_register_buf(sdio_slave_rx_buffer[i]);
|
||||
if (handle == NULL) {
|
||||
sdio_slave_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ret = sdio_slave_recv_load_buf(handle);
|
||||
if (ret != ESP_OK) {
|
||||
sdio_slave_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
sdio_slave_set_host_intena(SDIO_SLAVE_HOSTINT_SEND_NEW_PACKET); // only need one interrupt to notify of a new packet
|
||||
|
||||
ret = sdio_slave_start();
|
||||
if (ret != ESP_OK) {
|
||||
sdio_slave_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void eppp_sdio_slave_deinit(void)
|
||||
{
|
||||
sdio_slave_stop();
|
||||
sdio_slave_deinit();
|
||||
}
|
||||
|
||||
#else // SOC_SDIO_SLAVE NOT-SUPPORTED
|
||||
|
||||
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void eppp_sdio_slave_deinit(void)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t eppp_sdio_slave_init(struct eppp_config_sdio_s *config)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
#endif // CONFIG_SOC_SDIO_SLAVE_SUPPORTED
|
||||
495
managed_components/espressif__eppp_link/eppp_spi.c
Normal file
495
managed_components/espressif__eppp_link/eppp_spi.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* 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");
|
||||
ret = gpio_install_isr_service(0);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK || ret == ESP_ERR_INVALID_STATE /* In case the GPIO ISR already installed */,
|
||||
ret, 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);
|
||||
}
|
||||
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
managed_components/espressif__eppp_link/eppp_transport.h
Normal file
29
managed_components/espressif__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);
|
||||
283
managed_components/espressif__eppp_link/eppp_uart.c
Normal file
283
managed_components/espressif__eppp_link/eppp_uart.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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 = config->flow_control;
|
||||
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, config->rts_io, config->cts_io), 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
|
||||
struct eppp_handle *handle = esp_netif_get_io_driver(netif);
|
||||
struct eppp_uart *h = __containerof(handle, struct eppp_uart, parent);
|
||||
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:
|
||||
free(h);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# The following four 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(pppos_host)
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
# Client side demo of ESP-PPP-Link
|
||||
|
||||
This is a basic demo of using esp-mqtt library, but connects to the internet using a PPPoS client. To run this example, you would need a PPP server that provides connectivity to the MQTT broker used in this example (by default a public broker accessible on the internet).
|
||||
|
||||
If configured, this example could also run a ping session and an iperf console.
|
||||
|
||||
|
||||
The PPP server could be a Linux computer with `pppd` service or an ESP32 acting like a connection gateway with PPPoS server (see the "slave" project).
|
||||
@@ -0,0 +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
|
||||
${wifi_over_channels}
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,119 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_GLOBAL_DNS
|
||||
hex "Set global DNS server"
|
||||
range 0 0xFFFFFFFF
|
||||
default 0x08080808
|
||||
help
|
||||
Global DNS server address.
|
||||
|
||||
config EXAMPLE_MQTT
|
||||
bool "Run mqtt example"
|
||||
default y
|
||||
help
|
||||
Run MQTT client after startup.
|
||||
|
||||
config EXAMPLE_BROKER_URL
|
||||
string "Broker URL"
|
||||
depends on EXAMPLE_MQTT
|
||||
default "mqtt://mqtt.eclipseprojects.io"
|
||||
help
|
||||
URL of the broker to connect to.
|
||||
|
||||
config EXAMPLE_IPERF
|
||||
bool "Run iperf"
|
||||
default y
|
||||
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 17
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config EXAMPLE_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 18
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
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
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-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 "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "eppp_link.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "console_ping.h"
|
||||
|
||||
void register_iperf(void);
|
||||
|
||||
static const char *TAG = "eppp_host_example";
|
||||
|
||||
#if CONFIG_EXAMPLE_MQTT
|
||||
static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
esp_mqtt_client_handle_t client = event->client;
|
||||
int msg_id;
|
||||
switch ((esp_mqtt_event_id_t)event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
|
||||
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_app_start(void)
|
||||
{
|
||||
esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
|
||||
};
|
||||
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
|
||||
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(client);
|
||||
}
|
||||
#endif // MQTT
|
||||
|
||||
void station_over_eppp_channel(void *arg);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "[APP] Startup..");
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
|
||||
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* Sets up the default EPPP-connection
|
||||
*/
|
||||
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) {
|
||||
ESP_LOGE(TAG, "Failed to connect");
|
||||
return ;
|
||||
}
|
||||
// Setup global DNS
|
||||
esp_netif_dns_info_t dns;
|
||||
dns.ip.u_addr.ip4.addr = esp_netif_htonl(CONFIG_EXAMPLE_GLOBAL_DNS);
|
||||
dns.ip.type = ESP_IPADDR_TYPE_V4;
|
||||
ESP_ERROR_CHECK(esp_netif_set_dns_info(eppp_netif, ESP_NETIF_DNS_MAIN, &dns));
|
||||
|
||||
// Initialize console REPL
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
|
||||
#if CONFIG_EXAMPLE_IPERF
|
||||
register_iperf();
|
||||
|
||||
printf("\n =======================================================\n");
|
||||
printf(" | Steps to Test EPPP-host bandwidth |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | 1. Wait for the ESP32 to get an IP |\n");
|
||||
printf(" | 2. Server: 'iperf -u -s -i 3' (on host) |\n");
|
||||
printf(" | 3. Client: 'iperf -u -c SERVER_IP -t 60 -i 3' |\n");
|
||||
printf(" | |\n");
|
||||
printf(" =======================================================\n\n");
|
||||
|
||||
#endif // CONFIG_EXAMPLE_IPERF
|
||||
|
||||
// Register the ping command
|
||||
ESP_ERROR_CHECK(console_cmd_ping_register());
|
||||
// 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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
espressif/eppp_link:
|
||||
version: '*'
|
||||
espressif/iperf-cmd: ^0.1.1
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "sys/socket.h" // for INADDR_ANY
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_console.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "iperf.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* "iperf" command */
|
||||
|
||||
static struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_lit *server;
|
||||
struct arg_lit *udp;
|
||||
struct arg_lit *version;
|
||||
struct arg_int *port;
|
||||
struct arg_int *length;
|
||||
struct arg_int *interval;
|
||||
struct arg_int *time;
|
||||
struct arg_int *bw_limit;
|
||||
struct arg_lit *abort;
|
||||
struct arg_end *end;
|
||||
} iperf_args;
|
||||
|
||||
static int ppp_cmd_iperf(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
|
||||
// ethernet iperf only support IPV4 address
|
||||
iperf_cfg_t cfg = {.type = IPERF_IP_TYPE_IPV4};
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, iperf_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf -a */
|
||||
if (iperf_args.abort->count != 0) {
|
||||
iperf_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
|
||||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0))) {
|
||||
ESP_LOGE(__func__, "Wrong mode! ESP32 should run in client or server mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf -s */
|
||||
if (iperf_args.ip->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_SERVER;
|
||||
}
|
||||
/* iperf -c SERVER_ADDRESS */
|
||||
else {
|
||||
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
|
||||
cfg.flag |= IPERF_FLAG_CLIENT;
|
||||
}
|
||||
|
||||
if (iperf_args.length->count == 0) {
|
||||
cfg.len_send_buf = 0;
|
||||
} else {
|
||||
cfg.len_send_buf = iperf_args.length->ival[0];
|
||||
}
|
||||
|
||||
cfg.source_ip4 = INADDR_ANY;
|
||||
|
||||
/* iperf -u */
|
||||
if (iperf_args.udp->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_TCP;
|
||||
} else {
|
||||
cfg.flag |= IPERF_FLAG_UDP;
|
||||
}
|
||||
|
||||
/* iperf -p */
|
||||
if (iperf_args.port->count == 0) {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
if (cfg.flag & IPERF_FLAG_SERVER) {
|
||||
cfg.sport = iperf_args.port->ival[0];
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = iperf_args.port->ival[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -i */
|
||||
if (iperf_args.interval->count == 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
} else {
|
||||
cfg.interval = iperf_args.interval->ival[0];
|
||||
if (cfg.interval <= 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -t */
|
||||
if (iperf_args.time->count == 0) {
|
||||
cfg.time = IPERF_DEFAULT_TIME;
|
||||
} else {
|
||||
cfg.time = iperf_args.time->ival[0];
|
||||
if (cfg.time <= cfg.interval) {
|
||||
cfg.time = cfg.interval;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -b */
|
||||
if (iperf_args.bw_limit->count == 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
} else {
|
||||
cfg.bw_lim = iperf_args.bw_limit->ival[0];
|
||||
if (cfg.bw_lim <= 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
printf("mode=%s-%s sip=" IPSTR ":%" PRIu16 ", dip=%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ":%" PRIu16 ", interval=%" PRIu32 ", time=%" PRIu32 "\r\n",
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
(uint16_t) cfg.source_ip4 & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
|
||||
cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
cfg.interval, cfg.time);
|
||||
|
||||
iperf_start(&cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_iperf(void)
|
||||
{
|
||||
|
||||
iperf_args.ip = arg_str0("c", "client", "<ip>",
|
||||
"run in client mode, connecting to <host>");
|
||||
iperf_args.server = arg_lit0("s", "server", "run in server mode");
|
||||
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
|
||||
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
|
||||
iperf_args.port = arg_int0("p", "port", "<port>",
|
||||
"server port to listen on/connect to");
|
||||
iperf_args.length = arg_int0("l", "len", "<length>", "set read/write buffer size");
|
||||
iperf_args.interval = arg_int0("i", "interval", "<interval>",
|
||||
"seconds between periodic bandwidth reports");
|
||||
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
|
||||
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
|
||||
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
|
||||
iperf_args.end = arg_end(1);
|
||||
const esp_console_cmd_t iperf_cmd = {
|
||||
.command = "iperf",
|
||||
.help = "iperf command",
|
||||
.hint = NULL,
|
||||
.func = &ppp_cmd_iperf,
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
CONFIG_EPPP_LINK_USES_PPP=y
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_EPPP_LINK_DEVICE_ETH=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32p4"
|
||||
CONFIG_EPPP_LINK_DEVICE_SDIO=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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(pppos_slave)
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
# Wi-Fi station to PPPoS server
|
||||
|
||||
This example demonstrate using NAPT to bring connectivity from WiFi station to PPPoS server.
|
||||
|
||||
This example expect a PPPoS client to connect to the server and use the connectivity.
|
||||
The client could be a Linux computer with `pppd` service or another microcontroller with PPP client (or another ESP32 with not WiFi interface)
|
||||
@@ -0,0 +1,6 @@
|
||||
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 ".")
|
||||
@@ -0,0 +1,111 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config ESP_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config ESP_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
config ESP_MAXIMUM_RETRY
|
||||
int "Maximum retry"
|
||||
default 5
|
||||
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 18
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART TX.
|
||||
|
||||
config EXAMPLE_UART_RX_PIN
|
||||
int "RXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 17
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#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 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;
|
||||
|
||||
/* The event group allows multiple bits for each event, but we only care about two events:
|
||||
* - we are connected to the AP with an IP
|
||||
* - we failed to connect after the maximum amount of retries */
|
||||
#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) {
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "retry to connect to the AP");
|
||||
} else {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
ESP_LOGI(TAG, "connect to the AP fail");
|
||||
} else 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));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void init_network_interface(void)
|
||||
{
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
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_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
|
||||
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));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
|
||||
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
|
||||
CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
}
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // SoC WiFi capable chip || WiFi over EPPP channel
|
||||
|
||||
void station_over_eppp_channel(void *arg);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
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)
|
||||
|
||||
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;
|
||||
config.uart.rx_io = CONFIG_EXAMPLE_UART_RX_PIN;
|
||||
config.uart.baud = CONFIG_EXAMPLE_UART_BAUDRATE;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
||||
config.transport = EPPP_TRANSPORT_SDIO;
|
||||
#endif // transport device
|
||||
|
||||
esp_netif_t *eppp_netif = eppp_listen(&config);
|
||||
if (eppp_netif == NULL) {
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
espressif/eppp_link:
|
||||
version: '*'
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s2"
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
CONFIG_EPPP_LINK_CHANNELS_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
CONFIG_EPPP_LINK_USES_PPP=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_EPPP_LINK_DEVICE_ETH=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_LWIP_IPV6=n
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c6"
|
||||
CONFIG_EPPP_LINK_DEVICE_SDIO=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_LWIP_IP_FORWARD=y
|
||||
CONFIG_LWIP_IPV4_NAPT=y
|
||||
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
|
||||
11
managed_components/espressif__eppp_link/idf_component.yml
Normal file
11
managed_components/espressif__eppp_link/idf_component.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
espressif/esp_serial_slave_link: ^1.1.0
|
||||
idf: '>=5.2'
|
||||
description: The component provides a general purpose PPP connectivity, typically
|
||||
used as WiFi-PPP router
|
||||
repository: git://github.com/espressif/esp-protocols.git
|
||||
repository_info:
|
||||
commit_sha: ae052e55076efa401d0c80bce2675ff5933a8ea5
|
||||
path: components/eppp_link
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
|
||||
version: 1.1.3
|
||||
226
managed_components/espressif__eppp_link/include/eppp_link.h
Normal file
226
managed_components/espressif__eppp_link/include/eppp_link.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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_SPI_CONFIG() \
|
||||
.spi = { \
|
||||
.host = 1, \
|
||||
.mosi = 11, \
|
||||
.miso = 13, \
|
||||
.sclk = 12, \
|
||||
.cs = 10, \
|
||||
.intr = 2, \
|
||||
.freq = 16*1000*1000, \
|
||||
.input_delay_ns = 0, \
|
||||
.cs_ena_pretrans = 0, \
|
||||
.cs_ena_posttrans = 0, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_UART_CONFIG() \
|
||||
.uart = { \
|
||||
.port = 1, \
|
||||
.baud = 921600, \
|
||||
.tx_io = 25, \
|
||||
.rx_io = 26, \
|
||||
.queue_size = 16, \
|
||||
.rx_buffer_size = 1024, \
|
||||
.rts_io = -1, \
|
||||
.cts_io = -1, \
|
||||
.flow_control = 0, \
|
||||
}, \
|
||||
|
||||
#define EPPP_DEFAULT_SDIO_CONFIG() \
|
||||
.sdio = { \
|
||||
.width = 4, \
|
||||
.clk = 18, \
|
||||
.cmd = 19, \
|
||||
.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, \
|
||||
.priority = 8, \
|
||||
}, \
|
||||
. ppp = { \
|
||||
.our_ip4_addr = { .addr = our_ip }, \
|
||||
.their_ip4_addr = { .addr = their_ip }, \
|
||||
.netif_prio = 0, \
|
||||
.netif_description = NULL, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define EPPP_DEFAULT_SERVER_CONFIG() EPPP_DEFAULT_CONFIG(EPPP_DEFAULT_SERVER_IP(), EPPP_DEFAULT_CLIENT_IP())
|
||||
#define EPPP_DEFAULT_CLIENT_CONFIG() EPPP_DEFAULT_CONFIG(EPPP_DEFAULT_CLIENT_IP(), EPPP_DEFAULT_SERVER_IP())
|
||||
|
||||
typedef enum eppp_type {
|
||||
EPPP_SERVER,
|
||||
EPPP_CLIENT,
|
||||
} eppp_type_t;
|
||||
|
||||
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;
|
||||
int cs;
|
||||
int intr;
|
||||
int freq;
|
||||
int input_delay_ns;
|
||||
int cs_ena_pretrans;
|
||||
int cs_ena_posttrans;
|
||||
} spi;
|
||||
|
||||
struct eppp_config_uart_s {
|
||||
int port;
|
||||
int baud;
|
||||
int tx_io;
|
||||
int rx_io;
|
||||
int queue_size;
|
||||
int rx_buffer_size;
|
||||
int rts_io;
|
||||
int cts_io;
|
||||
int flow_control;
|
||||
} uart;
|
||||
|
||||
struct eppp_config_sdio_s {
|
||||
bool is_host;
|
||||
int width;
|
||||
int clk;
|
||||
int cmd;
|
||||
int d0;
|
||||
int d1;
|
||||
int d2;
|
||||
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;
|
||||
int priority;
|
||||
} task;
|
||||
|
||||
struct eppp_config_pppos_s {
|
||||
esp_ip4_addr_t our_ip4_addr;
|
||||
esp_ip4_addr_t their_ip4_addr;
|
||||
int netif_prio;
|
||||
const char *netif_description;
|
||||
} ppp;
|
||||
|
||||
} 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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,15 @@
|
||||
# The following four 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)
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "6.0")
|
||||
set(test_component_dir $ENV{IDF_PATH}/tools/test_apps/components)
|
||||
else()
|
||||
set(test_component_dir $ENV{IDF_PATH}/tools/unit-test-app/components)
|
||||
endif()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS ${test_component_dir})
|
||||
|
||||
project(test_app)
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
# Test application running both server and client on the same device
|
||||
|
||||
Need to connect client's Tx to server's Rx and vice versa:
|
||||
GPIO25 - GPIO4
|
||||
GPIO26 - GPIO5
|
||||
|
||||
We wait for the connection and then we start pinging the client's address on server's netif.
|
||||
|
||||
## Example of output:
|
||||
|
||||
```
|
||||
I (393) eppp_test_app: [APP] Startup..
|
||||
I (393) eppp_test_app: [APP] Free memory: 296332 bytes
|
||||
I (393) eppp_test_app: [APP] IDF version: v5.3-dev-1154-gf14d9e7431-dirty
|
||||
I (423) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (423) uart: queue free spaces: 16
|
||||
I (433) eppp_link: Waiting for IP address
|
||||
I (433) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (443) uart: queue free spaces: 16
|
||||
I (443) eppp_link: Waiting for IP address
|
||||
I (6473) esp-netif_lwip-ppp: Connected
|
||||
I (6513) eppp_link: Got IPv4 event: Interface "pppos_client" address: 192.168.11.2
|
||||
I (6523) esp-netif_lwip-ppp: Connected
|
||||
I (6513) eppp_link: Connected!
|
||||
I (6523) eppp_link: Got IPv4 event: Interface "pppos_server" address: 192.168.11.1
|
||||
I (6553) main_task: Returned from app_main()
|
||||
64bytes from 192.168.11.2 icmp_seq=1 ttl=255 time=18 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=2 ttl=255 time=19 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=3 ttl=255 time=19 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=4 ttl=255 time=20 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=5 ttl=255 time=19 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=6 ttl=255 time=19 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=7 ttl=255 time=19 ms
|
||||
From 192.168.11.2 icmp_seq=8 timeout // <-- Disconnected Tx-Rx wires
|
||||
From 192.168.11.2 icmp_seq=9 timeout
|
||||
```
|
||||
## Test cases
|
||||
|
||||
This test app exercises these methods of setting up server-client connection:
|
||||
* simple blocking API (eppp_listen() <--> eppp_connect()): Uses network events internally and waits for connection
|
||||
* simplified non-blocking API (eppp_open(EPPP_SERVER, ...) <--> eppp_open(EPPP_SERVER, ...) ): Uses events internally, optionally waits for connecting
|
||||
* manual API (eppp_init(), eppp_netif_start(), eppp_perform()): User to manually drive Rx task
|
||||
- Note that the ping test for this test case takes longer, since we call perform for both server and client from one task, for example:
|
||||
|
||||
```
|
||||
TEST(eppp_test, open_close_taskless)I (28562) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (28572) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
Note: esp_netif_init() has been called. Until next reset, TCP/IP task will periodicially allocate memory and consume CPU time.
|
||||
I (28602) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (28612) uart: queue free spaces: 16
|
||||
I (28612) uart: ESP_INTR_FLAG_IRAM flag not set while CONFIG_UART_ISR_IN_IRAM is enabled, flag updated
|
||||
I (28622) uart: queue free spaces: 16
|
||||
I (28642) esp-netif_lwip-ppp: Connected
|
||||
I (28642) esp-netif_lwip-ppp: Connected
|
||||
I (28642) test: Got IPv4 event: Interface "pppos_server(EPPP0)" address: 192.168.11.1
|
||||
I (28642) esp-netif_lwip-ppp: Connected
|
||||
I (28652) test: Got IPv4 event: Interface "pppos_client(EPPP1)" address: 192.168.11.2
|
||||
I (28662) esp-netif_lwip-ppp: Connected
|
||||
64bytes from 192.168.11.2 icmp_seq=1 ttl=255 time=93 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=2 ttl=255 time=98 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=3 ttl=255 time=99 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=4 ttl=255 time=99 ms
|
||||
64bytes from 192.168.11.2 icmp_seq=5 ttl=255 time=99 ms
|
||||
5 packets transmitted, 5 received, time 488ms
|
||||
I (29162) esp-netif_lwip-ppp: User interrupt
|
||||
I (29162) test: Disconnected interface "pppos_client(EPPP1)"
|
||||
I (29172) esp-netif_lwip-ppp: User interrupt
|
||||
I (29172) test: Disconnected interface "pppos_server(EPPP0)"
|
||||
MALLOC_CAP_8BIT usage: Free memory delta: 0 Leak threshold: -64
|
||||
MALLOC_CAP_32BIT usage: Free memory delta: 0 Leak threshold: -64
|
||||
PASS
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
|
||||
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
|
||||
else()
|
||||
set(driver_deps driver)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS app_main.c
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES test_utils
|
||||
PRIV_REQUIRES unity nvs_flash esp_netif esp_event ${driver_deps})
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "eppp_link.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "esp_log.h"
|
||||
#include "ping/ping_sock.h"
|
||||
#include "driver/uart.h"
|
||||
#include "test_utils.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "unity_fixture.h"
|
||||
#include "memory_checks.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#define CLIENT_INFO_CONNECTED BIT0
|
||||
#define CLIENT_INFO_DISCONNECT BIT1
|
||||
#define CLIENT_INFO_CLOSED BIT2
|
||||
#define PING_SUCCEEDED BIT3
|
||||
#define PING_FAILED BIT4
|
||||
#define STOP_WORKER_TASK BIT5
|
||||
#define WORKER_TASK_STOPPED BIT6
|
||||
|
||||
TEST_GROUP(eppp_test);
|
||||
TEST_SETUP(eppp_test)
|
||||
{
|
||||
// Perform some open/close operations to disregard lazy init one-time allocations
|
||||
// LWIP: core protection mutex
|
||||
sys_arch_protect();
|
||||
sys_arch_unprotect(0);
|
||||
// UART: install and delete both drivers to disregard potential leak in allocated interrupt slot
|
||||
TEST_ESP_OK(uart_driver_install(UART_NUM_1, 256, 0, 0, NULL, 0));
|
||||
TEST_ESP_OK(uart_driver_delete(UART_NUM_1));
|
||||
TEST_ESP_OK(uart_driver_install(UART_NUM_2, 256, 0, 0, NULL, 0));
|
||||
TEST_ESP_OK(uart_driver_delete(UART_NUM_2));
|
||||
// PING: used for timestamps
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
|
||||
test_utils_record_free_mem();
|
||||
TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL));
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(eppp_test)
|
||||
{
|
||||
test_utils_finish_and_evaluate_leaks(32, 64);
|
||||
}
|
||||
|
||||
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
|
||||
{
|
||||
EventGroupHandle_t event = args;
|
||||
uint32_t transmitted;
|
||||
uint32_t received;
|
||||
uint32_t total_time_ms;
|
||||
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_DURATION, &total_time_ms, sizeof(total_time_ms));
|
||||
printf("%" PRId32 " packets transmitted, %" PRId32 " received, time %" PRId32 "ms\n", transmitted, received, total_time_ms);
|
||||
if (transmitted == received) {
|
||||
xEventGroupSetBits(event, PING_SUCCEEDED);
|
||||
} else {
|
||||
xEventGroupSetBits(event, PING_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_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));
|
||||
printf("%" PRId32 "bytes from %s icmp_seq=%d ttl=%d time=%" PRId32 " ms\n",
|
||||
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
|
||||
}
|
||||
|
||||
struct client_info {
|
||||
esp_netif_t *netif;
|
||||
EventGroupHandle_t event;
|
||||
};
|
||||
|
||||
static void open_client_task(void *ctx)
|
||||
{
|
||||
struct client_info *info = ctx;
|
||||
eppp_config_t config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
config.uart.port = UART_NUM_2;
|
||||
config.uart.tx_io = 4;
|
||||
config.uart.rx_io = 5;
|
||||
|
||||
info->netif = eppp_connect(&config);
|
||||
xEventGroupSetBits(info->event, CLIENT_INFO_CONNECTED);
|
||||
|
||||
// wait for disconnection trigger
|
||||
EventBits_t bits = xEventGroupWaitBits(info->event, CLIENT_INFO_DISCONNECT, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_DISCONNECT, CLIENT_INFO_DISCONNECT);
|
||||
eppp_close(info->netif);
|
||||
xEventGroupSetBits(info->event, CLIENT_INFO_CLOSED);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST(eppp_test, init_deinit)
|
||||
{
|
||||
// Init and deinit server size
|
||||
eppp_config_t config = EPPP_DEFAULT_CONFIG(0, 0);
|
||||
esp_netif_t *netif = eppp_init(EPPP_SERVER, &config);
|
||||
TEST_ASSERT_NOT_NULL(netif);
|
||||
eppp_deinit(netif);
|
||||
netif = NULL;
|
||||
// Init and deinit client size
|
||||
netif = eppp_init(EPPP_CLIENT, &config);
|
||||
TEST_ASSERT_NOT_NULL(netif);
|
||||
eppp_deinit(netif);
|
||||
}
|
||||
|
||||
static EventBits_t ping_test(uint32_t addr, esp_netif_t *netif, EventGroupHandle_t event)
|
||||
{
|
||||
ip_addr_t target_addr = { .type = IPADDR_TYPE_V4, .u_addr.ip4.addr = addr };
|
||||
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
|
||||
ping_config.interval_ms = 100;
|
||||
ping_config.target_addr = target_addr;
|
||||
ping_config.interface = esp_netif_get_netif_impl_index(netif);
|
||||
esp_ping_callbacks_t cbs = { .cb_args = event, .on_ping_end = test_on_ping_end, .on_ping_success = test_on_ping_success };
|
||||
esp_ping_handle_t ping;
|
||||
esp_ping_new_session(&ping_config, &cbs, &ping);
|
||||
esp_ping_start(ping);
|
||||
// Wait for the client thread closure and delete locally created objects
|
||||
EventBits_t bits = xEventGroupWaitBits(event, PING_SUCCEEDED | PING_FAILED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
|
||||
esp_ping_stop(ping);
|
||||
esp_ping_delete_session(ping);
|
||||
return bits;
|
||||
}
|
||||
|
||||
TEST(eppp_test, open_close)
|
||||
{
|
||||
test_case_uses_tcpip();
|
||||
|
||||
eppp_config_t config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
struct client_info client = { .netif = NULL, .event = xEventGroupCreate()};
|
||||
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
|
||||
TEST_ASSERT_NOT_NULL(client.event);
|
||||
|
||||
// Need to connect the client in a separate thread, as the simplified API blocks until connection
|
||||
xTaskCreate(open_client_task, "client_task", 4096, &client, 5, NULL);
|
||||
|
||||
// Now start the server
|
||||
esp_netif_t *eppp_server = eppp_listen(&config);
|
||||
|
||||
// Wait for the client to connect
|
||||
EventBits_t bits = xEventGroupWaitBits(client.event, CLIENT_INFO_CONNECTED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_CONNECTED, CLIENT_INFO_CONNECTED);
|
||||
|
||||
// Check that both server and client are valid netif pointers
|
||||
TEST_ASSERT_NOT_NULL(eppp_server);
|
||||
TEST_ASSERT_NOT_NULL(client.netif);
|
||||
|
||||
// Now that we're connected, let's try to ping clients address
|
||||
bits = ping_test(config.ppp.their_ip4_addr.addr, eppp_server, client.event);
|
||||
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
|
||||
|
||||
// Trigger client disconnection and close the server
|
||||
xEventGroupSetBits(client.event, CLIENT_INFO_DISCONNECT);
|
||||
eppp_close(eppp_server);
|
||||
|
||||
// Wait for the client thread closure and delete locally created objects
|
||||
bits = xEventGroupWaitBits(client.event, CLIENT_INFO_CLOSED, pdFALSE, pdFALSE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & CLIENT_INFO_CLOSED, CLIENT_INFO_CLOSED);
|
||||
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
vEventGroupDelete(client.event);
|
||||
|
||||
// wait for the lwip sockets to close cleanly
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
static void on_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
EventGroupHandle_t event = arg;
|
||||
if (base == IP_EVENT && event_id == IP_EVENT_PPP_GOT_IP) {
|
||||
ip_event_got_ip_t *e = (ip_event_got_ip_t *)data;
|
||||
esp_netif_t *netif = e->esp_netif;
|
||||
ESP_LOGI("test", "Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
|
||||
esp_netif_get_ifkey(netif), IP2STR(&e->ip_info.ip));
|
||||
if (strcmp("pppos_server", esp_netif_get_desc(netif)) == 0) {
|
||||
xEventGroupSetBits(event, 1 << EPPP_SERVER);
|
||||
} else if (strcmp("pppos_client", esp_netif_get_desc(netif)) == 0) {
|
||||
xEventGroupSetBits(event, 1 << EPPP_CLIENT);
|
||||
}
|
||||
} else if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
|
||||
esp_netif_t **netif = data;
|
||||
ESP_LOGI("test", "Disconnected interface \"%s(%s)\"", esp_netif_get_desc(*netif), esp_netif_get_ifkey(*netif));
|
||||
if (strcmp("pppos_server", esp_netif_get_desc(*netif)) == 0) {
|
||||
xEventGroupSetBits(event, 1 << EPPP_SERVER);
|
||||
} else if (strcmp("pppos_client", esp_netif_get_desc(*netif)) == 0) {
|
||||
xEventGroupSetBits(event, 1 << EPPP_CLIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(eppp_test, open_close_nonblocking)
|
||||
{
|
||||
test_case_uses_tcpip();
|
||||
EventGroupHandle_t event = xEventGroupCreate();
|
||||
|
||||
eppp_config_t server_config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
|
||||
// Open the server size
|
||||
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_event, event));
|
||||
esp_netif_t *eppp_server = eppp_open(EPPP_SERVER, &server_config, 0);
|
||||
TEST_ASSERT_NOT_NULL(eppp_server);
|
||||
// Open the client size
|
||||
eppp_config_t client_config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
client_config.uart.port = UART_NUM_2;
|
||||
client_config.uart.tx_io = 4;
|
||||
client_config.uart.rx_io = 5;
|
||||
esp_netif_t *eppp_client = eppp_open(EPPP_CLIENT, &client_config, 0);
|
||||
TEST_ASSERT_NOT_NULL(eppp_client);
|
||||
const EventBits_t wait_bits = (1 << EPPP_SERVER) | (1 << EPPP_CLIENT);
|
||||
EventBits_t bits = xEventGroupWaitBits(event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
|
||||
|
||||
// Now that we're connected, let's try to ping clients address
|
||||
bits = ping_test(server_config.ppp.their_ip4_addr.addr, eppp_server, event);
|
||||
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
|
||||
|
||||
// stop network for both client and server
|
||||
eppp_netif_stop(eppp_client, 0); // ignore result, since we're not waiting for clean close
|
||||
eppp_close(eppp_server);
|
||||
eppp_close(eppp_client); // finish client close
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
vEventGroupDelete(event);
|
||||
|
||||
// wait for the lwip sockets to close cleanly
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
|
||||
struct worker {
|
||||
esp_netif_t *eppp_server;
|
||||
esp_netif_t *eppp_client;
|
||||
EventGroupHandle_t event;
|
||||
};
|
||||
|
||||
static void worker_task(void *ctx)
|
||||
{
|
||||
struct worker *info = ctx;
|
||||
while (1) {
|
||||
eppp_perform(info->eppp_server);
|
||||
eppp_perform(info->eppp_client);
|
||||
if (xEventGroupGetBits(info->event) & STOP_WORKER_TASK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
xEventGroupSetBits(info->event, WORKER_TASK_STOPPED);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST(eppp_test, open_close_taskless)
|
||||
{
|
||||
test_case_uses_tcpip();
|
||||
struct worker info = { .event = xEventGroupCreate() };
|
||||
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_event, info.event));
|
||||
TEST_ESP_OK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_event, info.event));
|
||||
|
||||
// Create server
|
||||
eppp_config_t server_config = EPPP_DEFAULT_SERVER_CONFIG();
|
||||
info.eppp_server = eppp_init(EPPP_SERVER, &server_config);
|
||||
TEST_ASSERT_NOT_NULL(info.eppp_server);
|
||||
// Create client
|
||||
eppp_config_t client_config = EPPP_DEFAULT_CLIENT_CONFIG();
|
||||
client_config.uart.port = UART_NUM_2;
|
||||
client_config.uart.tx_io = 4;
|
||||
client_config.uart.rx_io = 5;
|
||||
info.eppp_client = eppp_init(EPPP_CLIENT, &client_config);
|
||||
TEST_ASSERT_NOT_NULL(info.eppp_client);
|
||||
// Start workers
|
||||
xTaskCreate(worker_task, "worker", 4096, &info, 5, NULL);
|
||||
// Start network
|
||||
TEST_ESP_OK(eppp_netif_start(info.eppp_server));
|
||||
TEST_ESP_OK(eppp_netif_start(info.eppp_client));
|
||||
|
||||
const EventBits_t wait_bits = (1 << EPPP_SERVER) | (1 << EPPP_CLIENT);
|
||||
EventBits_t bits = xEventGroupWaitBits(info.event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
|
||||
xEventGroupClearBits(info.event, wait_bits);
|
||||
|
||||
// Now that we're connected, let's try to ping clients address
|
||||
bits = ping_test(server_config.ppp.their_ip4_addr.addr, info.eppp_server, info.event);
|
||||
TEST_ASSERT_EQUAL(bits & (PING_SUCCEEDED | PING_FAILED), PING_SUCCEEDED);
|
||||
|
||||
// stop network for both client and server, we won't wait for completion so expecting ESP_FAIL
|
||||
TEST_ASSERT_EQUAL(eppp_netif_stop(info.eppp_client, 0), ESP_FAIL);
|
||||
TEST_ASSERT_EQUAL(eppp_netif_stop(info.eppp_server, 0), ESP_FAIL);
|
||||
// and wait for completion
|
||||
bits = xEventGroupWaitBits(info.event, wait_bits, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & wait_bits, wait_bits);
|
||||
|
||||
// now stop the worker
|
||||
xEventGroupSetBits(info.event, STOP_WORKER_TASK);
|
||||
bits = xEventGroupWaitBits(info.event, WORKER_TASK_STOPPED, pdTRUE, pdTRUE, pdMS_TO_TICKS(50000));
|
||||
TEST_ASSERT_EQUAL(bits & WORKER_TASK_STOPPED, WORKER_TASK_STOPPED);
|
||||
|
||||
// and destroy objects
|
||||
eppp_deinit(info.eppp_server);
|
||||
eppp_deinit(info.eppp_client);
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
vEventGroupDelete(info.event);
|
||||
|
||||
// wait for the lwip sockets to close cleanly
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
|
||||
TEST_GROUP_RUNNER(eppp_test)
|
||||
{
|
||||
RUN_TEST_CASE(eppp_test, init_deinit)
|
||||
RUN_TEST_CASE(eppp_test, open_close)
|
||||
RUN_TEST_CASE(eppp_test, open_close_nonblocking)
|
||||
RUN_TEST_CASE(eppp_test, open_close_taskless)
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
UNITY_MAIN(eppp_test);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
espressif/eppp_link:
|
||||
version: "*"
|
||||
override_path: "../../.."
|
||||
@@ -0,0 +1,9 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
CONFIG_UART_ISR_IN_IRAM=y
|
||||
CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=0
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
CONFIG_LWIP_PPP_DEBUG_ON=y
|
||||
CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
Reference in New Issue
Block a user