EMAC driver for the ENC28J60 Ethernet controller. This is a simplified fork of https://github.com/tobiasjaster/ENC28J60-EMAC-Driver published by Tobias Jaster.

Dependents:   MQTT_Hello MQTT_HelloENC28J60

EMAC driver for the ENC28J60 Ethernet controller

https://os.mbed.com/media/uploads/hudakz/enc28j60_module01.jpg

This is a fork (the INT and RST pins are not used) of the ENC28J60-EMAC driver published by Tobias Jaster at

https://github.com/tobiasjaster/ENC28J60-EMAC-Driver

Usage:

  • Connect the ENC28J60 module to the Mbed board as follows:

https://os.mbed.com/media/uploads/hudakz/enc28j60-emac.png

  • Import (add) this ENC28J60-EMAC library to your program.
  • Add a "mbed_app.json" file with the following content to the root directory of your program:

    {
        "target_overrides": {
            "*": {
                "platform.callback-nontrivial": true,
                "enc28j60-emac.mosi":  "D11",
                "enc28j60-emac.miso":  "D12",
                "enc28j60-emac.sck" :  "D13",
                "enc28j60-emac.cs"  :  "D10"
            }
        }
    }
  • Replace "D11", ..., "D10" with the actual pin names you selected on the Mbed board to be used for the SPI communication.
  • To set the MAC address define an array with the desired address bytes and call the "set_hwaddr(mac)" function before calling the network interface "connect" function.

