esp_netif: Migrate SLIP interface to user-space

* Original commit: espressif/esp-idf@83b8556f10
This commit is contained in:
David Cermak
2022-07-22 17:37:47 +02:00
parent 56cb58ced7
commit 52d7458b99
8 changed files with 354 additions and 81 deletions

View File

@ -9,6 +9,8 @@
This provides SLIP support for connection to Contiki gateway devices, allowing the ESP platform board to be used to bridge between low-power networks and IP (Wifi / Ethernet).
This example also demonstrates creating custom network interfaces, including UART drivers and lwIP netif layers, and attaching them to the standard `esp_netif` component, so the generic system interfaces can still use the common approach of listing all interfaces, updating states, posting events and handling routing priorities. Please refer to the implementation of [slip_modem](components/slip_modem) component for more details.
## How to use example
### Hardware Required
@ -49,8 +51,8 @@ nc -u 10.0.0.2 5678
| ESP32 | Gateway |
| ------ | -------------- |
| GPIO4 | RX |
| GPIO36 | TX |
| GPIO25 | RX |
| GPIO26 | TX |
| GND | GND |
| 3v3 | VCC |

View File

@ -1,7 +1,7 @@
# SLIP Modem Component
idf_component_register(
SRCS "library/slip_modem.c"
SRCS "library/slip_modem.c" "library/slip_modem_netif.c"
INCLUDE_DIRS "include"
REQUIRES esp_netif driver
)

View File

@ -1,16 +1,8 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -18,15 +10,30 @@
#include <stdint.h>
#include "esp_netif.h"
#include "esp_netif_slip.h"
#include "driver/uart.h"
/** @brief Configuration of SLIP network interface
*
*/
#define ESP_NETIF_INHERENT_DEFAULT_SLIP() \
{ \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) \
ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(ip_info) \
.get_ip_event = 0, \
.lost_ip_event = 0, \
.if_key = "SLP_DEF", \
.if_desc = "slip", \
.route_prio = 16, \
.bridge_info = NULL \
};
extern esp_netif_netstack_config_t *netstack_default_slip;
// Forward declare modem object
typedef struct esp_slip_modem esp_slip_modem_t;
typedef struct slip_modem slip_modem_t;
// Filter callbacks for handling application specific slip messages
typedef bool slip_rx_filter_cb_t(void *ctx, uint8_t *data, uint32_t len);
typedef bool slip_rx_filter_cb_t(slip_modem_t *slip, uint8_t *data, uint32_t len);
/** @brief Configuration structure for SLIP modem interface
@ -43,9 +50,10 @@ typedef struct {
uint32_t rx_buffer_len; /* Length of buffer for RX messages */
slip_rx_filter_cb_t *rx_filter; /* Filter for parsing out non-SLIP messages from incoming SLIP stream */
void *rx_filter_ctx; /* Context to be passed to SLIP filter function */
esp_ip6_addr_t *ipv6_addr;
} slip_modem_config_t;
} esp_slip_modem_config_t;
/** @brief Create a slip modem
@ -56,7 +64,7 @@ typedef struct {
* @returns
* - slip modem driver glue object
*/
void *esp_slip_modem_create(esp_netif_t *slip_netif, esp_slip_modem_config_t *modem_config);
slip_modem_t *slip_modem_create(esp_netif_t *slip_netif, slip_modem_config_t *modem_config);
/** @brief Destroy a slip modem
*
@ -65,4 +73,24 @@ void *esp_slip_modem_create(esp_netif_t *slip_netif, esp_slip_modem_config_t *mo
* @return
* - ESP_OK on success
*/
esp_err_t esp_slip_modem_destroy(esp_slip_modem_t *slip_modem);
esp_err_t slip_modem_destroy(slip_modem_t *slip);
/**
* @brief Getter for the internally configured IPv6 address
*
* @param[in] slip modem object
*
* @returns
* - ipv6 address
*/
const esp_ip6_addr_t *slip_modem_get_ipv6_address(slip_modem_t *slip);
/**
* @brief Data path API that forward the supplied data to the attached network interface
*
* @param[in] slip modem object
* @param[in] buffer pointer to the outgoing data
* @param[in] len length of the data
*
*/
void slip_modem_raw_output(slip_modem_t *slip, void *buffer, size_t len);

View File

