- utils: atoip(), lefttrim() - ARP: bunch of bugfixes - TCP, FTP: WIP - DHCP: mutex; query the FSM state
284 lines
8.6 KiB
C
284 lines
8.6 KiB
C
//
|
|
// Created by epagris on 2022.10.20..
|
|
//
|
|
|
|
#include "eth_interface.h"
|
|
#include "dynmem.h"
|
|
#include "etherlib/prefab/conn_blocks/ethernet_connblock.h"
|
|
#include "etherlib_options.h"
|
|
#include "utils.h"
|
|
|
|
typedef enum {
|
|
ETH_IIE_RECV_NOTIFY = 0,
|
|
ETH_IIE_LINK_CHG_NOTIFY,
|
|
ETH_IIE_TRANSMIT_NOTIFY,
|
|
ETH_IIE_UNKNOWN = 0xFFFF
|
|
} EthIntfInternalEvent;
|
|
|
|
static int ethintf_llrecv(EthIODef *io, const RawPckt *pckt) {
|
|
ethinf_receive((EthInterface *)io->tag, pckt);
|
|
return 0;
|
|
}
|
|
|
|
static int ethintf_llrxnotify(EthIODef *io) {
|
|
ethinf_push_notification((EthInterface *)io->tag, ETH_IIE_RECV_NOTIFY, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int ethinf_lllinkchg(EthIODef *io, int ls, uint16_t speed, bool duplex) {
|
|
// TAG: speed [11:2] | duplex [1] | ls [0]
|
|
uint16_t tag = ((speed & 0x3FF) << 2) | ((duplex & 0b1) << 1) | (ls & 0b1);
|
|
ethinf_push_notification((EthInterface *)io->tag, ETH_IIE_LINK_CHG_NOTIFY, tag);
|
|
return 0;
|
|
}
|
|
|
|
static void ethintf_register(EthInterface *intf) {
|
|
intf->ioDef->tag = intf;
|
|
intf->ioDef->llRxStore = ethintf_llrecv;
|
|
intf->ioDef->llRxNotify = ethintf_llrxnotify;
|
|
intf->ioDef->llLinkChg = ethinf_lllinkchg;
|
|
}
|
|
|
|
// interface processing thread
|
|
static ThreadReturnType task_ethintf(ThreadParamType param);
|
|
|
|
EthInterface *ethintf_new(EthIODef *io) {
|
|
EthInterface *ethIntf = (EthInterface *)dynmem_alloc(sizeof(EthInterface));
|
|
ASSERT_NULL(ethIntf);
|
|
memset(ðIntf->sieve.layer0, 0, sizeof(PcktSieveLayer));
|
|
ethIntf->up = false; // the interface is down for now
|
|
ethIntf->sieve.intf = ethIntf;
|
|
ethIntf->sieve.layer0.connBReportFn = ethernet_print_report;
|
|
ethIntf->ioDef = io;
|
|
ethIntf->ip = 0;
|
|
ethIntf->arpc = arpc_new(ethIntf, ETHLIB_ARPCACHE_SIZE);
|
|
ASSERT_NULL(ethIntf->arpc);
|
|
|
|
ethIntf->txQ = mq_create(ETHLIB_DEF_MQ_SIZE);
|
|
ethIntf->rxQ = mq_create(ETHLIB_DEF_MQ_SIZE);
|
|
|
|
ethIntf->eventQ = ETHLIB_OS_QUEUE_CREATE(ETHLIB_DEF_MQ_SIZE, uint32_t);
|
|
// ETHLIB_OS_SEM_CREATE(ðIntf->eventSem, ETHLIB_DEF_MQ_SIZE);
|
|
|
|
ETHLIB_OS_THREAD_CREATE(task_ethintf, "eth", ethIntf, osPriorityRealtime, 2048);
|
|
|
|
ethIntf->ipra = ipra_new(ethIntf);
|
|
|
|
ethIntf->capabilities = 0;
|
|
|
|
ethIntf->dhcp = NULL;
|
|
ethIntf->manageDhcp = true; // by default, enable automatic DHCP management
|
|
|
|
memset(ðIntf->evtCbTable, 0, sizeof(EthIntfEventCbTable));
|
|
|
|
ethIntf->interceptTxCb = NULL;
|
|
ethIntf->interceptRxCb = NULL;
|
|
|
|
ethIntf->linkState = false;
|
|
|
|
ethintf_register(ethIntf);
|
|
|
|
return ethIntf;
|
|
}
|
|
|
|
static ThreadReturnType task_ethintf(ThreadParamType param) {
|
|
EthInterface *intf = (EthInterface *)param;
|
|
while (true) {
|
|
// fetch notification
|
|
uint16_t event_code = ETH_IIE_UNKNOWN;
|
|
uint16_t event_data;
|
|
ethinf_pull_notification(intf, &event_code, &event_data);
|
|
|
|
// act accordingly...
|
|
switch (event_code) {
|
|
case ETH_IIE_RECV_NOTIFY: {
|
|
intf->ioDef->llRxRead(intf->ioDef); // read packets, will invoke RxStore
|
|
while (mq_avail(intf->rxQ) > 0) {
|
|
// get the raw packet
|
|
RawPckt rawPckt = mq_top(intf->rxQ);
|
|
mq_pop(intf->rxQ);
|
|
|
|
// packet interception, if defined
|
|
if (intf->interceptRxCb != NULL) {
|
|
intf->interceptRxCb(intf, &rawPckt);
|
|
}
|
|
|
|
// packet processing
|
|
packsieve_input(&intf->sieve, &rawPckt);
|
|
}
|
|
|
|
} break;
|
|
case ETH_IIE_LINK_CHG_NOTIFY: {
|
|
bool ls = event_data & 0b1;
|
|
bool duplex = (event_data >> 1) & 0b1;
|
|
uint16_t speed = (event_data >> 2);
|
|
|
|
bool prev_ls = ethinf_get_link_state(intf); // get previous link state
|
|
ethinf_set_link_state(intf, ls); // set new link state
|
|
|
|
if ((intf->manageDhcp) && (intf->dhcp != NULL) && (ls != prev_ls)) { // no double start!
|
|
if (ls) { // if link is on
|
|
dhcp_start(intf->dhcp);
|
|
} else { // if link is off
|
|
dhcp_stop(intf->dhcp);
|
|
}
|
|
}
|
|
|
|
// print generic message
|
|
MSG("ETH LINK: %s%s", (ls ? (ANSI_COLOR_BGREEN "UP ") : (ANSI_COLOR_BRED "DOWN\n")), ANSI_COLOR_RESET);
|
|
|
|
// print link properties
|
|
if (ls) {
|
|
MSG("(%u Mbps, %s duplex)\n", speed, duplex ? "FULL" : "HALF");
|
|
}
|
|
} break;
|
|
case ETH_IIE_TRANSMIT_NOTIFY: {
|
|
intf->ioDef->llTxTrigger(intf->ioDef, intf->txQ);
|
|
} break;
|
|
default:
|
|
MSG("UNKNOWN event!\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ethinf_receive(EthInterface *intf, const RawPckt *rawPckt) {
|
|
bool pushOK = mq_push(intf->rxQ, rawPckt);
|
|
if (!pushOK) {
|
|
dynmem_free(rawPckt->payload);
|
|
ERROR("Interface RECEIVE queue full, packet dropped!\n");
|
|
}
|
|
}
|
|
|
|
void ethinf_transmit(EthInterface *intf, const RawPckt *rawPckt) {
|
|
// if link is down, discard packets
|
|
if (!ethinf_get_link_state(intf)) {
|
|
dynmem_free(rawPckt->payload);
|
|
return;
|
|
}
|
|
|
|
// packet interception, if defined
|
|
if (intf->interceptTxCb != NULL) {
|
|
intf->interceptTxCb(intf, rawPckt);
|
|
}
|
|
|
|
// push packet onto the message queue
|
|
if (mq_push(intf->txQ, rawPckt)) {
|
|
ethinf_push_notification(intf, ETH_IIE_TRANSMIT_NOTIFY, 0); // notify processing thread of the peding transmit
|
|
} else {
|
|
ERROR("Interface TRANSMIT queue full, packet dropped!\n");
|
|
}
|
|
}
|
|
|
|
void ethinf_push_notification(EthInterface *intf, uint16_t event_code, uint16_t event_data) {
|
|
if (!intf->up) { // disabled interfaces cannot receive notifications
|
|
return;
|
|
}
|
|
|
|
uint32_t cpd = (event_data << 16) | event_code; // create event compound
|
|
ETHLIB_OS_QUEUE_PUSH(intf->eventQ, &cpd);
|
|
}
|
|
|
|
void ethinf_pull_notification(EthInterface *intf, uint16_t *event_code, uint16_t *event_data) {
|
|
uint32_t cpd;
|
|
ETHLIB_OS_QUEUE_POP(intf->eventQ, &cpd); // wait for event semaphore
|
|
|
|
// retrieve event code and data
|
|
*event_code = cpd & 0xFFFF;
|
|
*event_data = (cpd >> 16) & 0xFFFF;
|
|
|
|
// MSG("EC: %d\n", *event_code);
|
|
}
|
|
|
|
void ethinf_set_capabilities(EthInterface *intf, uint32_t cap) {
|
|
intf->capabilities = cap;
|
|
}
|
|
|
|
void ethinf_set_event_callback(EthInterface *intf, EthIntfEvIndex idx, EthIntfEvtCb cb) {
|
|
((EthIntfEvtCb *)((&(intf->evtCbTable))))[idx] = cb;
|
|
}
|
|
|
|
void ethinf_trigger_event(EthInterface *intf, EthIntfEvIndex idx) {
|
|
EthIntfEvtCb cb = ((EthIntfEvtCb *)((&(intf->evtCbTable))))[idx];
|
|
if (cb != NULL) {
|
|
cb(intf);
|
|
}
|
|
}
|
|
|
|
void ethinf_set_link_state(EthInterface *intf, bool ls) {
|
|
if (intf->linkState != ls) { // trigger event only if state has actually changed
|
|
intf->linkState = ls;
|
|
ethinf_trigger_event(intf, ETH_EVT_LINK_CHANGE);
|
|
}
|
|
}
|
|
|
|
bool ethinf_get_link_state(EthInterface *intf) {
|
|
return intf->linkState;
|
|
}
|
|
|
|
void ethinf_up(EthInterface *intf) {
|
|
intf->up = true;
|
|
|
|
// trigger a link change interrupt to query link state
|
|
if (intf->ioDef->llTrigLinkChg != NULL) {
|
|
intf->ioDef->llTrigLinkChg(intf->ioDef);
|
|
}
|
|
}
|
|
|
|
void ethinf_down(EthInterface *intf) {
|
|
intf->up = false;
|
|
}
|
|
|
|
void ethinf_set_intercept_callback(EthInterface *intf, EthIntfInterceptCb cb, EthIntfInterceptDir dir) {
|
|
if (dir & ETH_INTERCEPT_TX) {
|
|
intf->interceptTxCb = cb;
|
|
}
|
|
|
|
if (dir & ETH_INTERCEPT_RX) {
|
|
intf->interceptRxCb = cb;
|
|
}
|
|
}
|
|
|
|
void ethinf_set_automatic_dhcp_management(EthInterface *intf, bool automg) {
|
|
intf->manageDhcp = automg;
|
|
}
|
|
|
|
void ethinf_set_config(EthInterface *intf, const EthInterfaceNetworkConfig *config) {
|
|
intf->ip = config->ip;
|
|
intf->router = config->router;
|
|
intf->netmask = config->netmask;
|
|
intf->dns = config->dns;
|
|
|
|
intf->manageDhcp = config->manageDhcp;
|
|
}
|
|
|
|
void ethinf_get_config(EthInterface *intf, EthInterfaceNetworkConfig *config) {
|
|
config->ip = intf->ip;
|
|
config->router = intf->router;
|
|
config->netmask = intf->netmask;
|
|
config->dns = intf->dns;
|
|
|
|
config->manageDhcp = intf->manageDhcp;
|
|
}
|
|
|
|
void ethinf_print_info(const EthInterface * intf) {
|
|
MSG("IP: " ANSI_COLOR_BYELLOW);
|
|
PRINT_IPv4(intf->ip);
|
|
MSG(ANSI_COLOR_RESET "\nRouter: ");
|
|
PRINT_IPv4(intf->router);
|
|
MSG("\nNetmask: ");
|
|
PRINT_IPv4(intf->netmask);
|
|
MSG("\nDNS: ");
|
|
PRINT_IPv4(intf->dns);
|
|
MSG("\n\n");
|
|
|
|
if (intf->dhcp != NULL) {
|
|
bool on = dhcp_get_fsm_state(intf->dhcp) != DHCP_STOPPED;
|
|
MSG("DHCP is %s" ANSI_COLOR_RESET "%s.", on ? (ANSI_COLOR_BGREEN "ON") : (ANSI_COLOR_BRED "OFF"), intf->manageDhcp ? "[automanaged]" : "");
|
|
} else {
|
|
MSG ("DHCP is " ANSI_COLOR_RED "absent" ANSI_COLOR_RESET ".");
|
|
}
|
|
|
|
MSG("\n");
|
|
}
|