For example:

    const uint8_t       MAC[6] = { 0, 1, 2, 3, 4, 5 };
    EthernetInterface   net;
 
    int main()
    {
        net.get_emac().set_hwaddr(MAC);             // set MAC address
        if (net.connect() != 0) {
            printf("Error: Unable to connect to the network.\n");
            return -1;
        }
     ...

Files at this revision

API Documentation at this revision

Comitter:
hudakz
Date:
Sat Mar 27 22:52:41 2021 +0000
Parent:
1:bce04bfc41fe
Child:
3:aa88808326b9
Commit message:
Mbed OS Ethernet MAC (EMAC) driver for the ENC28J60 Ethernet controller. This a simplified fork of https://github.com/tobiasjaster/ENC28J60-EMAC-Driver published by Tobias Jaster.

Changed in this revision

enc28j60_emac.cpp Show annotated file Show diff for this revision Revisions of this file
enc28j60_emac.h Show annotated file Show diff for this revision Revisions of this file
--- a/enc28j60_emac.cpp	Sat Mar 27 22:45:46 2021 +0000
+++ b/enc28j60_emac.cpp	Sat Mar 27 22:52:41 2021 +0000
@@ -15,53 +15,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "cmsis.h"
-#include "enc28j60.h"
-
-#ifndef NULL
-#define NULL    ((void*)0)
-#endif
-#define ENC28J60_DEBUG  0
-
-#if ENC28J60_DEBUG
-#define ENC28J60_DEBUG_PRINTF(...)  printf(__VA_ARGS__)
-#else
-#define ENC28J60_DEBUG_PRINTF(...)
-#endif
-/** Millisec timeout macros */
-#define RESET_TIME_OUT_MS       10ms
-#define REG_WRITE_TIME_OUT_MS   50ms
-#define PHY_RESET_TIME_OUT_MS   100ms
-#define INIT_FINISH_DELAY       2000ms
+#include "mbed_interface.h"
+#include "mbed_wait_api.h"
+#include "mbed_assert.h"
+#include "netsocket/nsapi_types.h"
+#include "mbed_shared_queues.h"
 
-/**
- * \brief TX FIFO Size definitions
- *
- */
-#define TX_STATUS_FIFO_SIZE_BYTES       512U    /*< fixed allocation in bytes */
-#define TX_DATA_FIFO_SIZE_KBYTES_POS    8U
-#define TX_DATA_FIFO_SIZE_KBYTES_MASK   0x0FU
-#define KBYTES_TO_BYTES_MULTIPLIER      1024U
-
-/**
- * \brief FIFO Info definitions
- *
- */
-#define FIFO_USED_SPACE_MASK        0xFFFFU
-#define DATA_FIFO_USED_SPACE_POS    0U
-#define STATUS_FIFO_USED_SPACE_POS  16U
-
-#define HW_CFG_REG_RX_FIFO_POS      10U
-#define HW_CFG_REG_RX_FIFO_SIZE_ALL 8U
-#define HW_CFG_REG_RX_FIFO_SIZE_MIN 2U          /*< Min Rx fifo size in KB */
-#define HW_CFG_REG_RX_FIFO_SIZE_MAX 6U          /*< Max Rx fifo size in KB */
-#define HW_CFG_REG_RX_FIFO_SIZE     5U          /*< Rx fifo size in KB */
-
-/**
- * \brief Chip ID definitions
- *
- */
-#define CHIP_ID 0x20C5U
+#include "enc28j60_emac.h"
 
 /**
  * @brief
@@ -69,29 +29,119 @@
  * @param
  * @retval
  */
-ENC28J60::ENC28J60(PinName mosi, PinName miso, PinName sclk, PinName cs) :
-    _spi(new SPI(mosi, miso, sclk)),
-    _cs(cs),
-    _bank(0),
-    _ready(true),
-    _next(ERXST_INI)
-{
-    init();
-}
+ENC28J60_EMAC::ENC28J60_EMAC() :
+    _enc28j60(new ENC28J60(ENC28J60_MOSI, ENC28J60_MISO, ENC28J60_SCK, ENC28J60_CS)),
+    _prev_link_status_up(PHY_STATE_LINK_DOWN),
+    _link_status_task_handle(0),
+    _receive_task_handle(0),
+    _memory_manager(NULL)
+{ }
 
 /**
  * @brief
- * @note
+ * @note    Allocates buffer chain from a pool and filles it with incoming packet.
+ * @param
+ * @retval  Data from incoming packet
+ */
+emac_mem_buf_t* ENC28J60_EMAC::low_level_input()
+{
+    emac_mem_buf_t*     chain;
+    emac_mem_buf_t*     buf;
+    packet_t            packet;
+
+    if (_enc28j60->getPacketInfo(&packet) != ENC28J60_ERROR_OK) {
+        return NULL;
+    }
+
+    if (packet.payload.len == 0) {
+        return NULL;
+    }
+
+    // Allocate a buffer chain from the memory pool.
+    chain = _memory_manager->alloc_pool(packet.payload.len, ENC28J60_BUFF_ALIGNMENT);
+    buf = chain;
+
+    // Iterate through the buffer chain and fill it with packet payload.
+    while (buf != NULL) {
+        packet.payload.buf = (uint8_t*)_memory_manager->get_ptr(buf);
+        packet.payload.len = (uint16_t) (_memory_manager->get_len(buf));
+
+        // Fill the ethernet stack buffer with packet payload received by ENC28J60.
+        _enc28j60->readPacket(&packet);
+
+        buf = _memory_manager->get_next(buf);
+    }
+
+    // Return the buffer chain filled with packet payload.
+    return chain;
+}
+
+/**
+ * @brief   Receive task.
+ * @note    Passes packet payload received by ENC28J60 to the ethernet stack
  * @param
  * @retval
  */
-ENC28J60::ENC28J60(mbed::SPI* spi, PinName cs) :
-    _cs(cs),
-    _bank(0),
-    _ready(true),
-    _next(ERXST_INI)
+void ENC28J60_EMAC::receive_task()
+{
+    emac_mem_buf_t*     payload;
+
+    _ethLockMutex.lock();
+    payload = low_level_input();
+    if (payload != NULL) {
+        if (_emac_link_input_cb) {
+            _emac_link_input_cb(payload);   // pass packet payload to the ethernet stack
+            _memory_manager->free(payload);
+        }
+    }
+
+    _enc28j60->freeRxBuffer();  // make room in ENC28J60 receive buffer for new packets
+    _ethLockMutex.unlock();
+}
+
+
+/**
+ * @brief
+ * @note    Passes a packet payload from the ethernet stack to ENC28J60 for transmittion
+ * @param
+ * @retval
+ */
+bool ENC28J60_EMAC::link_out(emac_mem_buf_t* buf)
 {
-    init();
+    emac_mem_buf_t*     chain = buf;
+    packet_t            packet;
+    enc28j60_error_t    error;
+
+    if (buf == NULL) {
+        return false;
+    }
+
+    _ethLockMutex.lock();
+    // Iterate through the buffer chain and fill the packet with payload.
+    while (buf != NULL) {
+        packet.payload.buf = (uint8_t *) (_memory_manager->get_ptr(buf));
+        packet.payload.len = _memory_manager->get_len(buf);
+        error = _enc28j60->loadPacketInTxBuffer(&packet);
+        if (error != ENC28J60_ERROR_OK) {
+            _memory_manager->free(chain);
+            _ethLockMutex.unlock();
+            return false;
+        }
+
+        error = _enc28j60->transmitPacket(&packet);
+        if (error != ENC28J60_ERROR_OK) {
+            _memory_manager->free(chain);
+            _ethLockMutex.unlock();
+            return false;
+        }
+
+        buf = _memory_manager->get_next(buf);
+    }
+
+    _memory_manager->free(chain);
+    _ethLockMutex.unlock();
+
+    return true;
 }
 
 /**
@@ -100,89 +150,28 @@
  * @param
  * @retval
  */
-void ENC28J60::init()
+void ENC28J60_EMAC::link_status_task()
 {
-    // Initialize SPI interface
-
-    _cs = 1;
-    _spi->format(8, 0);         // 8bit, mode 0
-    _spi->frequency(20000000);  // 20MHz
-
-    // Wait SPI to become stable
-    ThisThread::sleep_for(RESET_TIME_OUT_MS);
-
-    // Perform system reset
-    writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
+    uint16_t    phy_basic_status_reg_value = 0;
+    bool        current_link_status_up = false;
 
-    // Check CLKRDY bit to see if reset is complete
-    // while(!(readReg(ESTAT) & ESTAT_CLKRDY));
-    // The CLKRDY does not work. See Rev. B4 Silicon Errata point.
-    // Workaround: Just wait.
-    ThisThread::sleep_for(RESET_TIME_OUT_MS);
-
-    // Set pointers to receive buffer boundaries
-    writeRegPair(ERXSTL, ERXST_INI);
-    writeRegPair(ERXNDL, ERXND_INI);
+    /* Get current status */
 
-    // Set receive pointer. Receive hardware will write data up to,
-    // but not including the memory pointed to by ERXRDPT
-    writeReg(ERXRDPTL, ERXST_INI);
-
-    // All memory which is not used by the receive buffer is considered the transmission buffer.
-    // No explicit action is required to initialize the transmission buffer.
-    // TX buffer start.
-    writeRegPair(ETXSTL, ETXST_INI);
-
-    // TX buffer end at end of ethernet buffer memory.
-    writeRegPair(ETXNDL, ETXND_INI);
+    _ethLockMutex.lock();
+    _enc28j60->phyRead(PHSTAT2, &phy_basic_status_reg_value);
 
-    // However, he host controller should leave at least seven bytes between each
-    // packet and the beginning of the receive buffer.
-    // do bank 1 stuff, packet filter:
-    // For broadcast packets we allow only ARP packtets
-    // All other packets should be unicast only for our mac (MAADR)
-    //
-    // The pattern to match is therefore
-    // Type     ETH.DST
-    // ARP      BROADCAST
-    // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9
-    // in binary these poitions are:11 0000 0011 1111
-    // This is hex 303F->EPMM0=0x3f,EPMM1=0x30
-    //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN!
-    writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN);
-    writeRegPair(EPMM0, 0x303f);
-    writeRegPair(EPMCSL, 0xf7f9);
-
-    // Enable MAC receive and bring MAC out of reset (writes 0x00 to MACON2)
-    writeRegPair(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
-
-    // Enable automatic padding to 60bytes and CRC operations
-    writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
+    current_link_status_up = (bool) ((phy_basic_status_reg_value & PHSTAT2_LSTAT) != 0);
 
-    // Set inter-frame gap (non-back-to-back)
-    writeRegPair(MAIPGL, 0x0C12);
-
-    // Set inter-frame gap (back-to-back)
-    writeReg(MABBIPG, 0x12);
-
-    // Set the maximum packet size which the controller will accept
-    // Do not send packets longer than MAX_FRAMELEN:
-    writeRegPair(MAMXFLL, MAX_FRAMELEN);
+    /* Compare with previous state */
+    if (current_link_status_up != _prev_link_status_up) {
+        if (_emac_link_state_cb) {
+            _emac_link_state_cb(current_link_status_up);
+        }
 
-    // No loopback of transmitted frames
-    phyWrite(PHCON2, PHCON2_HDLDIS);
-
-    // Switch to bank 0
-    _setBank(ECON1);
+        _prev_link_status_up = current_link_status_up;
+    }
 
-    // Enable interrutps
-    writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE);
-
-    // Enable packet reception
-    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
-
-    // Configure leds
-    phyWrite(PHLCON, 0x476);
+    _ethLockMutex.unlock();
 }
 
 /**
@@ -191,67 +180,38 @@
  * @param
  * @retval
  */