@ -4,17 +4,18 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "slip_modem.h"
#include "esp_netif.h"
#include "esp_netif_slip.h"
#include "slip_modem_netif.h"
#include "esp_event.h"
#include "esp_log.h"
#define SLIP_RX_TASK_PRIORITY 10
#define SLIP_RX_TASK_STACK_SIZE (4 * 1024)
static const char *TAG = "esp-slip_modem";
static const char *TAG = "slip-modem";
// UART container object
@ -40,7 +41,7 @@ typedef struct {
// Modem object, implements glue logic for slip_driver and esp_netif
struct esp_slip_modem {
struct slip_modem {
// ESP base netif driver
esp_netif_driver_base_t base;
@ -53,50 +54,54 @@ struct esp_slip_modem {
// Filter callbacks for application-specific slip message handling
slip_rx_filter_cb_t *rx_filter;
void *rx_filter_ctx;
// Running flag
bool running;
// esp_netif related: SLIP interface IP6 address
esp_ip6_addr_t addr;
};
// Forward function declaration
static void esp_slip_modem_uart_rx_task(void *arg);
static esp_err_t esp_slip_modem_post_attach(esp_netif_t *esp_netif, void *args);
static void slip_modem_uart_rx_task(void *arg);
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args);
// Create a new slip netif
void *esp_slip_modem_create(esp_netif_t *slip_netif, esp_slip_modem_config_t *modem_config)
// Create a new slip modem
slip_modem_t *slip_modem_create(esp_netif_t *slip_netif, slip_modem_config_t *modem_config)
{
ESP_LOGI(TAG, "%s: Creating slip modem (netif: %p)", __func__, slip_netif);
ESP_LOGD(TAG, "%s (netif: %p)", __func__, slip_netif);
esp_slip_modem_t *slip_modem = calloc(1, sizeof(esp_slip_modem_t));
slip_modem_t *slip_modem = calloc(1, sizeof(slip_modem_t));
if (!slip_modem) {
ESP_LOGE(TAG, "create netif glue failed");
return NULL;
}
// Attach driver and post_attach callbacks
slip_modem->base.post_attach = esp_slip_modem_post_attach;
slip_modem->base.post_attach = slip_modem_post_attach;
slip_modem->base.netif = slip_netif;
// Attach config
slip_modem->buffer_len = modem_config->rx_buffer_len;
slip_modem->rx_filter = modem_config->rx_filter;
slip_modem->rx_filter_ctx = modem_config->rx_filter_ctx;
slip_modem->uart.uart_dev = modem_config->uart_dev;
slip_modem->uart.uart_baud = modem_config->uart_baud;
slip_modem->uart.uart_rx_pin = modem_config->uart_rx_pin;
slip_modem->uart.uart_tx_pin = modem_config->uart_tx_pin;
memcpy(&slip_modem->addr, modem_config->ipv6_addr, sizeof(esp_ip6_addr_t));
// Return new modem, with a cast to the first item
return &slip_modem->base;
// Return the new modem
return slip_modem;
}
// Internal handler called on driver start
static esp_err_t esp_slip_driver_start(esp_slip_modem_t *slip_modem)
static esp_err_t esp_slip_driver_start(slip_modem_t *slip_modem)
{
ESP_LOGD(TAG, "%s: Starting SLIP modem (modem %p)", __func__, slip_modem);
@ -129,38 +134,39 @@ static esp_err_t esp_slip_driver_start(esp_slip_modem_t *slip_modem)
// Start slip RX task
slip_modem->running = true;
xTaskCreate(esp_slip_modem_uart_rx_task, "slip_modem_uart_rx_task", SLIP_RX_TASK_STACK_SIZE, slip_modem, SLIP_RX_TASK_PRIORITY, &slip_modem->uart.uart_rx_task);
xTaskCreate(slip_modem_uart_rx_task, "slip_modem_uart_rx_task", SLIP_RX_TASK_STACK_SIZE, slip_modem, SLIP_RX_TASK_PRIORITY, &slip_modem->uart.uart_rx_task);
// Finally, initialise slip network interface
esp_netif_action_start(slip_modem->base.netif, 0, 0, 0);
ESP_ERROR_CHECK(slip_modem_netif_start(slip_modem->base.netif, &slip_modem->addr));
return ESP_OK;
}
esp_err_t esp_slip_modem_destroy(esp_slip_modem_t *slip_modem)
esp_err_t slip_modem_destroy(slip_modem_t *slip)
{
// Stop slip driver
esp_netif_action_stop(slip_modem->base.netif, 0, 0, 0);
esp_netif_action_stop(slip->base.netif, 0, 0, 0);
ESP_ERROR_CHECK(slip_modem_netif_stop(slip->base.netif));
// Stop uart rx task
vTaskDelete(slip_modem->uart.uart_rx_task);
vTaskDelete(slip->uart.uart_rx_task);
// Delete driver
uart_driver_delete(slip_modem->uart.uart_dev);
uart_driver_delete(slip->uart.uart_dev);
// Free slip interface
free(slip_modem);
free(slip);
return ESP_OK;
}
// Modem transmit for glue logic
static esp_err_t esp_slip_modem_transmit(void *slip_driver, void *buffer, size_t len)
static esp_err_t slip_modem_transmit(void *slip_driver, void *buffer, size_t len)
{
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
esp_slip_modem_t *slip_modem = (esp_slip_modem_t *) slip_driver;
slip_modem_t *slip_modem = (slip_modem_t *) slip_driver;
int32_t res = uart_write_bytes(slip_modem->uart.uart_dev, (char *)buffer, len);
if (res < 0) {
@ -172,15 +178,15 @@ static esp_err_t esp_slip_modem_transmit(void *slip_driver, void *buffer, size_t
}
// Post-attach handler for netif
static esp_err_t esp_slip_modem_post_attach(esp_netif_t *esp_netif, void *args)
static esp_err_t slip_modem_post_attach(esp_netif_t *esp_netif, void *args)
{
esp_slip_modem_t *slip_modem = (esp_slip_modem_t *) args;
slip_modem_t *slip_modem = (slip_modem_t *) args;
ESP_LOGD(TAG, "%s (netif: %p args: %p)", __func__, esp_netif, args);
const esp_netif_driver_ifconfig_t driver_ifconfig = {
.driver_free_rx_buffer = NULL,
.transmit = esp_slip_modem_transmit,
.transmit = slip_modem_transmit,
.handle = slip_modem,
};
@ -192,9 +198,9 @@ static esp_err_t esp_slip_modem_post_attach(esp_netif_t *esp_netif, void *args)
return ESP_OK;
}
static void esp_slip_modem_uart_rx_task(void *arg)
static void slip_modem_uart_rx_task(void *arg)
{
esp_slip_modem_t *slip_modem = (esp_slip_modem_t *) arg;
slip_modem_t *slip_modem = (slip_modem_t *) arg;
ESP_LOGD(TAG, "Start SLIP modem RX task (slip_modem %p filter: %p)", slip_modem, slip_modem->rx_filter);
ESP_LOGD(TAG, "Uart: %d, buffer: %p (%d bytes)", slip_modem->uart.uart_dev, slip_modem->buffer, slip_modem->buffer_len);
@ -213,11 +219,12 @@ static void esp_slip_modem_uart_rx_task(void *arg)
slip_modem->buffer[len] = '\0';
// Filter if provided
if ((slip_modem->rx_filter != NULL) && slip_modem->rx_filter(slip_modem->rx_filter_ctx, slip_modem->buffer, len)) {
if ((slip_modem->rx_filter != NULL) && slip_modem->rx_filter(slip_modem, slip_modem->buffer, len)) {
continue;
}
// Pass received bytes in to slip interface
ESP_LOGI(TAG, "esp_netif %p", slip_modem->base.netif);
esp_netif_receive(slip_modem->base.netif, slip_modem->buffer, len, NULL);
}
@ -225,3 +232,16 @@ static void esp_slip_modem_uart_rx_task(void *arg)
vTaskDelay(1 * portTICK_PERIOD_MS);
}
}
/**
* @brief Gets the internally configured ipv6 address
*/
const esp_ip6_addr_t *slip_modem_get_ipv6_address(slip_modem_t *slip)
{
return &slip->addr;
}
void slip_modem_raw_output(slip_modem_t *slip, void *buffer, size_t len)
{
slip_modem_netif_raw_output(slip->base.netif, buffer, len);
}

View File

@ -0,0 +1,192 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_netif.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "lwip/esp_netif_net_stack.h"
#include "lwip/dns.h"
#include "lwip/ip6_addr.h"
#include "lwip/netif.h"
#include "netif/slipif.h"
#include "lwip/sio.h"
static const char *TAG = "slip-modem-netif";
/**
* @brief Stops the SLIP interface
*/
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif)
{
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
ESP_LOGI(TAG, "%s: Stopped SLIP connection: lwip netif:%p", __func__, netif);
// Stop interface
netif_set_link_down(netif);
return ESP_OK;
}
/**
* @brief Starts the SLIP interface
*/
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr)
{
struct netif *netif = esp_netif_get_netif_impl(esp_netif);
ESP_LOGI(TAG, "%s: Starting SLIP interface: lwip netif:%p", __func__, netif);
// Set the netif up
netif_set_up(netif);
netif_set_link_up(netif);
#if CONFIG_LWIP_IPV6
int8_t addr_index = 0;
netif_ip6_addr_set(netif, addr_index, (ip6_addr_t *)addr);
netif_ip6_addr_set_state(netif, addr_index, IP6_ADDR_VALID);
#endif
return ESP_OK;
}
/**
* @brief Write incoming serial data to the SLIP interface
*/
void esp_netif_lwip_slip_input(void *h, void *buffer, unsigned int len, void *eb)
{
struct netif *netif = h;
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, len, ESP_LOG_DEBUG);
// Update slip netif with data
const int max_batch = 255;
int sent = 0;
while(sent < len) {
int batch = (len - sent) > max_batch ? max_batch : (len - sent);
slipif_received_bytes(netif, buffer+sent, batch);
sent += batch;
}
// Process incoming bytes
for (int i = 0; i < len; i++) {
slipif_process_rxqueue(netif);
}
}
/**
* @brief Write raw data out the SLIP interface
*/
void slip_modem_netif_raw_output(esp_netif_t *netif, void *buffer, size_t len)
{
struct netif *lwip_netif = esp_netif_get_netif_impl(netif);
ESP_LOGD(TAG, "%s", __func__);
struct pbuf p = {
.next = NULL,
.payload = buffer,
.tot_len = len,
.len = len,
};
// Call slip if output function to feed data out slip interface
#if CONFIG_LWIP_IPV6
lwip_netif->output_ip6(lwip_netif, &p, NULL);
#else
lwip_netif->output(lwip_netif, &p, NULL);
#endif
}
/** @brief Get esp-netif object corresponding to registration index
*/
static esp_netif_t * get_netif_with_esp_index(int index)
{
esp_netif_t *netif = NULL;
int counter = 0;
while ((netif = esp_netif_next(netif)) != NULL) {
if (counter == index) {
return netif;
}
counter++;
}
return NULL;
}
/** @brief Return list registration index of the supplied netif ptr
*/
static int get_esp_netif_index(esp_netif_t * esp_netif)
{
esp_netif_t *netif = NULL;
int counter = 0;
while ((netif = esp_netif_next(netif)) != NULL) {
if (esp_netif == netif) {
return counter;
}
counter++;
}
return -1;
}
err_t esp_slipif_init(struct netif *netif)
{
esp_netif_t *esp_netif = netif->state;
int esp_index = get_esp_netif_index(esp_netif);
if (esp_index < 0) {
return ERR_IF;
}
// Store netif index in net interface for SIO open command to abstract the dev
netif->state = (void *)esp_index;
return slipif_init(netif);
}
const struct esp_netif_netstack_config s_netif_config_slip = {
.lwip = {
.init_fn = esp_slipif_init,
.input_fn = esp_netif_lwip_slip_input,
}
};
const esp_netif_netstack_config_t *netstack_default_slip = &s_netif_config_slip;
/***
* @brief Open a serial device for communication
*/
sio_fd_t sio_open(uint8_t devnum)
{
ESP_LOGD(TAG, "Opening device: %d\r\n", devnum);
esp_netif_t *esp_netif = get_netif_with_esp_index(devnum);
if (!esp_netif) {
ESP_LOGE(TAG, "didn't find esp-netif with index=%d\n", devnum);
return NULL;
}
// Return SIO handle
return esp_netif;
}
/***
* @brief Send a single character to the serial device (blocking)
*/
void sio_send(uint8_t c, sio_fd_t fd)
{
esp_netif_t *esp_netif = fd;
ESP_LOGD(TAG, "%s", __func__);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &c, 1, ESP_LOG_DEBUG);
esp_err_t ret = esp_netif_transmit(esp_netif, &c, 1);
if (ret != ESP_OK) {
// Handle errors
ESP_LOGD(TAG, "%s: uart_write_bytes error %i", __func__, ret);
}
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/**
* @brief Stop the esp slip netif
*
* @param[in] esp_netif handle to slip esp-netif instance
*
* @return
* - ESP_OK on success
*/
esp_err_t slip_modem_netif_stop(esp_netif_t *esp_netif);
/**
* @brief Start the esp slip netif
*
* @param[in] esp_netif handle to slip esp-netif instance
* @param[in] addr IPv6 address associated with this SLIP interface
*
* @return
* - ESP_OK on success
*/
esp_err_t slip_modem_netif_start(esp_netif_t *esp_netif, esp_ip6_addr_t *addr);
/**
* @brief Data path API to write raw packet ous the SLIP interface
*
* This API is typically used when implementing user defined methods
*
* @param[in] esp_netif handle to slip esp-netif instance
* @param[in] buffer pointer to the outgoing data
* @param[in] len length of the data
*
* @return
* - ESP_OK on success
*/
void slip_modem_netif_raw_output(esp_netif_t *netif, void *buffer, size_t len);

