mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-08-14 01:54:29 +02:00
Merge pull request #462 from david-cermak/feat/eppp_link
New component ESP-PPP-Link
This commit is contained in:
28
.github/workflows/eppp__build.yml
vendored
Normal file
28
.github/workflows/eppp__build.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: "eppp_link: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_eppp:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'eppp') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
${IDF_PATH}/install.sh --enable-pytest
|
||||
. ${IDF_PATH}/export.sh
|
||||
python ./ci/build_apps.py ./components/eppp_link/${{matrix.test.path}} -vv --preserve-all
|
1
.github/workflows/publish-docs-component.yml
vendored
1
.github/workflows/publish-docs-component.yml
vendored
@@ -92,6 +92,7 @@ jobs:
|
||||
components/esp_modem;
|
||||
components/esp_mqtt_cxx;
|
||||
components/esp_websocket_client;
|
||||
components/eppp_link;
|
||||
components/mdns;
|
||||
components/console_simple_init;
|
||||
components/console_cmd_ping;
|
||||
|
@@ -61,8 +61,8 @@ repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit message scopes
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples)\)\:)'
|
||||
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp)\)\:)'
|
||||
language: pygrep
|
||||
args: [--multiline]
|
||||
stages: [commit-msg]
|
||||
|
@@ -49,3 +49,7 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
|
||||
### console_cmd_wifi
|
||||
|
||||
* Brief introduction [README](components/console_cmd_wifi/README.md)
|
||||
|
||||
### ESP PPP Link (eppp)
|
||||
|
||||
* Brief introduction [README](components/eppp_link/README.md)
|
||||
|
8
components/eppp_link/.cz.yaml
Normal file
8
components/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: 0.0.1
|
||||
version_files:
|
||||
- idf_component.yml
|
10
components/eppp_link/CHANGELOG.md
Normal file
10
components/eppp_link/CHANGELOG.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## [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))
|
3
components/eppp_link/CMakeLists.txt
Normal file
3
components/eppp_link/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "eppp_link.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES esp_netif esp_driver_spi esp_driver_gpio esp_timer driver)
|
35
components/eppp_link/Kconfig
Normal file
35
components/eppp_link/Kconfig
Normal file
@@ -0,0 +1,35 @@
|
||||
menu "eppp_link"
|
||||
|
||||
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.
|
||||
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
|
||||
help
|
||||
Size of the Tx packet queue.
|
||||
You can decrease the number for slower bit rates.
|
||||
|
||||
endmenu
|
202
components/eppp_link/LICENSE
Normal file
202
components/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.
|
52
components/eppp_link/README.md
Normal file
52
components/eppp_link/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# ESP PPP Link component (eppp_link)
|
||||
|
||||
The component provides a general purpose connectivity engine between two microcontrollers, one acting as PPP server (slave), the other one as PPP client (host).
|
||||
This component could be used for extending network using physical serial connection. Applications could vary from providing PRC engine for multiprocessor solutions to serial connection to POSIX machine. This uses a standard PPP protocol to negotiate IP addresses and networking, so standard PPP toolset could be used, e.g. a `pppd` service on linux. Typical application is a WiFi connectivity provider for chips that do not have WiFi
|
||||
|
||||
## Typical application
|
||||
|
||||
Using this component we can construct a WiFi connectivity gateway on PPP channel. The below diagram depicts an application where
|
||||
PPP server is running on a WiFi capable chip with NAPT module translating packets between WiFi and PPPoS interface.
|
||||
We usually call this node a SLAVE microcontroller. The "HOST" microcontroller runs PPP client and connects only to the serial line,
|
||||
brings in the WiFi connectivity from the "SLAVE" microcontroller.
|
||||
|
||||
```
|
||||
SLAVE micro HOST micro
|
||||
\|/ +----------------+ +----------------+
|
||||
| | | serial line | |
|
||||
+---+ WiFi NAT PPPoS |======== UART / SPI =======| PPPoS client |
|
||||
| (server)| | |
|
||||
+----------------+ +----------------+
|
||||
```
|
||||
|
||||
## 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)
|
||||
|
||||
## Throughput
|
||||
|
||||
Tested with WiFi-NAPT example, no IRAM optimizations
|
||||
|
||||
### UART @ 3Mbauds
|
||||
|
||||
* TCP - 2Mbits/s
|
||||
* UDP - 2Mbits/s
|
||||
|
||||
### SPI @ 20MHz
|
||||
|
||||
* TCP - 6Mbits/s
|
||||
* UDP - 10Mbits/s
|
820
components/eppp_link/eppp_link.c
Normal file
820
components/eppp_link/eppp_link.c
Normal file
@@ -0,0 +1,820 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2024 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"
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_rom_crc.h"
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
#include "driver/uart.h"
|
||||
#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
|
||||
|
||||
|
||||
struct packet {
|
||||
size_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
#define MAX_PAYLOAD 1500
|
||||
#define MIN_TRIGGER_US 20
|
||||
#define SPI_HEADER_MAGIC 0x1234
|
||||
|
||||
static void timer_callback(void *arg);
|
||||
|
||||
struct header {
|
||||
uint16_t magic;
|
||||
uint16_t size;
|
||||
uint16_t next_size;
|
||||
uint16_t check;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum blocked_status {
|
||||
NONE,
|
||||
MASTER_BLOCKED,
|
||||
MASTER_WANTS_READ,
|
||||
SLAVE_BLOCKED,
|
||||
SLAVE_WANTS_WRITE,
|
||||
};
|
||||
|
||||
#endif // CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
|
||||
struct eppp_handle {
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
QueueHandle_t out_queue;
|
||||
QueueHandle_t ready_semaphore;
|
||||
spi_device_handle_t spi_device;
|
||||
spi_host_device_t spi_host;
|
||||
int gpio_intr;
|
||||
uint16_t next_size;
|
||||
uint16_t transaction_size;
|
||||
struct packet outbound;
|
||||
enum blocked_status blocked;
|
||||
uint32_t slave_last_edge;
|
||||
esp_timer_handle_t timer;
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
QueueHandle_t uart_event_queue;
|
||||
uart_port_t uart_port;
|
||||
#endif
|
||||
esp_netif_t *netif;
|
||||
eppp_type_t role;
|
||||
bool stop;
|
||||
bool exited;
|
||||
bool netif_stop;
|
||||
};
|
||||
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
struct eppp_handle *handle = h;
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct packet buf = { };
|
||||
uint8_t *current_buffer = buffer;
|
||||
size_t remaining = len;
|
||||
do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform
|
||||
// fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD)
|
||||
size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining;
|
||||
buf.data = malloc(batch);
|
||||
if (buf.data == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate packet");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
buf.len = batch;
|
||||
remaining -= batch;
|
||||
memcpy(buf.data, current_buffer, batch);
|
||||
current_buffer += batch;
|
||||
BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0);
|
||||
if (ret != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to queue packet to slave!");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
} while (remaining > 0);
|
||||
|
||||
if (handle->role == EPPP_SERVER && handle->blocked == SLAVE_BLOCKED) {
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - handle->slave_last_edge;
|
||||
if (diff < MIN_TRIGGER_US) {
|
||||
esp_rom_delay_us(MIN_TRIGGER_US - diff);
|
||||
}
|
||||
gpio_set_level(handle->gpio_intr, 0);
|
||||
}
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_VERBOSE);
|
||||
uart_write_bytes(handle->uart_port, buffer, len);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void netif_deinit(esp_netif_t *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h == NULL) {
|
||||
return;
|
||||
}
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct packet buf = { };
|
||||
while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) {
|
||||
if (buf.len > 0) {
|
||||
free(buf.data);
|
||||
}
|
||||
}
|
||||
vQueueDelete(h->out_queue);
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
#endif
|
||||
free(h);
|
||||
esp_netif_destroy(netif);
|
||||
if (s_eppp_netif_count > 0) {
|
||||
s_eppp_netif_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_netif_t *netif_init(eppp_type_t role)
|
||||
{
|
||||
if (s_eppp_netif_count > 9) { // Limit to max 10 netifs, since we use "EPPPx" as the unique key (where x is 0-9)
|
||||
ESP_LOGE(TAG, "Cannot create more than 10 instances");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create the object first
|
||||
struct eppp_handle *h = calloc(1, sizeof(struct eppp_handle));
|
||||
if (!h) {
|
||||
ESP_LOGE(TAG, "Failed to allocate eppp_handle");
|
||||
return NULL;
|
||||
}
|
||||
h->role = role;
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet));
|
||||
if (!h->out_queue) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
if (role == EPPP_CLIENT) {
|
||||
h->ready_semaphore = xSemaphoreCreateBinary();
|
||||
if (!h->ready_semaphore) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
vQueueDelete(h->out_queue);
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
h->transaction_size = 0;
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
if (role == EPPP_SERVER) {
|
||||
esp_timer_create_args_t args = {
|
||||
.callback = &timer_callback,
|
||||
.arg = h,
|
||||
.name = "timer"
|
||||
};
|
||||
if (esp_timer_create(&args, &h->timer) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to create the packet queue");
|
||||
vQueueDelete(h->out_queue);
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = h,
|
||||
.transmit = transmit,
|
||||
};
|
||||
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
|
||||
|
||||
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
|
||||
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 (role == EPPP_CLIENT) {
|
||||
base_netif_cfg.if_desc = "pppos_client";
|
||||
} else {
|
||||
base_netif_cfg.if_desc = "pppos_server";
|
||||
}
|
||||
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
|
||||
.driver = ppp_driver_cfg,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
|
||||
};
|
||||
|
||||
esp_netif_t *netif = esp_netif_new(&netif_ppp_config);
|
||||
if (!netif) {
|
||||
ESP_LOGE(TAG, "Failed to create esp_netif");
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
vQueueDelete(h->out_queue);
|
||||
if (h->ready_semaphore) {
|
||||
vSemaphoreDelete(h->ready_semaphore);
|
||||
}
|
||||
#endif
|
||||
free(h);
|
||||
return NULL;
|
||||
}
|
||||
return netif;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
||||
{
|
||||
esp_netif_t **netif = data;
|
||||
ESP_LOGD(TAG, "PPP status event: %" PRId32, event_id);
|
||||
if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
|
||||
ESP_LOGI(TAG, "Disconnected %d", get_netif_num(*netif));
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(*netif);
|
||||
h->netif_stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
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 CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
|
||||
#define SPI_ALIGN(size) (((size) + 3U) & ~(3U))
|
||||
#define TRANSFER_SIZE SPI_ALIGN((MAX_PAYLOAD + 6))
|
||||
#define NEXT_TRANSACTION_SIZE(a,b) (((a)>(b))?(a):(b)) /* next transaction: whichever is bigger */
|
||||
|
||||
static void IRAM_ATTR timer_callback(void *arg)
|
||||
{
|
||||
struct eppp_handle *h = arg;
|
||||
if (h->blocked == SLAVE_WANTS_WRITE) {
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void *arg)
|
||||
{
|
||||
static uint32_t s_last_time;
|
||||
uint32_t now = esp_timer_get_time();
|
||||
uint32_t diff = now - s_last_time;
|
||||
if (diff < MIN_TRIGGER_US) { // debounce
|
||||
return;
|
||||
}
|
||||
s_last_time = now;
|
||||
struct eppp_handle *h = arg;
|
||||
BaseType_t yield = false;
|
||||
|
||||
// Positive edge means SPI slave prepared the data
|
||||
if (gpio_get_level(h->gpio_intr) == 1) {
|
||||
xSemaphoreGiveFromISR(h->ready_semaphore, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Negative edge (when master blocked) means that slave wants to transmit
|
||||
if (h->blocked == MASTER_BLOCKED) {
|
||||
struct packet buf = { .data = NULL, .len = -1 };
|
||||
xQueueSendFromISR(h->out_queue, &buf, &yield);
|
||||
if (yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t deinit_master(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI bus");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_master(struct eppp_config_spi_s *config, esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.max_transfer_sz = TRANSFER_SIZE;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
// TODO: Init and deinit SPI bus separately (per Kconfig?)
|
||||
if (spi_bus_initialize(config->host, &bus_cfg, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
spi_device_interface_config_t dev_cfg = {};
|
||||
dev_cfg.clock_speed_hz = config->freq;
|
||||
dev_cfg.mode = 0;
|
||||
dev_cfg.spics_io_num = config->cs;
|
||||
dev_cfg.cs_ena_pretrans = config->cs_ena_pretrans;
|
||||
dev_cfg.cs_ena_posttrans = config->cs_ena_posttrans;
|
||||
dev_cfg.duty_cycle_pos = 128;
|
||||
dev_cfg.input_delay_ns = config->input_delay_ns;
|
||||
dev_cfg.pre_cb = NULL;
|
||||
dev_cfg.post_cb = NULL;
|
||||
dev_cfg.queue_size = 3;
|
||||
|
||||
if (spi_bus_add_device(config->host, &dev_cfg, &h->spi_device) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//GPIO config for the handshake line.
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_ANYEDGE,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(0);
|
||||
gpio_set_intr_type(config->intr, GPIO_INTR_ANYEDGE);
|
||||
gpio_isr_handler_add(config->intr, gpio_isr_handler, esp_netif_get_io_driver(netif));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void post_setup(spi_slave_transaction_t *trans)
|
||||
{
|
||||
struct eppp_handle *h = trans->user;
|
||||
h->slave_last_edge = esp_timer_get_time();
|
||||
gpio_set_level(h->gpio_intr, 1);
|
||||
if (h->transaction_size == 0) { // If no transaction planned:
|
||||
if (h->outbound.len == 0) { // we're blocked if we don't have any data
|
||||
h->blocked = SLAVE_BLOCKED;
|
||||
} else {
|
||||
h->blocked = SLAVE_WANTS_WRITE; // we notify the master that we want to write
|
||||
esp_timer_start_once(h->timer, MIN_TRIGGER_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void post_trans(spi_slave_transaction_t *trans)
|
||||
{
|
||||
struct eppp_handle *h = trans->user;
|
||||
h->blocked = NONE;
|
||||
gpio_set_level(h->gpio_intr, 0);
|
||||
}
|
||||
|
||||
static esp_err_t deinit_slave(esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
ESP_RETURN_ON_ERROR(spi_slave_free(h->spi_host), TAG, "Failed to free SPI slave host");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(h->spi_device), TAG, "Failed to remove SPI device");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(h->spi_host), TAG, "Failed to free SPI bus");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_slave(struct eppp_config_spi_s *config, esp_netif_t *netif)
|
||||
{
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
h->spi_host = config->host;
|
||||
h->gpio_intr = config->intr;
|
||||
spi_bus_config_t bus_cfg = {};
|
||||
bus_cfg.mosi_io_num = config->mosi;
|
||||
bus_cfg.miso_io_num = config->miso;
|
||||
bus_cfg.sclk_io_num = config->sclk;
|
||||
bus_cfg.quadwp_io_num = -1;
|
||||
bus_cfg.quadhd_io_num = -1;
|
||||
bus_cfg.flags = 0;
|
||||
bus_cfg.intr_flags = 0;
|
||||
|
||||
//Configuration for the SPI slave interface
|
||||
spi_slave_interface_config_t slvcfg = {
|
||||
.mode = 0,
|
||||
.spics_io_num = config->cs,
|
||||
.queue_size = 3,
|
||||
.flags = 0,
|
||||
.post_setup_cb = post_setup,
|
||||
.post_trans_cb = post_trans,
|
||||
};
|
||||
|
||||
//Configuration for the handshake line
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = BIT64(config->intr),
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_pull_mode(config->mosi, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->sclk, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(config->cs, GPIO_PULLUP_ONLY);
|
||||
|
||||
//Initialize SPI slave interface
|
||||
if (spi_slave_initialize(config->host, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef esp_err_t (*perform_transaction_t)(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer);
|
||||
|
||||
static esp_err_t perform_transaction_master(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_transaction_t t = {};
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_device_transmit(h->spi_device, &t);
|
||||
}
|
||||
|
||||
static esp_err_t perform_transaction_slave(struct eppp_handle *h, size_t len, const void *tx_buffer, void *rx_buffer)
|
||||
{
|
||||
spi_slave_transaction_t t = {};
|
||||
t.user = h;
|
||||
t.length = len * 8;
|
||||
t.tx_buffer = tx_buffer;
|
||||
t.rx_buffer = rx_buffer;
|
||||
return spi_slave_transmit(h->spi_host, &t, portMAX_DELAY);
|
||||
}
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
static WORD_ALIGNED_ATTR uint8_t out_buf[TRANSFER_SIZE] = {};
|
||||
static WORD_ALIGNED_ATTR uint8_t in_buf[TRANSFER_SIZE] = {};
|
||||
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
|
||||
// Perform transaction for master and slave
|
||||
const perform_transaction_t perform_transaction = h->role == EPPP_CLIENT ? perform_transaction_master : perform_transaction_slave;
|
||||
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
BaseType_t tx_queue_stat;
|
||||
bool allow_test_tx = false;
|
||||
uint16_t next_tx_size = 0;
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
// SPI MASTER only code
|
||||
if (xSemaphoreTake(h->ready_semaphore, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
// slave might not be ready, but maybe we just missed an interrupt
|
||||
allow_test_tx = true;
|
||||
}
|
||||
if (h->outbound.len == 0 && h->transaction_size == 0 && h->blocked == NONE) {
|
||||
h->blocked = MASTER_BLOCKED;
|
||||
xQueueReceive(h->out_queue, &h->outbound, portMAX_DELAY);
|
||||
h->blocked = NONE;
|
||||
if (h->outbound.len == -1) {
|
||||
h->outbound.len = 0;
|
||||
h->blocked = MASTER_WANTS_READ;
|
||||
}
|
||||
} else if (h->blocked == MASTER_WANTS_READ) {
|
||||
h->blocked = NONE;
|
||||
}
|
||||
}
|
||||
struct header *head = (void *)out_buf;
|
||||
if (h->outbound.len <= h->transaction_size && allow_test_tx == false) {
|
||||
// sending outbound
|
||||
head->size = h->outbound.len;
|
||||
if (h->outbound.len > 0) {
|
||||
memcpy(out_buf + sizeof(struct header), h->outbound.data, h->outbound.len);
|
||||
free(h->outbound.data);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, out_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
h->outbound.data = NULL;
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
do {
|
||||
tx_queue_stat = xQueueReceive(h->out_queue, &h->outbound, 0);
|
||||
} while (tx_queue_stat == pdTRUE && h->outbound.len == -1);
|
||||
if (h->outbound.len == -1) { // used as a signal only, no actual data
|
||||
h->outbound.len = 0;
|
||||
}
|
||||
} else {
|
||||
// outbound is bigger, need to transmit in another transaction (keep this empty)
|
||||
head->size = 0;
|
||||
}
|
||||
next_tx_size = head->next_size = h->outbound.len;
|
||||
head->magic = SPI_HEADER_MAGIC;
|
||||
head->check = esp_rom_crc16_le(0, out_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
esp_err_t ret = perform_transaction(h, sizeof(struct header) + h->transaction_size, out_buf, in_buf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "spi_device_transmit failed");
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
return ESP_FAIL;
|
||||
}
|
||||
head = (void *)in_buf;
|
||||
uint16_t check = esp_rom_crc16_le(0, in_buf, sizeof(struct header) - sizeof(uint16_t));
|
||||
if (check != head->check || head->magic != SPI_HEADER_MAGIC) {
|
||||
h->transaction_size = 0; // need to start with HEADER only transaction
|
||||
if (allow_test_tx) {
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "Wrong checksum or magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (head->size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, in_buf + sizeof(struct header), head->size, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, in_buf + sizeof(struct header), head->size, NULL);
|
||||
}
|
||||
h->transaction_size = NEXT_TRANSACTION_SIZE(next_tx_size, head->next_size);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
#define BUF_SIZE (1024)
|
||||
|
||||
static esp_err_t init_uart(struct eppp_handle *h, eppp_config_t *config)
|
||||
{
|
||||
h->uart_port = config->uart.port;
|
||||
uart_config_t uart_config = {};
|
||||
uart_config.baud_rate = config->uart.baud;
|
||||
uart_config.data_bits = UART_DATA_8_BITS;
|
||||
uart_config.parity = UART_PARITY_DISABLE;
|
||||
uart_config.stop_bits = UART_STOP_BITS_1;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_DEFAULT;
|
||||
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(h->uart_port, config->uart.rx_buffer_size, 0, config->uart.queue_size, &h->uart_event_queue, 0), TAG, "Failed to install UART");
|
||||
ESP_RETURN_ON_ERROR(uart_param_config(h->uart_port, &uart_config), TAG, "Failed to set params");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(h->uart_port, config->uart.tx_io, config->uart.rx_io, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "Failed to set UART pins");
|
||||
ESP_RETURN_ON_ERROR(uart_set_rx_timeout(h->uart_port, 1), TAG, "Failed to set UART Rx timeout");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void deinit_uart(struct eppp_handle *h)
|
||||
{
|
||||
uart_driver_delete(h->uart_port);
|
||||
}
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif)
|
||||
{
|
||||
static uint8_t buffer[BUF_SIZE] = {};
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
uart_event_t event = {};
|
||||
if (h->stop) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (xQueueReceive(h->uart_event_queue, &event, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (event.type == UART_DATA) {
|
||||
size_t len;
|
||||
uart_get_buffered_data_len(h->uart_port, &len);
|
||||
if (len) {
|
||||
len = uart_read_bytes(h->uart_port, buffer, BUF_SIZE, 0);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(netif, buffer, len, NULL);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Received UART event: %d", event.type);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 cleanup the statics
|
||||
vEventGroupDelete(s_event_group);
|
||||
s_event_group = NULL;
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event);
|
||||
esp_event_handler_unregister(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event);
|
||||
}
|
||||
}
|
||||
|
||||
void eppp_deinit(esp_netif_t *netif)
|
||||
{
|
||||
if (netif == NULL) {
|
||||
return;
|
||||
}
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
||||
if (h->role == EPPP_CLIENT) {
|
||||
deinit_master(netif);
|
||||
} else {
|
||||
deinit_slave(netif);
|
||||
}
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
deinit_uart(esp_netif_get_io_driver(netif));
|
||||
#endif
|
||||
netif_deinit(netif);
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config)
|
||||
{
|
||||
esp_netif_t *netif = netif_init(role);
|
||||
if (!netif) {
|
||||
ESP_LOGE(TAG, "Failed to initialize PPP netif");
|
||||
remove_handlers();
|
||||
return NULL;
|
||||
}
|
||||
esp_netif_ppp_config_t netif_params;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_get_params(netif, &netif_params));
|
||||
netif_params.ppp_our_ip4_addr = config->ppp.our_ip4_addr;
|
||||
netif_params.ppp_their_ip4_addr = config->ppp.their_ip4_addr;
|
||||
netif_params.ppp_error_event_enabled = true;
|
||||
ESP_ERROR_CHECK(esp_netif_ppp_set_params(netif, &netif_params));
|
||||
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
||||
if (role == EPPP_CLIENT) {
|
||||
init_master(&config->spi, netif);
|
||||
} else {
|
||||
init_slave(&config->spi, netif);
|
||||
|
||||
}
|
||||
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
||||
init_uart(esp_netif_get_io_driver(netif), config);
|
||||
#endif
|
||||
return netif;
|
||||
}
|
||||
|
||||
esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms)
|
||||
{
|
||||
#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->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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
esp_netif_t *netif = eppp_init(role, config);
|
||||
if (!netif) {
|
||||
remove_handlers();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
eppp_netif_start(netif);
|
||||
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
8
components/eppp_link/examples/host/CMakeLists.txt
Normal file
8
components/eppp_link/examples/host/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# 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)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/iperf)
|
||||
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pppos_host)
|
9
components/eppp_link/examples/host/README.md
Normal file
9
components/eppp_link/examples/host/README.md
Normal file
@@ -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).
|
2
components/eppp_link/examples/host/main/CMakeLists.txt
Normal file
2
components/eppp_link/examples/host/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS app_main.c register_iperf.c
|
||||
INCLUDE_DIRS ".")
|
53
components/eppp_link/examples/host/main/Kconfig.projbuild
Normal file
53
components/eppp_link/examples/host/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,53 @@
|
||||
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_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 10
|
||||
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 11
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 2000000
|
||||
range 0 4000000
|
||||
help
|
||||
Baudrate used by the PPP over UART
|
||||
|
||||
endmenu
|
149
components/eppp_link/examples/host/main/app_main.c
Normal file
149
components/eppp_link/examples/host/main/app_main.c
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 <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 "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 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;
|
||||
#else
|
||||
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;
|
||||
#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());
|
||||
|
||||
#if CONFIG_EXAMPLE_MQTT
|
||||
mqtt_app_start();
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
dependencies:
|
||||
espressif/eppp_link:
|
||||
version: "*"
|
||||
override_path: "../../.."
|
||||
console_cmd_ping:
|
||||
version: "*"
|
179
components/eppp_link/examples/host/main/register_iperf.c
Normal file
179
components/eppp_link/examples/host/main/register_iperf.c
Normal file
@@ -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));
|
||||
}
|
1
components/eppp_link/examples/host/sdkconfig.ci.spi
Normal file
1
components/eppp_link/examples/host/sdkconfig.ci.spi
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
1
components/eppp_link/examples/host/sdkconfig.ci.uart
Normal file
1
components/eppp_link/examples/host/sdkconfig.ci.uart
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
4
components/eppp_link/examples/host/sdkconfig.defaults
Normal file
4
components/eppp_link/examples/host/sdkconfig.defaults
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
||||
CONFIG_LWIP_PPP_DEBUG_ON=y
|
6
components/eppp_link/examples/slave/CMakeLists.txt
Normal file
6
components/eppp_link/examples/slave/CMakeLists.txt
Normal file
@@ -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)
|
7
components/eppp_link/examples/slave/README.md
Normal file
7
components/eppp_link/examples/slave/README.md
Normal file
@@ -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)
|
2
components/eppp_link/examples/slave/main/CMakeLists.txt
Normal file
2
components/eppp_link/examples/slave/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "eppp_slave.c"
|
||||
INCLUDE_DIRS ".")
|
45
components/eppp_link/examples/slave/main/Kconfig.projbuild
Normal file
45
components/eppp_link/examples/slave/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,45 @@
|
||||
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_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 11
|
||||
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 10
|
||||
range 0 31
|
||||
help
|
||||
Pin number of UART RX.
|
||||
|
||||
config EXAMPLE_UART_BAUDRATE
|
||||
int "Baudrate"
|
||||
depends on EPPP_LINK_DEVICE_UART
|
||||
default 2000000
|
||||
range 0 4000000
|
||||
help
|
||||
Baudrate used by the PPP over UART
|
||||
|
||||
endmenu
|
147
components/eppp_link/examples/slave/main/eppp_slave.c
Normal file
147
components/eppp_link/examples/slave/main/eppp_slave.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 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 "nvs_flash.h"
|
||||
#include "eppp_link.h"
|
||||
|
||||
static const char *TAG = "eppp_slave";
|
||||
|
||||
#if CONFIG_SOC_WIFI_SUPPORTED
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
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_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
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
|
||||
|
||||
void init_network_interface(void)
|
||||
{
|
||||
// placeholder to initialize any other network interface if WiFi is not available
|
||||
}
|
||||
|
||||
#endif // SoC WiFi capable chip
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
#else
|
||||
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;
|
||||
#endif
|
||||
esp_netif_t *eppp_netif = eppp_listen(&config);
|
||||
if (eppp_netif == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to setup connection");
|
||||
return ;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_netif_napt_enable(eppp_netif));
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
espressif/eppp_link:
|
||||
version: "*"
|
||||
override_path: "../../.."
|
1
components/eppp_link/examples/slave/sdkconfig.ci.spi
Normal file
1
components/eppp_link/examples/slave/sdkconfig.ci.spi
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_SPI=y
|
1
components/eppp_link/examples/slave/sdkconfig.ci.uart
Normal file
1
components/eppp_link/examples/slave/sdkconfig.ci.uart
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_EPPP_LINK_DEVICE_UART=y
|
6
components/eppp_link/examples/slave/sdkconfig.defaults
Normal file
6
components/eppp_link/examples/slave/sdkconfig.defaults
Normal file
@@ -0,0 +1,6 @@
|
||||
CONFIG_LWIP_IP_FORWARD=y
|
||||
CONFIG_LWIP_IPV4_NAPT=y
|
||||
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_VJ_HEADER_COMPRESSION=n
|
6
components/eppp_link/idf_component.yml
Normal file
6
components/eppp_link/idf_component.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 0.0.1
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
|
||||
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=5.2'
|
111
components/eppp_link/include/eppp_link.h
Normal file
111
components/eppp_link/include/eppp_link.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define EPPP_DEFAULT_SERVER_IP() ESP_IP4TOADDR(192, 168, 11, 1)
|
||||
#define EPPP_DEFAULT_CLIENT_IP() ESP_IP4TOADDR(192, 168, 11, 2)
|
||||
|
||||
#define EPPP_DEFAULT_CONFIG(our_ip, their_ip) { \
|
||||
.transport = EPPP_TRANSPORT_UART, \
|
||||
.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, \
|
||||
}, \
|
||||
.uart = { \
|
||||
.port = 1, \
|
||||
.baud = 921600, \
|
||||
.tx_io = 25, \
|
||||
.rx_io = 26, \
|
||||
.queue_size = 16, \
|
||||
.rx_buffer_size = 1024, \
|
||||
}, \
|
||||
. task = { \
|
||||
.run_task = true, \
|
||||
.stack_size = 4096, \
|
||||
.priority = 8, \
|
||||
}, \
|
||||
. ppp = { \
|
||||
.our_ip4_addr.addr = our_ip, \
|
||||
.their_ip4_addr.addr = their_ip, \
|
||||
} \
|
||||
}
|
||||
|
||||
#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_t;
|
||||
|
||||
|
||||
typedef struct eppp_config_t {
|
||||
eppp_transport_t transport;
|
||||
|
||||
struct eppp_config_spi_s {
|
||||
int host;
|
||||
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;
|
||||
} uart;
|
||||
|
||||
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;
|
||||
} ppp;
|
||||
|
||||
} eppp_config_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);
|
||||
|
||||
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_err_t eppp_netif_stop(esp_netif_t *netif, int stop_timeout_ms);
|
||||
|
||||
esp_err_t eppp_netif_start(esp_netif_t *netif);
|
||||
|
||||
esp_err_t eppp_perform(esp_netif_t *netif);
|
7
components/eppp_link/test/test_app/CMakeLists.txt
Normal file
7
components/eppp_link/test/test_app/CMakeLists.txt
Normal file
@@ -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)
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_app)
|
73
components/eppp_link/test/test_app/README.md
Normal file
73
components/eppp_link/test/test_app/README.md
Normal file
@@ -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
|
||||
```
|
4
components/eppp_link/test/test_app/main/CMakeLists.txt
Normal file
4
components/eppp_link/test/test_app/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS app_main.c
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES test_utils
|
||||
PRIV_REQUIRES unity nvs_flash esp_netif driver esp_event)
|
344
components/eppp_link/test/test_app/main/app_main.c
Normal file
344
components/eppp_link/test/test_app/main/app_main.c
Normal file
@@ -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: "../../.."
|
9
components/eppp_link/test/test_app/sdkconfig.defaults
Normal file
9
components/eppp_link/test/test_app/sdkconfig.defaults
Normal file
@@ -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