-enc28j60_error_t ENC28J60::getPacketInfo(packet_t* packet)
+bool ENC28J60_EMAC::power_up()
 {
-    enc28j60_error_t    ret;
-    uint8_t             nextPacketAddrL;
-    uint8_t             nextPacketAddrH;
-    uint8_t             status[RX_STAT_LEN];
-
-    if (!_ready)
-        return ENC28J60_ERROR_LASTPACKET;
-
-    // Check if a new packet has been received and buffered.
-    // The Receive Packet Pending Interrupt Flag (EIR.PKTIF) does not reliably
-    // report the status of pending packets. See Rev. B4 Silicon Errata point 6.
-    // Workaround: Check EPKTCNT
-    if (readReg(EPKTCNT) == 0)
-        return ENC28J60_ERROR_NOPACKET;
-
-    _ready = false;
-
-    // Packet pointer in receive buffer is wrapped by hardware.
-    packet->addr = _next;
-
-    // Program the receive buffer read pointer to point to this packet.
-    writeRegPair(ERDPTL, packet->addr);
-
-    // Read the next packet address
-    nextPacketAddrL = readOp(ENC28J60_READ_BUF_MEM, 0);
-    nextPacketAddrH = readOp(ENC28J60_READ_BUF_MEM, 0);
-    _next = (nextPacketAddrH << 8) | nextPacketAddrL;
-
-    // Read the packet status vector bytes (see datasheet page 43)
-    for (uint8_t i = 0; i < RX_STAT_LEN; i++) {
-        status[i] = readOp(ENC28J60_READ_BUF_MEM, 0);
+    volatile uint32_t   timeout = 500;
+    _enc28j60->writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
+    while (_enc28j60->readReg(ESTAT_CLKRDY) == 0) {
+        ThisThread::sleep_for(1ms);
+        timeout--;
+        if (timeout == 0) {
+            return false;
+        }
     }
 
-    // Get payload length (see datasheet page 43)
-    packet->payload.len = (status[1] << 8) | status[0];
-
-    // Remove CRC bytes
-    packet->payload.len -= RX_CRC_LEN;
+    /* Trigger thread to deal with any RX packets that arrived
+     * before receiver_thread was started */
+    _receive_task_handle = mbed::mbed_event_queue()->call_every
+        (
+            RECEIVE_TASK_PERIOD_MS,
+            mbed::callback(this, &ENC28J60_EMAC::receive_task)
+        );
 
-    // Check CRC and symbol errors (see datasheet page 44, table 7-3):
-    // The ERXFCON.CRCEN is set by default. Normally we should not
-    // need to check this.
-    // Bit 7 in byte 3 of receive status vectors indicates that the packet
-    // had a valid CRC and no symbol errors.
-    if ((status[2] & (1 << 7)) != 0) {
-        ret = ENC28J60_ERROR_OK;
-    }
-    else {
-        // Drop faulty packet:
-        // Move the receive read pointer to the begin of the next packet.
-        // This frees the memory we just read out.
-        freeRxBuffer();
-        ret = ENC28J60_ERROR_RECEIVE;
-    }
+    _prev_link_status_up = PHY_STATE_LINK_DOWN;
+    mbed::mbed_event_queue()->call(mbed::callback(this, &ENC28J60_EMAC::link_status_task));
 
-    // Decrement the packet counter to indicate we are done with this packet.
-    writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
+    /* Allow the Link Status task to detect the initial link state */
+    ThisThread::sleep_for(10ms);
+    _link_status_task_handle = mbed::mbed_event_queue()->call_every
+        (
+            LINK_STATUS_TASK_PERIOD_MS,
+            mbed::callback(this, &ENC28J60_EMAC::link_status_task)
+        );
 
-    return ret;
+    return true;
 }
 
 /**
@@ -260,46 +220,20 @@
  * @param
  * @retval
  */
-void ENC28J60::readPacket(packet_t* packet)
+uint32_t ENC28J60_EMAC::get_mtu_size() const
 {
-    // Read operation in receive buffer wraps the read pointer automatically.
-    // To utilize this feature we program the receive buffer read pointer (ERDPTL)
-    // to point to the begin of this packet (see datasheet page 43).
-    // Then we read the next packet pointer bytes + packet status vector bytes
-    // rather than calculating the payload position and advancing ERDPTL by software.
-    writeRegPair(ERDPTL, packet->addr);
-
-    // Advance the read pointer to the payload by reading bytes out of interest.
-    for (uint8_t i = 0; i < (RX_NEXT_LEN + RX_STAT_LEN); i++)
-        readOp(ENC28J60_READ_BUF_MEM, 0);
-
-    // The receive buffer read pointer is now pointing to the correct place.
-    // We can read packet payload bytes.
-    readBuf(packet->payload.buf, packet->payload.len);
+    return ENC28J60_ETH_MTU_SIZE;
 }
 
 /**
- * @brief   Frees the memory occupied by last packet.
- * @note    Programs the Receive Pointer (ERXRDPT)to point to the next
- *          packet address. Receive hardware will write data up to,
- *          but not including the memory pointed to by ERXRDPT.
+ * @brief
+ * @note
  * @param
  * @retval
  */
-void ENC28J60::freeRxBuffer(void)
+uint32_t ENC28J60_EMAC::get_align_preference() const
 {
-    // Compensate for the errata rev B7, point 11:
-    // The receive hardware may corrupt the circular
-    // receive buffer (including the Next Packet Pointer
-    // and receive status vector fields) when an even value
-    // is programmed into the ERXRDPTH:ERXRDPTL registers.
-    // Workaround: Never write an even address!
-    if ((_next - 1 < ERXST_INI) || (_next - 1 > ERXND_INI))
-        writeRegPair(ERXRDPTL, ERXND_INI);
-    else
-        writeRegPair(ERXRDPTL, _next - 1);
-
-    _ready = true;  // ready for next packet
+    return ENC28J60_BUFF_ALIGNMENT;
 }
 
 /**
@@ -308,25 +242,9 @@
  * @param
  * @retval
  */
-enc28j60_error_t ENC28J60::loadPacketInTxBuffer(packet_t* packet)
+void ENC28J60_EMAC::get_ifname(char* name, uint8_t size) const
 {
-    uint8_t             controlByte = 0;
-    enc28j60_error_t    error = ENC28J60_ERROR_OK;
-    uint16_t            packetLen = TX_CTRL_LEN + packet->payload.len + TX_STAT_LEN;
-
-    if (packetLen > ETXND_INI - ETXST_INI) {
-        error = ENC28J60_ERROR_FIFOFULL;
-        return error;
-    }
-
-    setWritePrt(ETXST_INI, 0);
-
-    //_tx_packet.payload.len = data_len;
-    writeBuf(&controlByte, sizeof(controlByte));
-    error = setWritePrt(ETXST_INI, sizeof(controlByte));
-    writeBuf(packet->payload.buf, packet->payload.len);
-
-    return error;
+    memcpy(name, ENC28J60_ETH_IF_NAME, (size < sizeof(ENC28J60_ETH_IF_NAME)) ? size : sizeof(ENC28J60_ETH_IF_NAME));
 }
 
 /**
@@ -335,12 +253,9 @@
  * @param
  * @retval
  */
-enc28j60_error_t ENC28J60::softReset(void)
+uint8_t ENC28J60_EMAC::get_hwaddr_size() const
 {
-    writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
-
-    ThisThread::sleep_for(1ms);
-    return ENC28J60_ERROR_OK;
+    return ENC28J60_HWADDR_SIZE;
 }
 
 /**
@@ -349,16 +264,14 @@
  * @param
  * @retval
  */
-void ENC28J60::setRxBufSize(uint32_t size_kb)
+bool ENC28J60_EMAC::get_hwaddr(uint8_t* addr) const
 {
-    if (size_kb >= HW_CFG_REG_RX_FIFO_SIZE_MIN && size_kb <= HW_CFG_REG_RX_FIFO_SIZE_MAX) {
-        writeRegPair(ERXSTL, ERXST_INI);
-
-        // set receive pointer address
-        writeRegPair(ERXRDPTL, ERXST_INI);
-
-        // RX end
-        writeRegPair(ERXNDL, 0x1FFF - (size_kb << HW_CFG_REG_RX_FIFO_POS));
+    enc28j60_error_t    error = _enc28j60->readMacAddr((char*)addr);
+    if (error == ENC28J60_ERROR_OK) {
+        return true;
+    }
+    else {
+        return false;
     }
 }
 
@@ -368,417 +281,19 @@
  * @param
  * @retval
  */
-enc28j60_error_t ENC28J60::resetPhy(void)
-{
-    enc28j60_error_t    error = ENC28J60_ERROR_OK;
-    uint16_t            phcon1 = 0;
-    error = phyRead(PHCON1, &phcon1);
-    if (error)
-        return ENC28J60_ERROR_TIMEOUT;
-    error = phyWrite(PHCON1, (phcon1 | PHCON1_PRST));
-    if (error)
-        return ENC28J60_ERROR_TIMEOUT;
-    ThisThread::sleep_for(PHY_RESET_TIME_OUT_MS);
-    error = phyRead(PHCON1, &phcon1);
-    if (error || (phcon1 & PHCON1_PRST) != 0) {
-        return ENC28J60_ERROR_TIMEOUT;
-    }
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::enableMacRecv(void)
-{
-    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::disableMacRecv(void)
-{
-    writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::readMacAddr(char* mac)
-{
-    if (!mac) {
-        return ENC28J60_ERROR_PARAM;
-    }
-
-    _setBank(MAADR0);
-
-    mac[0] = readReg(MAADR5);
-    mac[1] = readReg(MAADR4);
-    mac[2] = readReg(MAADR3);
-    mac[3] = readReg(MAADR2);
-    mac[4] = readReg(MAADR1);
-    mac[5] = readReg(MAADR0);
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::writeMacAddr(char* mac)
-{
-    if (!mac) {
-        return ENC28J60_ERROR_PARAM;
-    }
-
-    _setBank(MAADR0);
-
-    writeReg(MAADR5, mac[0]);
-    writeReg(MAADR4, mac[1]);
-    writeReg(MAADR3, mac[2]);
-    writeReg(MAADR2, mac[3]);
-    writeReg(MAADR1, mac[4]);
-    writeReg(MAADR0, mac[5]);
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::setWritePrt(uint16_t position, uint16_t offset)
+void ENC28J60_EMAC::set_hwaddr(const uint8_t* addr)
 {
-    uint32_t    start = position + offset > ETXND_INI ? position + offset - ETXND_INI + ETXST_INI : position + offset;
-
-    writeRegPair(EWRPTL, start);
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::transmitPacket(packet_t* packet)
-{
-    // Set Transmit Buffer Start pointer
-    writeRegPair(ETXSTL, ETXST_INI);
-
-    // Set Transmit Buffer End pointer
-    writeRegPair(ETXNDL, ETXST_INI + TX_CTRL_LEN + packet->payload.len);
-
-    // Enable transmittion
-    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
-
-    // Wait until transmission is completed
-    while ((readReg(ECON1) & ECON1_TXRTS) != 0) { }
-
-    // Chek whether the transmission was successfull
-    if ((readReg(ESTAT) & ESTAT_TXABRT) == 0)
-        return ENC28J60_ERROR_OK;
-    else
-        return ENC28J60_ERROR_NEXTPACKET;
-}
-
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-uint32_t ENC28J60::getRxBufFreeSpace(void)
-{
-    uint16_t    readPointer = getRecvPointer();
-    uint16_t    writePointer = getWritePointer();
-    uint32_t    freeSpace = 0;
-    if (writePointer > readPointer) {
-        freeSpace = (uint32_t) (ERXND_INI - ERXST_INI) - (writePointer - readPointer);
-    }
-    else
-    if (writePointer == readPointer) {
-        freeSpace = (ERXND_INI - ERXST_INI);
-    }
-    else {
-        freeSpace = readPointer - writePointer - 1;
-    }
-
-    return freeSpace;
-}
-
-/**
- * @brief   Sets Ethernet buffer read pointer
- * @note    Points to a location in receive buffer to read from.
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::setRxBufReadPtr(uint16_t position)
-{
-    //
-    // Wrap the start pointer of received data when greater than end of receive buffer
-    if (position > ERXND_INI)
-        position = ERXST_INI + (position - ERXND_INI - 1);
-
-    writeRegPair(ERDPTL, position);
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief   Sets receive pointer.
- * @note    Receive hardware will write received data up to, but not including
- *          the memory pointed to by the receive pointer.
- * @param
- * @retval
- */
-uint16_t ENC28J60::getRecvPointer(void)
-{
-    return readRegPair(ERXRDPTL);
-}
-
-/**
- * @brief   Gets receive buffer's write pointer.
- * @note    Location within the receive buffer where the hardware will write bytes that it receives.
- *          The pointer is read-only and is automatically updated by the hardware whenever
- *          a new packet is successfully received.
- * @param
- * @retval
- */
-uint16_t ENC28J60::getWritePointer(void)
-{
-    uint16_t    count_pre = readReg(EPKTCNT);
-    uint16_t    writePointer = readRegPair(ERXWRPTL);
-    uint16_t    count_post = readReg(EPKTCNT);
-    while (count_pre != count_post) {
-        count_pre = count_post;
-        writePointer = readRegPair(ERXWRPTL);
-        count_post = readReg(EPKTCNT);
+    if (!addr) {
+        return;
     }
 
-    ENC28J60_DEBUG_PRINTF("[ENC28J60] rx_getWritePointer: %d", writePointer);
-    return writePointer;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::readBuf(uint8_t* data, uint16_t len)
-{
-    _read(ENC28J60_READ_BUF_MEM, data, len, false);
-    data[len] = '\0';
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::writeBuf(uint8_t* data, uint16_t len)
-{
-    _write(ENC28J60_WRITE_BUF_MEM, data, len, false);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-uint8_t ENC28J60::readReg(uint8_t address)
-{
-    _setBank(address);
-
-    // do the read
-    return readOp(ENC28J60_READ_CTRL_REG, address);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-uint16_t ENC28J60::readRegPair(uint8_t address)
-{
-    uint16_t    temp;
-
-    _setBank(address);
-
-    // do the read
-    temp = (uint16_t) (readOp(ENC28J60_READ_CTRL_REG, address + 1)) << 8;
-    temp |= readOp(ENC28J60_READ_CTRL_REG, address);
-
-    return temp;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::writeReg(uint8_t address, uint8_t data)
-{
-    _setBank(address);
-
-    // do the write
-    writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::writeRegPair(uint8_t address, uint16_t data)
-{
-    _setBank(address);
-
-    // do the write
-    writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF));
-    writeOp(ENC28J60_WRITE_CTRL_REG, address + 1, (data) >> 8);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::phyRead(uint8_t address, uint16_t* data)
-{
-    uint8_t timeout = 0;
-    writeReg(MIREGADR, address);
-    writeReg(MICMD, MICMD_MIIRD);
+    memcpy(_hwaddr, addr, sizeof _hwaddr);
+    _ethLockMutex.lock();
 
-    // wait until the PHY read completes
-    while (readReg(MISTAT) & MISTAT_BUSY) {
-        wait_us(15);
-        timeout++;
-        if (timeout > 10)
-            return ENC28J60_ERROR_TIMEOUT;
-    }   //and MIRDH
-
-    writeReg(MICMD, 0);
-    *data = (readReg(MIRDL) | readReg(MIRDH) << 8);
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-enc28j60_error_t ENC28J60::phyWrite(uint8_t address, uint16_t data)
-{
-    uint8_t timeout = 0;
-    // set the PHY register address
-
-    writeReg(MIREGADR, address);
-
-    // write the PHY data
-    writeRegPair(MIWRL, data);
-
-    // wait until the PHY write completes
-    while (readReg(MISTAT) & MISTAT_BUSY) {
-        wait_us(15);
-        timeout++;
-        if (timeout > 10)
-            return ENC28J60_ERROR_TIMEOUT;
-    }
-
-    return ENC28J60_ERROR_OK;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-bool ENC28J60::linkStatus(void)
-{
-    uint16_t    data;
-    phyRead(PHSTAT2, &data);
-    return(data & 0x0400) > 0;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-uint8_t ENC28J60::readOp(uint8_t op, uint8_t address)
-{
-    uint8_t result;
-    uint8_t data[2];
-
-    // issue read command
-
-    if (address & 0x80) {
-        _read((op | (address & ADDR_MASK)), &data[0], 2, false);
-        result = data[1];
-        return result;
-    }
-    else {
-        _read((op | (address & ADDR_MASK)), &data[0], 1, false);
-    }
-
-    result = data[0];
-    return result;
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::writeOp(uint8_t op, uint8_t address, uint8_t data)
-{
-    // issue write command
-
-    _write(op | (address & ADDR_MASK), &data, 1, false);
-}
-
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void ENC28J60::_setBank(uint8_t address)
-{
-    if ((address & BANK_MASK) != _bank) {
-        writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0));
-        writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5);
-        _bank = (address & BANK_MASK);
+    enc28j60_error_t    error = _enc28j60->writeMacAddr((char*)addr);
+    _ethLockMutex.unlock();
+    if (error) {
+        return;
     }
 }
 
@@ -788,28 +303,42 @@
  * @param
  * @retval
  */
-void ENC28J60::_read(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking)
+void ENC28J60_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
 {
-#ifndef ENC28J60_READWRITE
-    _SPIMutex.lock();
-    _cs = 0;
+    _emac_link_input_cb = input_cb;
+}
 
-    // issue read command
-    _spi->write((int)cmd);
-    while (len) {
-        len--;
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ENC28J60_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
+{
+    _emac_link_state_cb = state_cb;
+}
 
-        // read data
-        *buf = _spi->write(0x00);
-        buf++;
-    }
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ENC28J60_EMAC::add_multicast_group(const uint8_t* addr)
+{
+    // No action for now
+}
 
-    _cs = 1;
-    _SPIMutex.unlock();
-#else
-    uint8_t*    dummy = NULL;
-    _readwrite(cmd, buf, dummy, len, blocking);
-#endif
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ENC28J60_EMAC::remove_multicast_group(const uint8_t* addr)
+{
+    // No action for now
 }
 
 /**
@@ -818,63 +347,58 @@
  * @param
  * @retval
  */
-void ENC28J60::_write(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking)
+void ENC28J60_EMAC::set_all_multicast(bool all)
 {
-#ifndef ENC28J60_READWRITE
-    _SPIMutex.lock();
-    _cs = 0;
-
-    // issue read command
-    _spi->write((int)cmd);
-    while (len) {
-        len--;
-
-        // read data
-        _spi->write((int) *buf);
-        buf++;
-    }
-
-    _cs = 1;
-    _SPIMutex.unlock();
-#else
-    uint8_t*    dummy = NULL;
-    _readwrite(cmd, dummy, buf, len, blocking);
-#endif
+    // No action for now
 }
 
-#ifdef ENC28J60_READWRITE
-
 /**
  * @brief
  * @note
  * @param
  * @retval
  */
-void ENC28J60::_readwrite(uint8_t cmd, uint8_t* readbuf, uint8_t* writebuf, uint16_t len, bool blocking)
+void ENC28J60_EMAC::power_down()
 {
-    _SPIMutex.lock();
-    _cs = 0;
-
-    // issue read command
-    _spi->write((int)cmd);
-    while (len) {
-        len--;
+    _enc28j60->disableMacRecv();
+    if (_enc28j60->readReg(ESTAT_RXBUSY) != 0) {
+        _enc28j60->enableMacRecv();
+        return;
+    }
 
-        if (readbuf == NULL) {
-            _spi->write((int) *writebuf);
-            writebuf++;
-        }
-        else
-        if (writebuf == NULL) {
-            *readbuf = _spi->write(0x00);
-            readbuf++;
-        }
-        else {
-            break;
-        }
+    if (_enc28j60->readReg(ECON1_TXRTS) != 0) {
+        _enc28j60->enableMacRecv();
+        return;
     }
 
-    _cs = 1;
-    _SPIMutex.unlock();
+    _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
+    _enc28j60->writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void ENC28J60_EMAC::set_memory_manager(EMACMemoryManager& mem_mngr)
+{
+    _memory_manager = &mem_mngr;
 }
-#endif
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+ENC28J60_EMAC& ENC28J60_EMAC::get_instance()
+{
+    static ENC28J60_EMAC    emac;
+    return emac;
+}
+
+EMAC& EMAC::get_default_instance()
+{
+    return ENC28J60_EMAC::get_instance();
+}
--- a/enc28j60_emac.h	Sat Mar 27 22:45:46 2021 +0000
+++ b/enc28j60_emac.h	Sat Mar 27 22:52:41 2021 +0000
@@ -15,216 +15,173 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef ENC28J60_ETH_DRV_H_
-#define ENC28J60_ETH_DRV_H_
+#ifndef ENC28J60_EMAC_H_
+#define ENC28J60_EMAC_H_
 
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
 #include "mbed.h"
+#include "EMAC.h"
+#include "rtos.h"
+
 #include "enc28j60_reg.h"
+#include "enc28j60.h"
 #include "enc28j60_emac_config.h"
 
-#define ENC28J60_READWRITE  1
-
-/**
- * \brief Error code definitions
- *
- */
-typedef struct
-{
-    uint8_t*    buf;
-    uint16_t    len;
-} payload_t;
-
-typedef struct
-{
-    uint32_t    addr;
-    payload_t   payload;
-} packet_t;
-
-/**
- * \brief Error code definitions
- *
- */
-typedef enum
-{
-    ENC28J60_ERROR_OK               = 0U,   /*!< no error */
-    ENC28J60_ERROR_TIMEOUT          = 1U,   /*!< timeout */
-    ENC28J60_ERROR_BUSY             = 2U,   /*!< no error */
-    ENC28J60_ERROR_PARAM            = 3U,   /*!< invalid parameter */
-    ENC28J60_ERROR_INTERNAL         = 4U,   /*!< internal error */
-    ENC28J60_ERROR_WRONG_ID         = 5U,   /*!< internal error */
-    ENC28J60_ERROR_NOPACKET         = 10U,
-    ENC28J60_ERROR_RECEIVE          = 11U,
-    ENC28J60_ERROR_LASTPACKET       = 12U,
-    ENC28J60_ERROR_POSITIONLENGTH   = 13U,  /*!< internal error */
-    ENC28J60_ERROR_SIZE             = 20U,  /*!< internal error */
-    ENC28J60_ERROR_FIFOFULL         = 21U,  /*!< internal error */
-    ENC28J60_ERROR_NEXTPACKET       = 22U,  /*!< internal error */
-} enc28j60_error_t;
-
-/**
- * \brief Interrupt source definitions
- *
- */
-typedef enum
-{
-    ENC28J60_INTERRUPT_ENABLE           = EIE_INTIE,
-    ENC28J60_INTERRUPT_RX_PENDING_ENABLE= EIE_PKTIE,
-    ENC28J60_INTERRUPT_DMA_ENABLE       = EIE_DMAIE,
-    ENC28J60_INTERRUPT_LINK_STATE_ENABLE= EIE_LINKIE,
-    ENC28J60_INTERRUPT_TX_ENABLE        = EIE_TXIE,
-    ENC28J60_INTERRUPT_TX_ERROR_ENABLE  = EIE_TXERIE,
-    ENC28J60_INTERRUPT_RX_ERROR_ENABLE  = EIE_RXERIE
-} enc28j60_interrupt_source;
-
-class   ENC28J60
+class ENC28J60_EMAC :
+    public EMAC
 {
 public:
-    ENC28J60(PinName mosi, PinName miso, PinName sclk, PinName cs);
+    ENC28J60_EMAC();
+
+    /** Return the ENC28J60 EMAC
+     *
+     * Returns the default on-board EMAC - this will be target-specific, and
+     * may not be available on all targets.
+     */
+    static ENC28J60_EMAC&   get_instance(void);
 
-    ENC28J60(mbed::SPI * spi, PinName cs);
+    /**
+     * Return maximum transmission unit
+     *
+     * @return     MTU in bytes
+     */
+    virtual uint32_t        get_mtu_size(void) const;
+
+    /**
+     * Gets memory buffer alignment preference
+     *
+     * Gets preferred memory buffer alignment of the Emac device. IP stack may
+     * or may not align link out memory buffer chains using the alignment.
+     *
+     * @return         Memory alignment requirement in bytes
+     */
+    virtual uint32_t        get_align_preference(void) const;
 
     /**
-     * \brief Initializes ENC28J60 Ethernet controller to a known default state:
-     *          - device ID is checked
-     *          - global interrupt is enabled, but all irq sources are disabled
-     *          - Establish link enabled
-     *          - Rx enabled
-     *          - Tx enabled
-     *        Init should be called prior to any other process and
-     *        it's the caller's responsibility to follow proper call order.
+     * Return interface name
      *
-     * \return error code /ref enc28j60_error_t
+     * @param name Pointer to where the name should be written
+     * @param size Maximum number of character to copy
      */
-    void                init(void);
-
-    /** This returns a unique 6-byte MAC address, based on the device UID
-    *  This function overrides hal/common/mbed_interface.c function
-    *  @param mac A 6-byte array to write the MAC address
-    */
-    void                mbed_mac_address(char* mac);
-    MBED_WEAK uint8_t   mbed_otp_mac_address(char* mac);
-    void                mbed_default_mac_address(char* mac);
+    virtual void            get_ifname(char* name, uint8_t size) const;
 
     /**
-     * \brief Initiates a soft reset, returns failure or success.
+     * Returns size of the underlying interface HW address size.
      *
-     * \return error code /ref enc28j60_error_t
+     * @return     HW address size in bytes
      */
-    enc28j60_error_t    softReset(void);
+    virtual uint8_t         get_hwaddr_size(void) const;
 
     /**
-     * \brief Set maximum transition unit by Rx fifo size.
-     *        Note: The MTU will be smaller by 512 bytes,
-     *        because the status uses this fixed space.
+     * Return interface-supplied HW address
+     *
+     * Copies HW address to provided memory, @param addr has to be of correct
+     * size see @a get_hwaddr_size
      *
-     * \param[in] val Size of the fifo in kbytes
+     * HW address need not be provided if this interface does not have its own
+     * HW address configuration; stack will choose address from central system
+     * configuration if the function returns false and does not write to addr.
+     *
+     * @param addr HW address for underlying interface
+     * @return     true if HW address is available
      */
-    void                setRxBufSize(uint32_t val);
+    virtual bool            get_hwaddr(uint8_t* addr) const;
 
     /**
-     * \brief Reset PHY
+     * Set HW address for interface
+     *
+     * Provided address has to be of correct size, see @a get_hwaddr_size
      *
-     * \return error code /ref enc28j60_error_t
+     * Called to set the MAC address to actually use - if @a get_hwaddr is
+     * provided the stack would normally use that, but it could be overridden,
+     * eg for test purposes.
+     *
+     * @param addr Address to be set
      */
-    enc28j60_error_t    resetPhy(void);
-
-    /**
-     * \brief Enable receive
-     */
-    void                enableMacRecv(void);
-
-    /**
-     * \brief Disable receive
-     */
-    void                disableMacRecv(void);
+    virtual void            set_hwaddr(const uint8_t* addr);
 
     /**
-     * \brief Read MAC address from EEPROM.
+     * Sends the packet over the link
+     *
+     * That can not be called from an interrupt context.
      *
-     * \param[in,out] mac array will include the read MAC address in
-     *                6 bytes hexadecimal format.
-     *                It should be allocated by the caller to 6 bytes.
-     *
-     * \return error code /ref enc28j60_error_t
+     * @param buf  Packet to be send
+     * @return     True if the packet was send successfully, False otherwise
      */
-    enc28j60_error_t    readMacAddr(char* mac);
+    virtual bool            link_out(emac_mem_buf_t* buf);
 
     /**
-     * \brief Write MAC address to EEPROM.
+     * Initializes the HW
      *
-     * \param[in,out] mac array will include the write MAC address in
-     *                6 bytes hexadecimal format.
-     *                It should be allocated by the caller to 6 bytes.
-     *
-     * \return error code /ref enc28j60_error_t
+     * @return True on success, False in case of an error.
      */
-    enc28j60_error_t    writeMacAddr(char* mac);
+    virtual bool            power_up(void);
 
     /**
-     * \brief Check device ID.
+     * Deinitializes the HW
      *
-     * \return error code /ref enc28j60_error_t
      */
-    bool                check_id(void);
+    virtual void            power_down(void);
+
+    /**
+     * Sets a callback that needs to be called for packets received for that
+     * interface
+     *
+     * @param input_cb Function to be register as a callback
+     */
+    virtual void            set_link_input_cb(emac_link_input_cb_t input_cb);
 
     /**
-     * \brief Get the data size of the Rx buffer, aka Maximum Transition Unit
+     * Sets a callback that needs to be called on link status changes for given
+     * interface
      *
-     * \return Fifo data size in bytes
+     * @param state_cb Function to be register as a callback
      */
-    enc28j60_error_t    setWritePrt(uint16_t position, uint16_t offset);
-    enc28j60_error_t    transmitPacket(packet_t* packet);
-    enc28j60_error_t    loadPacketInTxBuffer(packet_t* packet);
+    virtual void            set_link_state_cb(emac_link_state_change_cb_t state_cb);
 
-    /**
-     * \brief Get the free space of Rx fifo in bytes.
-     *
-     * \param[in] dev Ethernet device structure \ref enc28j60_eth_dev_t
+    /** Add device to a multicast group
      *
-     * \return Space available to store received data in bytes
+     * @param address  A multicast group hardware address
      */
-    uint32_t            getRxBufFreeSpace(void);
+    virtual void            add_multicast_group(const uint8_t* address);
 
-    /**
-     * \brief Get the size of next unread packet in Rx buffer, using the peak
-     *        register, which is not destructive so can be read asynchronously.
-     *        Warning: In case of heavy receiving load, it's possible this register
-     *        is not perfectly in sync.
+    /** Remove device from a multicast group
      *
-     * \param[in] dev Ethernet device structure \ref enc28j60_eth_dev_t
+     * @param address  A multicast group hardware address
+     */
+    virtual void            remove_multicast_group(const uint8_t* address);
+
+    /** Request reception of all multicast packets
      *
-     * \return Size in bytes of the next packet can be read from Rx fifo, according
-     *         to the peek register.
+     * @param all True to receive all multicasts
+     *            False to receive only multicasts addressed to specified groups
      */
-    enc28j60_error_t    setRxBufReadPtr(uint16_t position);
-    enc28j60_error_t    getPacketInfo(packet_t* packet);
-    void                readPacket(packet_t* packet);
-    void                freeRxBuffer(void);
-    uint16_t            getRecvPointer(void);
-    uint16_t            getWritePointer(void);
-    void                readBuf(uint8_t* data, uint16_t len);
-    void                writeBuf(uint8_t* data, uint16_t len);
-    uint8_t             readReg(uint8_t address);
-    uint16_t            readRegPair(uint8_t address);
-    void                writeReg(uint8_t address, uint8_t data);
-    void                writeRegPair(uint8_t address, uint16_t data);
-    enc28j60_error_t    phyRead(uint8_t address, uint16_t* data);
-    enc28j60_error_t    phyWrite(uint8_t address, uint16_t data);
-    bool                linkStatus(void);
-    uint8_t             readOp(uint8_t op, uint8_t address);
-    void                writeOp(uint8_t op, uint8_t address, uint8_t data);
+    virtual void            set_all_multicast(bool all);
+
+    /** Sets memory manager that is used to handle memory buffers
+     *
+     * @param mem_mngr Pointer to memory manager
+     */
+    virtual void            set_memory_manager(EMACMemoryManager& mem_mngr);
 private:
-    void        _setBank(uint8_t address);
-    void        _read(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking);
-    void        _write(uint8_t cmd, uint8_t* buf, uint16_t len, bool blocking);
-#ifdef ENC28J60_READWRITE
-    void        _readwrite(uint8_t cmd, uint8_t* readbuf, uint8_t* writebuf, uint16_t len, bool blocking);
-#endif
-    SPI*        _spi;
-    Mutex       _SPIMutex;
-    DigitalOut  _cs;
-    uint8_t     _bank;
-    bool        _ready;
-    uint32_t    _next;
+    void                        link_status_task();
+    void                        receive_task();
+    bool                        low_level_init_successful();
+    emac_mem_buf_t*             low_level_input();
+
+    ENC28J60*                   _enc28j60;
+    bool                        _prev_link_status_up;
+    int                         _link_status_task_handle;
+    int                         _receive_task_handle;
+    EMACMemoryManager*          _memory_manager;
+    Mutex                       _ethLockMutex;
+    uint8_t                     _hwaddr[ENC28J60_HWADDR_SIZE];
+
+    emac_link_input_cb_t        _emac_link_input_cb;
+    emac_link_state_change_cb_t _emac_link_state_cb;
 };
-#endif /* ENC28J60_ETH_DRV_H_ */
+#endif /* ENC28J60_EMAC_H_ */