⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lpc17_emac.c

📁 NXPl788上lwip的无操作系统移植,基于Embest开发板
💻 C
📖 第 1 页 / 共 3 页
字号:
/**********************************************************************
* $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 + -