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
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:
- 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; } ...
Revision 2:19d290369c66, committed 2021-03-27
- 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_ */