View File

@ -3,14 +3,14 @@ menu "Example Configuration"
menu "UART Configuration"
config EXAMPLE_UART_TX_PIN
int "TXD Pin Number"
default 4
default 25
range 0 36
help
Pin number of UART TX.
config EXAMPLE_UART_RX_PIN
int "RXD Pin Number"
default 36
default 26
range 0 36
help
Pin number of UART RX.

View File

@ -7,20 +7,18 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/socket.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_netif_slip.h"
#include "lwip/sockets.h"
#include "slip_modem.h"
static const char *TAG = "SLIP_EXAMPLE";
static const char *TAG = "slip-example";
#define STACK_SIZE (10 * 1024)
#define PRIORITY 10
@ -118,13 +116,10 @@ esp_err_t udp_rx_tx_init(void)
}
// Write a prefix to the contiki slip device
static void slip_set_prefix(esp_netif_t *slip_netif)
static void slip_set_prefix(slip_modem_t *slip)
{
uint8_t buff[10] = {0};
// Fetch the slip interface IP
const esp_ip6_addr_t *addr = esp_slip_get_ip6(slip_netif);
const esp_ip6_addr_t *addr = slip_modem_get_ipv6_address(slip);
ESP_LOGI(TAG, "%s: prefix set (%08x:%08x)", __func__,
lwip_ntohl(addr->addr[0]), lwip_ntohl(addr->addr[1]));
@ -138,21 +133,18 @@ static void slip_set_prefix(esp_netif_t *slip_netif)
}
// Write raw data out the slip interface
esp_netif_lwip_slip_raw_output(slip_netif, buff, 2 + 8);
slip_modem_raw_output(slip, buff, 2 + 8);
}
// slip_rx_filter filters incoming commands from the slip interface
// this implementation is designed for use with contiki slip devices
bool slip_rx_filter(void *ctx, uint8_t *data, uint32_t len)
static bool slip_rx_filter(slip_modem_t *slip, uint8_t *data, uint32_t len)
{
esp_netif_t *slip_netif = (esp_netif_t *)ctx;
if (data[1] == '?') {
switch (data[2]) {
case 'P':
ESP_LOGI(TAG, "Prefix request");
slip_set_prefix(slip_netif);
slip_set_prefix(slip);
return true;
@ -191,24 +183,21 @@ esp_netif_t *slip_if_init(void)
#endif
esp_netif_config_t cfg = { .base = &base_cfg,
.driver = NULL,
.stack = ESP_NETIF_NETSTACK_DEFAULT_SLIP };
.stack = netstack_default_slip };
esp_netif_t *slip_netif = esp_netif_new(&cfg);
esp_netif_slip_config_t slip_config;
IP6_ADDR(&slip_config.ip6_addr,
esp_ip6_addr_t local_addr; /* Local IP6 address */
IP6_ADDR(&local_addr,
lwip_htonl(0xfd0000),
lwip_htonl(0x00000000),
lwip_htonl(0x00000000),
lwip_htonl(0x00000001)
);
esp_netif_slip_set_params(slip_netif, &slip_config);
ESP_LOGI(TAG, "Initialising SLIP modem");
esp_slip_modem_config_t modem_cfg = {
slip_modem_config_t modem_cfg = {
.uart_dev = UART_NUM_1,
.uart_tx_pin = CONFIG_EXAMPLE_UART_TX_PIN,
@ -216,12 +205,12 @@ esp_netif_t *slip_if_init(void)
.uart_baud = CONFIG_EXAMPLE_UART_BAUD,
.rx_buffer_len = 1024,
.rx_filter = slip_rx_filter,
.rx_filter_ctx = slip_netif,
.ipv6_addr = &local_addr
};
void *slip_modem = esp_slip_modem_create(slip_netif, &modem_cfg);
void *slip_modem = slip_modem_create(slip_netif, &modem_cfg);
assert(slip_modem);
ESP_ERROR_CHECK(esp_netif_attach(slip_netif, slip_modem));
ESP_LOGI(TAG, "SLIP init complete");