mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-22 14:57:30 +02:00
888 lines
28 KiB
C
888 lines
28 KiB
C
/*
|
|
* 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"
|
|
#include "esp_serial_slave_link/essl_sdio.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;
|
|
};
|
|
|
|
typedef esp_err_t (*transmit_t)(void *h, void *buffer, size_t len);
|
|
|
|
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
esp_err_t eppp_sdio_host_tx(void *h, void *buffer, size_t len);
|
|
esp_err_t eppp_sdio_host_rx(esp_netif_t *netif);
|
|
esp_err_t eppp_sdio_slave_rx(esp_netif_t *netif);
|
|
esp_err_t eppp_sdio_slave_tx(void *h, void *buffer, size_t len);
|
|
esp_err_t eppp_sdio_host_init(struct eppp_config_sdio_s *config);
|
|
esp_err_t eppp_sdio_slave_init(void);
|
|
void eppp_sdio_slave_deinit(void);
|
|
void eppp_sdio_host_deinit(void);
|
|
#else
|
|
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
|
{
|
|
struct eppp_handle *handle = h;
|
|
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
|
struct packet buf = { };
|
|
uint8_t *current_buffer = buffer;
|
|
size_t remaining = len;
|
|
do { // TODO(IDF-9194): Refactor this loop to allocate only once and perform
|
|
// fragmentation after receiving from the queue (applicable only if MTU > MAX_PAYLOAD)
|
|
size_t batch = remaining > MAX_PAYLOAD ? MAX_PAYLOAD : remaining;
|
|
buf.data = malloc(batch);
|
|
if (buf.data == NULL) {
|
|
ESP_LOGE(TAG, "Failed to allocate packet");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
buf.len = batch;
|
|
remaining -= batch;
|
|
memcpy(buf.data, current_buffer, batch);
|
|
current_buffer += batch;
|
|
BaseType_t ret = xQueueSend(handle->out_queue, &buf, 0);
|
|
if (ret != pdTRUE) {
|
|
ESP_LOGE(TAG, "Failed to queue packet to slave!");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
} while (remaining > 0);
|
|
|
|
if (handle->role == EPPP_SERVER && handle->blocked == SLAVE_BLOCKED) {
|
|
uint32_t now = esp_timer_get_time();
|
|
uint32_t diff = now - handle->slave_last_edge;
|
|
if (diff < MIN_TRIGGER_US) {
|
|
esp_rom_delay_us(MIN_TRIGGER_US - diff);
|
|
}
|
|
gpio_set_level(handle->gpio_intr, 0);
|
|
}
|
|
|
|
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
|
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_VERBOSE);
|
|
uart_write_bytes(handle->uart_port, buffer, len);
|
|
#endif // DEVICE UART or SPI
|
|
return ESP_OK;
|
|
}
|
|
#endif
|
|
|
|
static void netif_deinit(esp_netif_t *netif)
|
|
{
|
|
if (netif == NULL) {
|
|
return;
|
|
}
|
|
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
|
if (h == NULL) {
|
|
return;
|
|
}
|
|
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
|
struct packet buf = { };
|
|
while (xQueueReceive(h->out_queue, &buf, 0) == pdTRUE) {
|
|
if (buf.len > 0) {
|
|
free(buf.data);
|
|
}
|
|
}
|
|
vQueueDelete(h->out_queue);
|
|
if (h->role == EPPP_CLIENT) {
|
|
vSemaphoreDelete(h->ready_semaphore);
|
|
}
|
|
#endif
|
|
free(h);
|
|
esp_netif_destroy(netif);
|
|
if (s_eppp_netif_count > 0) {
|
|
s_eppp_netif_count--;
|
|
}
|
|
}
|
|
|
|
static esp_netif_t *netif_init(eppp_type_t role, eppp_config_t *eppp_config)
|
|
{
|
|
if (s_eppp_netif_count > 9) { // Limit to max 10 netifs, since we use "EPPPx" as the unique key (where x is 0-9)
|
|
ESP_LOGE(TAG, "Cannot create more than 10 instances");
|
|
return NULL;
|
|
}
|
|
|
|
// Create the object first
|
|
struct eppp_handle *h = calloc(1, sizeof(struct eppp_handle));
|
|
if (!h) {
|
|
ESP_LOGE(TAG, "Failed to allocate eppp_handle");
|
|
return NULL;
|
|
}
|
|
h->role = role;
|
|
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
|
h->out_queue = xQueueCreate(CONFIG_EPPP_LINK_PACKET_QUEUE_SIZE, sizeof(struct packet));
|
|
if (!h->out_queue) {
|
|
ESP_LOGE(TAG, "Failed to create the packet queue");
|
|
free(h);
|
|
return NULL;
|
|
}
|
|
if (role == EPPP_CLIENT) {
|
|
h->ready_semaphore = xSemaphoreCreateBinary();
|
|
if (!h->ready_semaphore) {
|
|
ESP_LOGE(TAG, "Failed to create the packet queue");
|
|
vQueueDelete(h->out_queue);
|
|
free(h);
|
|
return NULL;
|
|
}
|
|
}
|
|
h->transaction_size = 0;
|
|
h->outbound.data = NULL;
|
|
h->outbound.len = 0;
|
|
if (role == EPPP_SERVER) {
|
|
esp_timer_create_args_t args = {
|
|
.callback = &timer_callback,
|
|
.arg = h,
|
|
.name = "timer"
|
|
};
|
|
if (esp_timer_create(&args, &h->timer) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to create the packet queue");
|
|
vQueueDelete(h->out_queue);
|
|
vSemaphoreDelete(h->ready_semaphore);
|
|
free(h);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
esp_netif_driver_ifconfig_t driver_cfg = {
|
|
.handle = h,
|
|
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
.transmit = role == EPPP_CLIENT ? eppp_sdio_host_tx : eppp_sdio_slave_tx,
|
|
#else
|
|
.transmit = transmit,
|
|
#endif
|
|
};
|
|
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
|
|
|
|
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 (eppp_config->ppp.netif_description) {
|
|
base_netif_cfg.if_desc = eppp_config->ppp.netif_description;
|
|
} else {
|
|
base_netif_cfg.if_desc = role == EPPP_CLIENT ? "pppos_client" : "pppos_server";
|
|
}
|
|
if (eppp_config->ppp.netif_prio) {
|
|
base_netif_cfg.route_prio = eppp_config->ppp.netif_prio;
|
|
}
|
|
esp_netif_config_t netif_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;
|
|
}
|
|
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
|
|
esp_err_t eppp_perform(esp_netif_t *netif)
|
|
{
|
|
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
|
if (h->stop) {
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
if (h->role == EPPP_SERVER) {
|
|
return eppp_sdio_slave_rx(netif);
|
|
} else {
|
|
return eppp_sdio_host_rx(netif);
|
|
}
|
|
}
|
|
|
|
#endif // CONFIG_EPPP_LINK_DEVICE_SPI / UART
|
|
|
|
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));
|
|
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
struct eppp_handle *h = esp_netif_get_io_driver(netif);
|
|
if (h->role == EPPP_CLIENT) {
|
|
eppp_sdio_host_deinit();
|
|
} else {
|
|
eppp_sdio_slave_deinit();
|
|
}
|
|
#endif
|
|
netif_deinit(netif);
|
|
}
|
|
|
|
esp_netif_t *eppp_init(eppp_type_t role, eppp_config_t *config)
|
|
{
|
|
if (config == NULL || (role != EPPP_SERVER && role != EPPP_CLIENT)) {
|
|
ESP_LOGE(TAG, "Invalid configuration or role");
|
|
return NULL;
|
|
}
|
|
|
|
esp_netif_t *netif = netif_init(role, config);
|
|
if (!netif) {
|
|
ESP_LOGE(TAG, "Failed to initialize PPP netif");
|
|
return NULL;
|
|
}
|
|
esp_netif_ppp_config_t netif_params;
|
|
ESP_ERROR_CHECK(esp_netif_ppp_get_params(netif, &netif_params));
|
|
netif_params.ppp_our_ip4_addr = config->ppp.our_ip4_addr;
|
|
netif_params.ppp_their_ip4_addr = config->ppp.their_ip4_addr;
|
|
netif_params.ppp_error_event_enabled = true;
|
|
ESP_ERROR_CHECK(esp_netif_ppp_set_params(netif, &netif_params));
|
|
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
|
if (role == EPPP_CLIENT) {
|
|
init_master(&config->spi, netif);
|
|
} else {
|
|
init_slave(&config->spi, netif);
|
|
|
|
}
|
|
#elif CONFIG_EPPP_LINK_DEVICE_UART
|
|
init_uart(esp_netif_get_io_driver(netif), config);
|
|
#elif CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
esp_err_t ret;
|
|
if (role == EPPP_SERVER) {
|
|
ret = eppp_sdio_slave_init();
|
|
} else {
|
|
ret = eppp_sdio_host_init(&config->sdio);
|
|
}
|
|
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to initialize SDIO %d", ret);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
return netif;
|
|
}
|
|
|
|
esp_netif_t *eppp_open(eppp_type_t role, eppp_config_t *config, int connect_timeout_ms)
|
|
{
|
|
if (config == NULL || (role != EPPP_SERVER && role != EPPP_CLIENT)) {
|
|
ESP_LOGE(TAG, "Invalid configuration or role");
|
|
return NULL;
|
|
}
|
|
#if CONFIG_EPPP_LINK_DEVICE_UART
|
|
if (config->transport != EPPP_TRANSPORT_UART) {
|
|
ESP_LOGE(TAG, "Invalid transport: UART device must be enabled in Kconfig");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
#if CONFIG_EPPP_LINK_DEVICE_SPI
|
|
if (config->transport != EPPP_TRANSPORT_SPI) {
|
|
ESP_LOGE(TAG, "Invalid transport: SPI device must be enabled in Kconfig");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
#if CONFIG_EPPP_LINK_DEVICE_SDIO
|
|
if (config->transport != EPPP_TRANSPORT_SDIO) {
|
|
ESP_LOGE(TAG, "Invalid transport: SDIO device must be enabled in Kconfig");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
if (config->task.run_task == false) {
|
|
ESP_LOGE(TAG, "task.run_task == false is invalid in this API. Please use eppp_init()");
|
|
return NULL;
|
|
}
|
|
if (s_event_group == NULL) {
|
|
s_event_group = xEventGroupCreate();
|
|
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to register IP event handler");
|
|
remove_handlers();
|
|
return NULL;
|
|
}
|
|
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();
|
|
}
|