ESP32-S3 firmware acting as BMC for another board, controlling an ATX power supply and providing access to UART. Co-authored with GLM-5
363 lines
9.4 KiB
C
363 lines
9.4 KiB
C
#include "ethernet_manager.h"
|
|
#include "config.h"
|
|
#include "esp_log.h"
|
|
#include "esp_event.h"
|
|
#include "esp_eth_driver.h"
|
|
#include "esp_eth_mac.h"
|
|
#include "esp_eth_phy.h"
|
|
#include "esp_netif.h"
|
|
#include "driver/spi_master.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/semphr.h"
|
|
#include "lwip/ip_addr.h"
|
|
#include "esp_eth_phy_w5500.h"
|
|
#include "esp_eth_mac_w5500.h"
|
|
#include <string.h>
|
|
|
|
static const char *TAG = TAG_ETH;
|
|
|
|
// Ethernet handles
|
|
static esp_eth_handle_t eth_handle = NULL;
|
|
static esp_eth_mac_t *eth_mac = NULL;
|
|
static esp_eth_phy_t *eth_phy = NULL;
|
|
static esp_netif_t *eth_netif = NULL;
|
|
|
|
// State tracking
|
|
static bool eth_initialized = false;
|
|
static bool eth_connected = false;
|
|
static bool eth_has_ip = false;
|
|
static SemaphoreHandle_t eth_mutex = NULL;
|
|
|
|
// Event callback
|
|
static eth_event_callback_t event_callback = NULL;
|
|
|
|
// Current configuration
|
|
static bmc_eth_config_t current_config = {
|
|
.use_dhcp = true,
|
|
.static_ip = BMC_STATIC_IP,
|
|
.netmask = BMC_NETMASK,
|
|
.gateway = BMC_GATEWAY,
|
|
};
|
|
|
|
// ============================================================================
|
|
// Event Handler
|
|
// ============================================================================
|
|
|
|
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
|
switch (event_id) {
|
|
case ETHERNET_EVENT_CONNECTED:
|
|
ESP_LOGI(TAG, "Ethernet link up");
|
|
eth_connected = true;
|
|
if (event_callback) {
|
|
event_callback(ETH_EVENT_CONNECTED);
|
|
}
|
|
break;
|
|
|
|
case ETHERNET_EVENT_DISCONNECTED:
|
|
ESP_LOGI(TAG, "Ethernet link down");
|
|
eth_connected = false;
|
|
eth_has_ip = false;
|
|
if (event_callback) {
|
|
event_callback(ETH_EVENT_DISCONNECTED);
|
|
}
|
|
break;
|
|
|
|
case ETHERNET_EVENT_START:
|
|
ESP_LOGI(TAG, "Ethernet started");
|
|
break;
|
|
|
|
case ETHERNET_EVENT_STOP:
|
|
ESP_LOGI(TAG, "Ethernet stopped");
|
|
break;
|
|
|
|
case IP_EVENT_ETH_GOT_IP: {
|
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
|
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
|
|
ESP_LOGI(TAG, "Netmask: " IPSTR, IP2STR(&event->ip_info.netmask));
|
|
ESP_LOGI(TAG, "Gateway: " IPSTR, IP2STR(&event->ip_info.gw));
|
|
eth_has_ip = true;
|
|
if (event_callback) {
|
|
event_callback(ETH_EVENT_GOT_IP);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// SPI Configuration for W5500
|
|
// ============================================================================
|
|
|
|
static esp_err_t init_spi_for_ethernet(void) {
|
|
spi_bus_config_t buscfg = {
|
|
.miso_io_num = ETH_SPI_MISO_GPIO,
|
|
.mosi_io_num = ETH_SPI_MOSI_GPIO,
|
|
.sclk_io_num = ETH_SPI_SCLK_GPIO,
|
|
.quadwp_io_num = -1,
|
|
.quadhd_io_num = -1,
|
|
.max_transfer_sz = 4096,
|
|
};
|
|
|
|
esp_err_t ret = spi_bus_initialize(ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
|
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
|
ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "SPI bus initialized for W5500");
|
|
return ESP_OK;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Public Functions
|
|
// ============================================================================
|
|
|
|
esp_err_t ethernet_manager_init(void) {
|
|
if (eth_initialized) {
|
|
ESP_LOGW(TAG, "Ethernet manager already initialized");
|
|
return ESP_OK;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Initializing Ethernet manager (W5500 SPI)");
|
|
|
|
// Create mutex
|
|
eth_mutex = xSemaphoreCreateMutex();
|
|
if (!eth_mutex) {
|
|
ESP_LOGE(TAG, "Failed to create mutex");
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
// Initialize TCP/IP stack
|
|
ESP_ERROR_CHECK(esp_netif_init());
|
|
|
|
// Create default event loop if not already created
|
|
esp_err_t loop_ret = esp_event_loop_create_default();
|
|
if (loop_ret != ESP_OK && loop_ret != ESP_ERR_INVALID_STATE) {
|
|
ESP_LOGE(TAG, "Failed to create event loop: %s", esp_err_to_name(loop_ret));
|
|
return loop_ret;
|
|
}
|
|
|
|
// Create default ETH netif
|
|
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
|
eth_netif = esp_netif_new(&netif_cfg);
|
|
|
|
// Register event handlers
|
|
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_event_handler, NULL));
|
|
|
|
// Initialize SPI bus
|
|
esp_err_t ret = init_spi_for_ethernet();
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
|
|
// Configure W5500 MAC address
|
|
uint8_t mac_addr[6] = ETH_MAC_ADDR;
|
|
|
|
// Create SPI device configuration for W5500
|
|
spi_device_interface_config_t devcfg = {
|
|
.mode = 0,
|
|
.clock_speed_hz = 36 * 1000 * 1000, // 36 MHz
|
|
.queue_size = 20,
|
|
.spics_io_num = ETH_SPI_CS_GPIO,
|
|
};
|
|
|
|
// Create W5500 MAC instance using SPI (new API uses spi_host and device config pointer)
|
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(ETH_SPI_HOST, &devcfg);
|
|
w5500_config.int_gpio_num = ETH_SPI_INT_GPIO;
|
|
|
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
|
mac_config.rx_task_stack_size = 4096;
|
|
mac_config.rx_task_prio = 15;
|
|
|
|
eth_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
|
if (!eth_mac) {
|
|
ESP_LOGE(TAG, "Failed to create W5500 MAC");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
// Create W5500 PHY instance
|
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
|
phy_config.phy_addr = 1; // W5500 uses address 1
|
|
phy_config.reset_gpio_num = ETH_SPI_RST_GPIO;
|
|
phy_config.reset_timeout_ms = 1000;
|
|
|
|
eth_phy = esp_eth_phy_new_w5500(&phy_config);
|
|
if (!eth_phy) {
|
|
ESP_LOGE(TAG, "Failed to create W5500 PHY");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
// Create Ethernet driver
|
|
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy);
|
|
ret = esp_eth_driver_install(ð_config, ð_handle);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to install Ethernet driver: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Set MAC address
|
|
ret = esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGW(TAG, "Failed to set MAC address: %s", esp_err_to_name(ret));
|
|
}
|
|
|
|
// Attach Ethernet driver to TCP/IP stack
|
|
ret = esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle));
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to attach Ethernet to netif: %s", esp_err_to_name(ret));
|
|
esp_eth_driver_uninstall(eth_handle);
|
|
return ret;
|
|
}
|
|
|
|
// Start Ethernet driver
|
|
ret = esp_eth_start(eth_handle);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to start Ethernet: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
eth_initialized = true;
|
|
ESP_LOGI(TAG, "Ethernet manager initialized successfully");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
bool ethernet_is_connected(void) {
|
|
return eth_connected && eth_has_ip;
|
|
}
|
|
|
|
esp_err_t ethernet_get_ip(char *ip_str) {
|
|
if (!eth_initialized || !ip_str) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
esp_netif_ip_info_t ip_info;
|
|
esp_err_t ret = esp_netif_get_ip_info(eth_netif, &ip_info);
|
|
|
|
if (ret == ESP_OK) {
|
|
snprintf(ip_str, 16, IPSTR, IP2STR(&ip_info.ip));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t ethernet_get_network_info(char *ip, char *netmask, char *gateway) {
|
|
if (!eth_initialized) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
esp_netif_ip_info_t ip_info;
|
|
esp_err_t ret = esp_netif_get_ip_info(eth_netif, &ip_info);
|
|
|
|
if (ret == ESP_OK) {
|
|
if (ip)
|
|
snprintf(ip, 16, IPSTR, IP2STR(&ip_info.ip));
|
|
if (netmask)
|
|
snprintf(netmask, 16, IPSTR, IP2STR(&ip_info.netmask));
|
|
if (gateway)
|
|
snprintf(gateway, 16, IPSTR, IP2STR(&ip_info.gw));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t ethernet_get_mac(char *mac_str) {
|
|
if (!eth_initialized || !mac_str) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
uint8_t mac[6];
|
|
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac);
|
|
|
|
if (ret == ESP_OK) {
|
|
snprintf(mac_str, 18, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t ethernet_set_static_ip(const bmc_eth_config_t *config) {
|
|
if (!eth_initialized || !config) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
xSemaphoreTake(eth_mutex, portMAX_DELAY);
|
|
|
|
// Stop DHCP
|
|
esp_netif_dhcpc_stop(eth_netif);
|
|
|
|
// Set static IP
|
|
esp_netif_ip_info_t ip_info;
|
|
ip_info.ip.addr = ipaddr_addr(config->static_ip);
|
|
ip_info.netmask.addr = ipaddr_addr(config->netmask);
|
|
ip_info.gw.addr = ipaddr_addr(config->gateway);
|
|
|
|
esp_err_t ret = esp_netif_set_ip_info(eth_netif, &ip_info);
|
|
|
|
if (ret == ESP_OK) {
|
|
memcpy(¤t_config, config, sizeof(bmc_eth_config_t));
|
|
current_config.use_dhcp = false;
|
|
ESP_LOGI(TAG, "Static IP configured: %s", config->static_ip);
|
|
} else {
|
|
ESP_LOGE(TAG, "Failed to set static IP: %s", esp_err_to_name(ret));
|
|
}
|
|
|
|
xSemaphoreGive(eth_mutex);
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t ethernet_enable_dhcp(void) {
|
|
if (!eth_initialized) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
xSemaphoreTake(eth_mutex, portMAX_DELAY);
|
|
|
|
esp_err_t ret = esp_netif_dhcpc_start(eth_netif);
|
|
|
|
if (ret == ESP_OK) {
|
|
current_config.use_dhcp = true;
|
|
ESP_LOGI(TAG, "DHCP enabled");
|
|
} else {
|
|
ESP_LOGE(TAG, "Failed to enable DHCP: %s", esp_err_to_name(ret));
|
|
}
|
|
|
|
xSemaphoreGive(eth_mutex);
|
|
return ret;
|
|
}
|
|
|
|
int ethernet_get_link_speed(void) {
|
|
if (!eth_initialized || !eth_connected) {
|
|
return 0;
|
|
}
|
|
|
|
eth_speed_t speed;
|
|
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &speed);
|
|
|
|
if (ret == ESP_OK) {
|
|
return (speed == ETH_SPEED_100M) ? 100 : 10;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool ethernet_is_full_duplex(void) {
|
|
if (!eth_initialized || !eth_connected) {
|
|
return false;
|
|
}
|
|
|
|
eth_duplex_t duplex;
|
|
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &duplex);
|
|
|
|
return (ret == ESP_OK && duplex == ETH_DUPLEX_FULL);
|
|
}
|
|
|
|
esp_err_t ethernet_register_event_callback(eth_event_callback_t callback) {
|
|
event_callback = callback;
|
|
return ESP_OK;
|
|
}
|