361 lines
9.3 KiB
C
361 lines
9.3 KiB
C
|
|
#include <string.h>
|
|
|
|
#include "module.h"
|
|
#include "lauxlib.h"
|
|
#include "lextra.h"
|
|
#include "lmem.h"
|
|
#include "nodemcu_esp_event.h"
|
|
#include "ip_fmt.h"
|
|
#include "common.h"
|
|
|
|
#include "driver/gpio.h"
|
|
|
|
// phy includes
|
|
#include "eth_phy/phy_lan8720.h"
|
|
#include "eth_phy/phy_tlk110.h"
|
|
#include "eth_phy/phy_ip101.h"
|
|
|
|
|
|
typedef enum {
|
|
ETH_PHY_LAN8720 = 0,
|
|
ETH_PHY_TLK110,
|
|
ETH_PHY_IP101,
|
|
ETH_PHY_MAX
|
|
} eth_phy_t;
|
|
|
|
static struct {
|
|
const eth_config_t *eth_config;
|
|
int pin_power;
|
|
gpio_num_t pin_mdc, pin_mdio;
|
|
} module_config;
|
|
|
|
|
|
|
|
static void phy_device_power_enable_via_gpio( bool enable )
|
|
{
|
|
if (!enable)
|
|
module_config.eth_config->phy_power_enable( false );
|
|
|
|
gpio_pad_select_gpio( module_config.pin_power );
|
|
gpio_set_level( module_config.pin_power, enable ? 1 : 0 );
|
|
gpio_set_direction( module_config.pin_power, GPIO_MODE_OUTPUT );
|
|
|
|
// Allow the power up/down to take effect, min 300us
|
|
//vTaskDelay(1);
|
|
|
|
if (enable)
|
|
module_config.eth_config->phy_power_enable(true);
|
|
}
|
|
|
|
/**
|
|
* @brief gpio specific init
|
|
*
|
|
* @note RMII data pins are fixed in esp32:
|
|
* TXD0 <=> GPIO19
|
|
* TXD1 <=> GPIO22
|
|
* TX_EN <=> GPIO21
|
|
* RXD0 <=> GPIO25
|
|
* RXD1 <=> GPIO26
|
|
* CLK <=> GPIO0
|
|
*
|
|
*/
|
|
static void eth_gpio_config_rmii( void )
|
|
{
|
|
phy_rmii_configure_data_interface_pins();
|
|
phy_rmii_smi_configure_pins( module_config.pin_mdc, module_config.pin_mdio );
|
|
}
|
|
|
|
|
|
|
|
// --- Event handling -----------------------------------------------------
|
|
|
|
typedef void (*fill_cb_arg_fn) (lua_State *L, const system_event_t *evt);
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
system_event_id_t event_id;
|
|
fill_cb_arg_fn fill_cb_arg;
|
|
} event_desc_t;
|
|
|
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
static void eth_got_ip (lua_State *L, const system_event_t *evt);
|
|
static void empty_arg (lua_State *L, const system_event_t *evt) {}
|
|
|
|
static const event_desc_t events[] =
|
|
{
|
|
{ "start", SYSTEM_EVENT_ETH_START, empty_arg },
|
|
{ "stop", SYSTEM_EVENT_ETH_STOP, empty_arg },
|
|
{ "connected", SYSTEM_EVENT_ETH_CONNECTED, empty_arg },
|
|
{ "disconnected", SYSTEM_EVENT_ETH_DISCONNECTED, empty_arg },
|
|
{ "got_ip", SYSTEM_EVENT_ETH_GOT_IP, eth_got_ip },
|
|
};
|
|
|
|
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
|
static int event_cb[ARRAY_LEN(events)];
|
|
|
|
static int eth_event_idx_by_id( system_event_id_t id )
|
|
{
|
|
for (unsigned i = 0; i < ARRAY_LEN(events); ++i)
|
|
if (events[i].event_id == id)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
static int eth_event_idx_by_name( const char *name )
|
|
{
|
|
for (unsigned i = 0; i < ARRAY_LEN(events); ++i)
|
|
if (strcmp( events[i].name, name ) == 0)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void eth_got_ip( lua_State *L, const system_event_t *evt )
|
|
{
|
|
(void)evt;
|
|
tcpip_adapter_ip_info_t ip_info;
|
|
|
|
memset(&ip_info, 0, sizeof(tcpip_adapter_ip_info_t));
|
|
if (tcpip_adapter_get_ip_info( ESP_IF_ETH, &ip_info ) != ESP_OK) {
|
|
luaL_error( L, "error from tcpip_adapter_get_ip_info!" );
|
|
}
|
|
|
|
// on_event() has prepared a table on top of stack. fill it with cb-specific fields:
|
|
// ip, netmask, gw
|
|
char ipstr[IP_STR_SZ] = { 0 };
|
|
ip4str( ipstr, &ip_info.ip );
|
|
lua_pushstring( L, ipstr );
|
|
lua_setfield( L, -2, "ip" );
|
|
|
|
ip4str( ipstr, &ip_info.netmask );
|
|
lua_pushstring( L, ipstr );
|
|
lua_setfield( L, -2, "netmask" );
|
|
|
|
ip4str(ipstr, &ip_info.gw );
|
|
lua_pushstring( L, ipstr );
|
|
lua_setfield( L, -2, "gw" );
|
|
}
|
|
|
|
static void on_event( const system_event_t *evt )
|
|
{
|
|
int idx = eth_event_idx_by_id( evt->event_id );
|
|
if (idx < 0 || event_cb[idx] == LUA_NOREF)
|
|
return;
|
|
|
|
lua_State *L = lua_getstate();
|
|
int top = lua_gettop( L );
|
|
lua_rawgeti( L, LUA_REGISTRYINDEX, event_cb[idx] );
|
|
lua_pushstring( L, events[idx].name );
|
|
lua_createtable( L, 0, 5 );
|
|
events[idx].fill_cb_arg( L, evt );
|
|
lua_pcall( L, 2, 0, 0 );
|
|
|
|
lua_settop( L, top );
|
|
}
|
|
|
|
NODEMCU_ESP_EVENT(SYSTEM_EVENT_ETH_START, on_event);
|
|
NODEMCU_ESP_EVENT(SYSTEM_EVENT_ETH_STOP, on_event);
|
|
NODEMCU_ESP_EVENT(SYSTEM_EVENT_ETH_CONNECTED, on_event);
|
|
NODEMCU_ESP_EVENT(SYSTEM_EVENT_ETH_DISCONNECTED, on_event);
|
|
NODEMCU_ESP_EVENT(SYSTEM_EVENT_ETH_GOT_IP, on_event);
|
|
|
|
|
|
|
|
// Lua API
|
|
|
|
static int leth_set_mac( lua_State *L )
|
|
{
|
|
uint8_t mac[6];
|
|
const char *macstr = luaL_checkstring( L, 1 );
|
|
|
|
if (6 != sscanf( macstr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] )) {
|
|
return luaL_error( L, "invalid mac string" );
|
|
}
|
|
|
|
if (ESP_OK != esp_eth_set_mac( mac )) {
|
|
return luaL_error( L, "error setting mac" );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leth_get_mac( lua_State *L )
|
|
{
|
|
char temp[64];
|
|
uint8_t mac[6];
|
|
|
|
esp_eth_get_mac( mac );
|
|
snprintf( temp, 63, MACSTR, MAC2STR(mac) );
|
|
lua_pushstring( L, temp );
|
|
return 1;
|
|
}
|
|
|
|
static int leth_set_ip(lua_State *L )
|
|
{
|
|
tcpip_adapter_ip_info_t ipInfo;
|
|
tcpip_adapter_dns_info_t dnsinfo;
|
|
size_t len;
|
|
const char *str;
|
|
|
|
ip_addr_t ipAddr;
|
|
ipAddr.type = IPADDR_TYPE_V4;
|
|
|
|
luaL_checkanytable (L, 1);
|
|
|
|
//memset(&ipInfo, 0, sizeof(tcpip_adapter_ip_info_t));
|
|
|
|
lua_getfield (L, 1, "ip");
|
|
str = luaL_checklstring (L, -1, &len);
|
|
if(!ipaddr_aton(str, &ipAddr))
|
|
{
|
|
return luaL_error(L, "Could not parse IP address, aborting");
|
|
}
|
|
ipInfo.ip = ipAddr.u_addr.ip4;
|
|
|
|
lua_getfield (L, 1, "netmask");
|
|
str = luaL_checklstring (L, -1, &len);
|
|
if(!ipaddr_aton(str, &ipAddr))
|
|
{
|
|
return luaL_error(L, "Could not parse Netmask, aborting");
|
|
}
|
|
ipInfo.netmask = ipAddr.u_addr.ip4;
|
|
|
|
lua_getfield (L, 1, "gateway");
|
|
str = luaL_checklstring (L, -1, &len);
|
|
if(!ipaddr_aton(str, &ipAddr))
|
|
{
|
|
return luaL_error(L, "Could not parse Gateway address, aborting");
|
|
}
|
|
ipInfo.gw = ipAddr.u_addr.ip4;
|
|
|
|
lua_getfield (L, 1, "dns");
|
|
str = luaL_optlstring(L, -1, str, &len);
|
|
if(!ipaddr_aton(str, &dnsinfo.ip))
|
|
{
|
|
return luaL_error(L, "Could not parse DNS address, aborting");
|
|
}
|
|
|
|
ESP_ERROR_CHECK(tcpip_adapter_dhcpc_stop(ESP_IF_ETH));
|
|
tcpip_adapter_set_ip_info(ESP_IF_ETH, &ipInfo);
|
|
tcpip_adapter_set_dns_info(ESP_IF_ETH, TCPIP_ADAPTER_DNS_MAIN, &dnsinfo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leth_get_speed( lua_State *L )
|
|
{
|
|
eth_speed_mode_t speed = esp_eth_get_speed();
|
|
switch (speed) {
|
|
case ETH_SPEED_MODE_10M:
|
|
lua_pushnumber( L, 10 );
|
|
break;
|
|
case ETH_SPEED_MODE_100M:
|
|
lua_pushnumber( L, 100 );
|
|
break;
|
|
default:
|
|
return luaL_error( L, "invalid speed" );
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int leth_on( lua_State *L )
|
|
{
|
|
const char *event_name = luaL_checkstring( L, 1 );
|
|
if (!lua_isnoneornil( L, 2 )) {
|
|
luaL_checkanyfunction( L, 2 );
|
|
}
|
|
lua_settop( L, 2 );
|
|
|
|
int idx = eth_event_idx_by_name( event_name );
|
|
if (idx < 0)
|
|
return luaL_error( L, "unknown event: %s", event_name );
|
|
|
|
luaL_unref( L, LUA_REGISTRYINDEX, event_cb[idx] );
|
|
event_cb[idx] = lua_isnoneornil( L, 2 ) ?
|
|
LUA_NOREF : luaL_ref( L, LUA_REGISTRYINDEX );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int leth_init( lua_State *L )
|
|
{
|
|
int stack = 0;
|
|
int top = lua_gettop( L );
|
|
|
|
luaL_checktype( L, ++stack, LUA_TTABLE );
|
|
// temporarily copy option table to top of stack for opt_ functions
|
|
lua_pushvalue( L, stack );
|
|
|
|
eth_phy_t phy = opt_checkint_range( L, "phy", -1, 0, ETH_PHY_MAX );
|
|
eth_phy_base_t phy_addr = opt_checkint_range( L, "addr", -1, 0, PHY31 );
|
|
eth_clock_mode_t clock_mode = opt_checkint_range( L, "clock_mode", -1, 0, ETH_CLOCK_GPIO17_OUT );
|
|
module_config.pin_power = opt_checkint_range( L, "power", -1, -1, GPIO_NUM_MAX-1 ); // optional
|
|
module_config.pin_mdc = opt_checkint_range( L, "mdc", -1, GPIO_NUM_0, GPIO_NUM_MAX-1 );
|
|
module_config.pin_mdio = opt_checkint_range( L, "mdio", -1, GPIO_NUM_0, GPIO_NUM_MAX-1 );
|
|
|
|
lua_settop( L, top );
|
|
|
|
eth_config_t config;
|
|
|
|
switch (phy) {
|
|
case ETH_PHY_LAN8720:
|
|
config = phy_lan8720_default_ethernet_config;
|
|
module_config.eth_config = &phy_lan8720_default_ethernet_config;
|
|
break;
|
|
case ETH_PHY_TLK110:
|
|
config = phy_tlk110_default_ethernet_config;
|
|
module_config.eth_config = &phy_tlk110_default_ethernet_config;
|
|
break;
|
|
case ETH_PHY_IP101:
|
|
config = phy_ip101_default_ethernet_config;
|
|
module_config.eth_config = &phy_ip101_default_ethernet_config;
|
|
break;
|
|
default:
|
|
// prevented by opt_checkint_range
|
|
break;
|
|
};
|
|
|
|
config.phy_addr = phy_addr;
|
|
config.gpio_config = eth_gpio_config_rmii;
|
|
config.tcpip_input = tcpip_adapter_eth_input;
|
|
config.clock_mode = clock_mode;
|
|
|
|
if (module_config.pin_power >= GPIO_NUM_0) {
|
|
// power pin is optional
|
|
config.phy_power_enable = phy_device_power_enable_via_gpio;
|
|
}
|
|
|
|
if (esp_eth_init( &config ) != ESP_OK) {
|
|
luaL_error( L, "esp_eth_init failed" );
|
|
}
|
|
if (esp_eth_enable() != ESP_OK) {
|
|
luaL_error( L, "esp_eth_enable failed" );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LROT_BEGIN(eth)
|
|
LROT_FUNCENTRY( init, leth_init )
|
|
LROT_FUNCENTRY( on, leth_on )
|
|
LROT_FUNCENTRY( get_speed, leth_get_speed )
|
|
LROT_FUNCENTRY( get_mac, leth_get_mac )
|
|
LROT_FUNCENTRY( set_mac, leth_set_mac )
|
|
LROT_FUNCENTRY( set_ip, leth_set_ip )
|
|
|
|
LROT_NUMENTRY( PHY_LAN8720, ETH_PHY_LAN8720 )
|
|
LROT_NUMENTRY( PHY_TLK110, ETH_PHY_TLK110 )
|
|
LROT_NUMENTRY( PHY_IP101, ETH_PHY_IP101 )
|
|
|
|
LROT_NUMENTRY( CLOCK_GPIO0_IN, ETH_CLOCK_GPIO0_IN )
|
|
LROT_NUMENTRY( CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO0_OUT )
|
|
LROT_NUMENTRY( CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO16_OUT )
|
|
LROT_NUMENTRY( CLOCK_GPIO17_OUT, ETH_CLOCK_GPIO17_OUT )
|
|
LROT_END(eth, NULL, 0)
|
|
|
|
NODEMCU_MODULE(ETH, "eth", eth, NULL);
|