📄 lpc17_emac.c
字号:
/**********************************************************************
* $Id$ lpc17_emac.c 2011-11-20
*//**
* @file lpc17_emac.c
* @brief LPC17 ethernet driver for LWIP
* @version 1.0
* @date 20. Nov. 2011
* @author NXP MCU SW Application Team
*
* Copyright(C) 2011, NXP Semiconductor
* All rights reserved.
*
***********************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
**********************************************************************/
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "netif/etharp.h"
#include "netif/ppp_oe.h"
#include "lpc177x_8x_emac.h"
#include "lpc177x_8x_clkpwr.h"
#include "lpc17_emac.h"
#include "lpc_emac_config.h"
/** @defgroup lwip_emac_DRIVER lpc17 EMAC driver for LWIP
* @ingroup lwip_emac
*
* This driver is currently for the LPC177x_8x devices only, although
* the LPC32x0 and LPC2000 series devices share the same ethernet
* controller.
*
* @{
*/
/** \brief Write a value via the MII link (non-blocking)
This function will write a value on the MII link interface to a PHY
or a connected device. The function will return immediately without
a status. Status needs to be polled later to determine if the write
was successful.
\param [in] PhyReg PHY register to write to
\param [in] Value Value to write
*/
void lpc_mii_write_noblock(u32_t PhyReg, u32_t Value)
{
/* Write value at PHY address and register */
LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg;
LPC_EMAC->MWTD = Value;
}
/** \brief Write a value via the MII link (blocking)
This function will write a value on the MII link interface to a PHY
or a connected device. The function will block until complete.
\param [in] PhyReg PHY register to write to
\param [in] Value Value to write
\returns 0 if the write was successful, otherwise !0
*/
err_t lpc_mii_write(u32_t PhyReg, u32_t Value)
{
u32_t mst = 250;
err_t sts = ERR_OK;
/* Write value at PHY address and register */
lpc_mii_write_noblock(PhyReg, Value);
/* Wait for unbusy status */
while (mst > 0) {
sts = LPC_EMAC->MIND;
if ((sts & EMAC_MIND_BUSY) == 0)
mst = 0;
else {
mst--;
msDelay(1);
}
}
if (sts != 0)
sts = ERR_TIMEOUT;
return sts;
}
/** \brief Reads current MII link busy status
This function will return the current MII link busy status and is meant to
be used with non-blocking functions for monitor PHY status such as
connection state.
\returns !0 if the MII link is busy, otherwise 0
*/
u32_t lpc_mii_is_busy(void)
{
return (u32_t) (LPC_EMAC->MIND & EMAC_MIND_BUSY);
}
/** \brief Starts a read operation via the MII link (non-blocking)
This function returns the current value in the MII data register. It is
meant to be used with the non-blocking oeprations. This value should
only be read after a non-block read command has been issued and the
MII status has been determined to be good.
\returns The current value in the MII value register
*/
u32_t lpc_mii_read_data(void)
{
u32_t data = LPC_EMAC->MRDD;
LPC_EMAC->MCMD = 0;
return data;
}
/** \brief Starts a read operation via the MII link (non-blocking)
This function will start a read operation on the MII link interface
from a PHY or a connected device. The function will not block and
the status mist be polled until complete. Once complete, the data
can be read.
\param [in] PhyReg PHY register to read from
*/
void lpc_mii_read_noblock(u32_t PhyReg)
{
/* Read value at PHY address and register */
LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg;
LPC_EMAC->MCMD = EMAC_MCMD_READ;
}
/** \brief Read a value via the MII link (blocking)
This function will read a value on the MII link interface from a PHY
or a connected device. The function will block until complete.
\param [in] PhyReg PHY register to read from
\param [in] data Pointer to where to save data read via MII
\returns 0 if the read was successful, otherwise !0
*/
err_t lpc_mii_read(u32_t PhyReg, u32_t *data)
{
u32_t mst = 250;//0x00050000;
err_t sts = ERR_OK;
/* Read value at PHY address and register */
lpc_mii_read_noblock(PhyReg);
/* Wait for unbusy status */
while (mst > 0) {
sts = LPC_EMAC->MIND & ~EMAC_MIND_MII_LINK_FAIL;
if ((sts & EMAC_MIND_BUSY) == 0) {
mst = 0;
*data = LPC_EMAC->MRDD;
} else {
mst--;
msDelay(1);
}
}
LPC_EMAC->MCMD = 0;
if (sts != 0)
sts = ERR_TIMEOUT;
return sts;
}
#if LPC_PBUF_RX_ZEROCOPY
/** \brief Queues a pbuf into the RX descriptor list
\param lpc_enetdata_def Pointer to the drvier data structure
\param p Pointer to pbuf to queue
*/
static void lpc_rxqueue_pbuf(struct lpc_enetdata_def *lpc_enetdata, struct pbuf *p)
{
u32_t idx;
/* Get next free descriptor index */
idx = lpc_enetdata->rx_fill_desc_index;
/* Setup descriptor and clear statuses */
lpc_enetdata->prxd[idx].control = EMAC_RCTRL_INT | ((u32_t) (p->len - 1));
lpc_enetdata->prxd[idx].packet = (u32_t) p->payload;
lpc_enetdata->prxs[idx].statusinfo = 0xFFFFFFFF;
lpc_enetdata->prxs[idx].statushashcrc = 0xFFFFFFFF;
/* Save pbuf pointer for push to network layer later */
lpc_enetdata->rxb[idx] = p;
/* Wrap at end of descriptor list */
idx++;
if (idx >= LPC_NUM_BUFF_RXDESCS)
idx = 0;
/* Queue descriptor(s) */
lpc_enetdata->rx_free_descs -= 1;
lpc_enetdata->rx_fill_desc_index = idx;
LPC_EMAC->RxConsumeIndex = idx;
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_rxqueue_pbuf: pbuf packet queued: %p (free desc=%d)\n", p,
lpc_enetdata->rx_free_descs));
}
/** \brief Attempt to allocate and requeue a new pbuf for RX
\param netif Pointer to the netif structure
\returns 1 if a packet was allocated and requeued, otherwise 0
*/
s32_t lpc_rx_queue(struct netif *netif)
{
struct pbuf *p;
struct lpc_enetdata_def *lpc_enetdata = netif->state;
/* Exit of there are no descriptor available */
if (lpc_enetdata->rx_free_descs == 0)
return 0;
/* Allocate a pbuf from the pool. We need to allocate at the maximum size
as we don't know the size of the yet to be received packet. */
p = pbuf_alloc(PBUF_RAW, (u16_t) EMAC_ETH_MAX_FLEN, PBUF_RAM);
if (p == NULL) {
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_rx_queue: could not allocate RX pbuf (free desc=%d)\n",
lpc_enetdata->rx_free_descs));
return 0;
}
/* pbufs allocated from the RAM pool should be non-chained. */
LWIP_ASSERT("lpc_rx_queue: pbuf is not contiguous (chained)",
pbuf_clen(p) <= 1);
/* Queue packet */
lpc_rxqueue_pbuf(lpc_enetdata, p);
return 1;
}
/** \brief Sets up the RX descriptor ring buffers.
This function sets up the descriptor list used for receive packets.
\param [in] lpc_enetdata_def Pointer to driver data structure
\returns Always returns ERR_OK
*/
static err_t lpc_rx_setup(struct lpc_enetdata_def *lpc_enetdata)
{
s32_t idx;
/* Setup pointers to RX structures */
LPC_EMAC->RxDescriptor = (u32_t) &lpc_enetdata->prxd[0];
LPC_EMAC->RxStatus = (u32_t) &lpc_enetdata->prxs[0];
LPC_EMAC->RxDescriptorNumber = LPC_NUM_BUFF_RXDESCS - 1;
lpc_enetdata->rx_free_descs = LPC_NUM_BUFF_RXDESCS;
lpc_enetdata->rx_fill_desc_index = 0;
/* Build RX buffer and descriptors */
for (idx = 0; idx < LPC_NUM_BUFF_RXDESCS; idx++)
lpc_rx_queue(lpc_enetdata->netif);
return ERR_OK;
}
/** \brief Allocates a pbuf and returns the data from the incoming packet.
\param netif the lwip network interface structure for this lpc_enetif
\return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf *lpc_low_level_input(struct netif *netif)
{
struct lpc_enetdata_def *lpc_enetdata = netif->state;
struct pbuf *p = NULL, *q;
u32_t idx, length;
u8_t *src;
/* Monitor RX overrun status. This should never happen unless
(possibly) the internal bus is behing held up by something.
Unless your system is running at a very low clock speed or
there are possibilities that the internal buses may be held
up for a long time, this can probably safely be removed. */
if (LPC_EMAC->IntStatus & EMAC_INT_RX_OVERRUN) {
LINK_STATS_INC(link.err);
LINK_STATS_INC(link.drop);
/* Temporarily disable RX */
LPC_EMAC->MAC1 &= ~EMAC_MAC1_REC_EN;
/* Reset the RX side */
LPC_EMAC->MAC1 |= EMAC_MAC1_RES_RX;
LPC_EMAC->IntClear = EMAC_INT_RX_OVERRUN;
/* De-allocate all queued RX pbufs */
for (idx = 0; idx < LPC_NUM_BUFF_RXDESCS; idx++) {
if (lpc_enetdata->rxb[idx] != NULL) {
pbuf_free(lpc_enetdata->rxb[idx]);
lpc_enetdata->rxb[idx] = NULL;
}
}
/* Start RX side again */
lpc_rx_setup(lpc_enetdata);
/* Re-enable RX */
LPC_EMAC->MAC1 |= EMAC_MAC1_REC_EN;
return NULL;
}
/* Determine if a frame has been received */
length = 0;
idx = LPC_EMAC->RxConsumeIndex;
if (LPC_EMAC->RxProduceIndex != idx) {
/* Handle errors */
if ((lpc_enetdata->prxs[idx].statusinfo & (EMAC_RINFO_NO_DESCR |
EMAC_RINFO_ALIGN_ERR | EMAC_RINFO_LEN_ERR | EMAC_RINFO_SYM_ERR |
EMAC_RINFO_CRC_ERR))) {
#if LINK_STATS
if (lpc_enetdata->prxs[idx].statusinfo & (EMAC_RINFO_CRC_ERR |
EMAC_RINFO_SYM_ERR | EMAC_RINFO_ALIGN_ERR))
LINK_STATS_INC(link.chkerr);
if (lpc_enetdata->prxs[idx].statusinfo & EMAC_RINFO_LEN_ERR)
LINK_STATS_INC(link.lenerr);
#endif
/* Drop the frame */
LINK_STATS_INC(link.drop);
/* Free the pbuf associated with this descriptor. The RX
queue function will queue a new pbuf later. */
if (lpc_enetdata->rxb[idx] != NULL) {
pbuf_free(lpc_enetdata->rxb[idx]);
lpc_enetdata->rxb[idx] = NULL;
lpc_enetdata->rx_free_descs++;
}
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_low_level_input: Packet dropped with errors\n"));
}
else {
/* A packet is waiting, get length */
length = (lpc_enetdata->prxs[idx].statusinfo & 0x7FF) + 1;
/* Zero-copy */
p = lpc_enetdata->rxb[idx];
p->len = (u16_t) length;
/* Free pbuf from desriptor */
lpc_enetdata->rxb[idx] = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -