1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/esp32/esp-eth/esp_eth_netdev.c

409 lines
11 KiB
C
Raw Normal View History

2018-10-08 12:20:49 +02:00
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32_esp_eth
* @{
*
* @file
* @brief Netdev interface for the ESP32 Ethernet MAC (EMAC) module
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifdef MODULE_ESP_ETH
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "log.h"
#include "tools.h"
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "net/gnrc/netif/ethernet.h"
#include "net/gnrc.h"
#include "net/ethernet.h"
#include "net/netdev/eth.h"
#include "xtimer.h"
#include "esp_common.h"
#include "esp_attr.h"
#include "irq_arch.h"
#include "esp_eth_params.h"
#include "esp_eth_netdev.h"
#include "esp32/esp_event_loop.h"
#if !defined(EMAC_PHY_SMI_MDC_PIN) || !defined(EMAC_PHY_SMI_MDIO_PIN)
#error Board definition does not provide EMAC configuration
#endif
#define SYSTEM_EVENT_ETH_RX_DONE (SYSTEM_EVENT_MAX + 1)
#define SYSTEM_EVENT_ETH_TX_DONE (SYSTEM_EVENT_MAX + 2)
#define EMAC_PHY_CLOCK_DELAY (500 * USEC_PER_MSEC) /* given in us */
#ifdef EMAC_PHY_LAN8720
#include "eth_phy/phy_lan8720.h"
#define EMAC_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
#endif
#ifdef EMAC_PHY_TLK110
#include "eth_phy/phy_tlk110.h"
#define EMAC_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config
#endif
/**
* There is only one ESP-ETH device. We define it as static device variable
2019-10-23 21:13:52 +02:00
* to have access to the device inside ESP-ETH interrupt routines which do
2018-10-08 12:20:49 +02:00
* not provide an argument that could be used as pointer to the ESP-ETH
* device which triggers the interrupt.
*/
esp_eth_netdev_t _esp_eth_dev;
2018-10-08 12:20:49 +02:00
static void _eth_gpio_config_rmii(void)
{
DEBUG("%s\n", __func__);
/*
* RMII data pins are fixed:
* TXD0 = GPIO19
* TXD1 = GPIO22
* TX_EN = GPIO21
* RXD0 = GPIO25
* RXD1 = GPIO26
* CLK = GPIO0
*/
phy_rmii_configure_data_interface_pins();
/* SMI pins can be configured in board definition */
phy_rmii_smi_configure_pins(EMAC_PHY_SMI_MDC_PIN, EMAC_PHY_SMI_MDIO_PIN);
}
static esp_err_t IRAM_ATTR _eth_input_callback(void *buffer, uint16_t len, void *eb)
{
DEBUG("%s: buf=%p len=%d eb=%p\n", __func__, buffer, len, eb);
CHECK_PARAM_RET (buffer != NULL, -EINVAL);
CHECK_PARAM_RET (len <= ETHERNET_MAX_LEN, -EINVAL);
2018-10-08 12:20:49 +02:00
mutex_lock(&_esp_eth_dev.dev_lock);
memcpy(_esp_eth_dev.rx_buf, buffer, len);
_esp_eth_dev.rx_len = len;
_esp_eth_dev.event = SYSTEM_EVENT_ETH_RX_DONE;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
mutex_unlock(&_esp_eth_dev.dev_lock);
return ESP_OK;
}
static void _esp_eth_phy_power_enable_gpio(bool enable)
{
assert(EMAC_ETHERNET_PHY_CONFIG.phy_power_enable);
if (EMAC_PHY_POWER_PIN == GPIO_UNDEF) {
return;
}
2018-10-08 12:20:49 +02:00
if (!enable) {
EMAC_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
gpio_init(EMAC_PHY_POWER_PIN, GPIO_OUT);
gpio_write(EMAC_PHY_POWER_PIN, enable);
xtimer_usleep (USEC_PER_MSEC);
if (enable) {
EMAC_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
static int _esp_eth_init(netdev_t *netdev)
{
DEBUG("%s: netdev=%p\n", __func__, netdev);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
esp_err_t ret = ESP_OK;
eth_config_t config = EMAC_ETHERNET_PHY_CONFIG;
/* Set configuration */
config.phy_addr = EMAC_PHY_ADDRESS;
config.clock_mode = EMAC_PHY_CLOCK_MODE;
config.gpio_config = _eth_gpio_config_rmii;
config.tcpip_input = _eth_input_callback;
/*
* Replace the default 'power enable' function with an example-specific
* one that toggles a power GPIO.
*/
if (EMAC_PHY_POWER_PIN != GPIO_UNDEF) {
config.phy_power_enable = _esp_eth_phy_power_enable_gpio;
}
2018-10-08 12:20:49 +02:00
/* initialize EMAC with config */
if (ret == ESP_OK && (ret = esp_eth_init(&config)) != ESP_OK) {
LOG_TAG_ERROR("esp_eth", "initialization failed");
}
/*
* after activating clock logic it can take some time before we can
* enable EMAC
*/
xtimer_usleep (EMAC_PHY_CLOCK_DELAY);
if (ret == ESP_OK && (ret = esp_eth_enable()) != ESP_OK) {
LOG_TAG_ERROR("esp_eth", "enable failed");
}
mutex_unlock(&dev->dev_lock);
return (ret == ESP_OK) ? 0 : -1;
}
static int _esp_eth_send(netdev_t *netdev, const iolist_t *iolist)
{
DEBUG("%s: netdev=%p iolist=%p\n", __func__, netdev, iolist);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (iolist != NULL, -EINVAL);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
if (!_esp_eth_dev.link_up) {
DEBUG("%s: link is down\n", __func__);
return -ENODEV;
}
mutex_lock(&dev->dev_lock);
dev->tx_len = 0;
/* load packet data into TX buffer */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
if (dev->tx_len + iol->iol_len > ETHERNET_MAX_LEN) {
2018-10-08 12:20:49 +02:00
mutex_unlock(&dev->dev_lock);
return -EOVERFLOW;
}
if (iol->iol_len) {
memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len);
dev->tx_len += iol->iol_len;
}
2018-10-08 12:20:49 +02:00
}
#if ENABLE_DEBUG
printf ("%s: send %d byte\n", __func__, dev->tx_len);
/* esp_hexdump (dev->tx_buf, dev->tx_len, 'b', 16); */
#endif
int ret = 0;
/* send the the packet to the peer(s) mac address */
if (esp_eth_tx(dev->tx_buf, dev->tx_len) == ESP_OK) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
else {
ret = -EIO;
}
mutex_unlock(&dev->dev_lock);
return ret;
}
static int _esp_eth_recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
DEBUG("%s: netdev=%p buf=%p len=%u info=%p\n",
__func__, netdev, buf, len, info);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
int size = dev->rx_len;
2018-10-08 12:20:49 +02:00
if (!buf) {
/* get the size of the frame; if len > 0 then also drop the frame */
if (len > 0) {
/* drop frame requested */
dev->rx_len = 0;
}
2018-10-08 12:20:49 +02:00
mutex_unlock(&dev->dev_lock);
return size;
}
if (dev->rx_len > len) {
/* buffer is smaller than the number of received bytes */
DEBUG("%s: Not enough space in receive buffer for %d bytes\n",
__func__, dev->rx_len);
mutex_unlock(&dev->dev_lock);
return -ENOBUFS;
}
2018-10-08 12:20:49 +02:00
#if ENABLE_DEBUG
/* esp_hexdump (dev->rx_buf, dev->rx_len, 'b', 16); */
#endif
2018-10-08 12:20:49 +02:00
/* copy received date and reset the receive length */
memcpy(buf, dev->rx_buf, dev->rx_len);
dev->rx_len = 0;
2018-10-08 12:20:49 +02:00
mutex_unlock(&dev->dev_lock);
return size;
2018-10-08 12:20:49 +02:00
}
static int _esp_eth_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
DEBUG("%s: netdev=%p opt=%s val=%p len=%u\n",
__func__, netdev, netopt2str(opt), val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
2018-10-08 12:20:49 +02:00
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len >= ETHERNET_ADDR_LEN);
2018-10-08 12:20:49 +02:00
esp_eth_get_mac((uint8_t *)val);
return ETHERNET_ADDR_LEN;
case NETOPT_LINK_CONNECTED:
assert(max_len == 1);
*((netopt_enable_t *)val) = (dev->link_up) ? NETOPT_ENABLE
: NETOPT_DISABLE;
return sizeof(netopt_enable_t);
2018-10-08 12:20:49 +02:00
default:
return netdev_eth_get(netdev, opt, val, max_len);
}
}
static int _esp_eth_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len)
{
DEBUG("%s: netdev=%p opt=%s val=%p len=%u\n",
__func__, netdev, netopt2str(opt), val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len == ETHERNET_ADDR_LEN);
esp_eth_set_mac((uint8_t *)val);
return ETHERNET_ADDR_LEN;
default:
return netdev_eth_set(netdev, opt, val, max_len);
}
}
static void _esp_eth_isr(netdev_t *netdev)
{
DEBUG("%s: netdev=%p\n", __func__, netdev);
CHECK_PARAM (netdev != NULL);
esp_eth_netdev_t *dev = (esp_eth_netdev_t *) netdev;
switch (dev->event) {
case SYSTEM_EVENT_ETH_RX_DONE:
/* if data were received */
if (dev->rx_len) {
dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
break;
case SYSTEM_EVENT_ETH_CONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP);
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN);
break;
default:
break;
}
_esp_eth_dev.event = SYSTEM_EVENT_MAX; /* no event */
return;
}
static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_ETH_START:
DEBUG("%s: Ethernet started\n", __func__);
break;
case SYSTEM_EVENT_ETH_STOP:
DEBUG("%s: Ethernet started\n", __func__);
break;
case SYSTEM_EVENT_ETH_CONNECTED:
DEBUG("%s Ethernet link up\n", __func__);
_esp_eth_dev.link_up = true;
if (SYSTEM_EVENT_MAX) {
_esp_eth_dev.event = SYSTEM_EVENT_ETH_CONNECTED;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
}
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG("%s: Ethernet link up\n", __func__);
_esp_eth_dev.link_up = false;
if (SYSTEM_EVENT_MAX) {
_esp_eth_dev.event = SYSTEM_EVENT_ETH_DISCONNECTED;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
}
break;
default:
DEBUG("%s: event=%d\n", __func__, event->event_id);
break;
}
return ESP_OK;
}
static const netdev_driver_t _esp_eth_driver =
{
.send = _esp_eth_send,
.recv = _esp_eth_recv,
.init = _esp_eth_init,
.isr = _esp_eth_isr,
.get = _esp_eth_get,
.set = _esp_eth_set,
};
extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler,
void *arg);
void esp_eth_setup(esp_eth_netdev_t* dev)
2018-10-08 12:20:49 +02:00
{
(void)dev;
2018-10-08 12:20:49 +02:00
LOG_TAG_INFO("esp_eth", "initializing ESP32 Ethernet MAC (EMAC) device\n");
/* initialize locking */
mutex_init(&_esp_eth_dev.dev_lock);
/* init esp system event loop */
esp_system_event_add_handler(_esp_system_event_handler, NULL);
/* set the netdev driver */
_esp_eth_dev.netdev.driver = &_esp_eth_driver;
/* initialize netdev data structure */
_esp_eth_dev.event = SYSTEM_EVENT_MAX; /* no event */
_esp_eth_dev.link_up = false;
_esp_eth_dev.rx_len = 0;
_esp_eth_dev.tx_len = 0;
}
2018-10-08 12:20:49 +02:00
#endif /* MODULE_ESP_ETH */
/**@}*/