📄 lpc17_emac.c
字号:
lpc_enetdata->rx_free_descs++;
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_low_level_input: Packet received: %p, size %d (index=%d)\n",
p, length, idx));
/* Save size */
p->tot_len = (u16_t) length;
LINK_STATS_INC(link.recv);
}
}
return p;
}
#else /* Copied RX obuf driver starts here */
/** \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;
/* Build RX descriptors */
for (idx = 0; idx < LPC_NUM_BUFF_RXDESCS; idx++) {
/* Setup DMA RX descriptor */
lpc_enetdata->prxd[idx].packet = (u32_t) &lpc_enetdata->lpc_rx_buffs[idx][0];
lpc_enetdata->prxd[idx].control = EMAC_RCTRL_INT |
(EMAC_ETH_MAX_FLEN - 1);
lpc_enetdata->prxs[idx].statusinfo = 0xFFFFFFFF;
lpc_enetdata->prxs[idx].statushashcrc = 0xFFFFFFFF;
}
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;
/* 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 */
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);
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;
/* Allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, (u16_t) length, PBUF_POOL);
if (p == NULL) {
/* Buffer in hardware is not lost, but cannot get packet now */
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_low_level_input: Could not allocate input pbuf\n"));
return NULL;
}
/* Copy buffer */
src = (u8_t *) lpc_enetdata->prxd[idx].packet;
for(q = p; q != NULL; q = q->next) {
MEMCPY((u8_t *) q->payload, src, q->len);
src += q->len;
}
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_low_level_input: Packet received: %p, size %d (index=%d)\n",
p, length, idx));
/* Save size */
p->tot_len = (u16_t) length;
LINK_STATS_INC(link.recv);
}
idx++;
if (idx >= LPC_NUM_BUFF_RXDESCS)
idx = 0;
LPC_EMAC->RxConsumeIndex = idx;
}
return p;
}
#endif /* End of RX copied or zero-copy driver section */
/** \brief Attempt to read a packet from the EMAC interface.
\param[in] netif the lwip network interface structure for this lpc_enetif
*/
void lpc_enetif_input(struct netif *netif)
{
struct lpc_enetif *lpc_enetif = netif->state;
struct eth_hdr *ethhdr;
struct pbuf *p;
/* move received packet into a new pbuf */
p = lpc_low_level_input(netif);
if (p == NULL)
return;
/* points to packet payload, which starts with an Ethernet header */
ethhdr = p->payload;
switch (htons(ethhdr->type)) {
case ETHTYPE_IP:
case ETHTYPE_ARP:
#if PPPOE_SUPPORT
case ETHTYPE_PPPOEDISC:
case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
/* full packet send to tcpip_thread to process */
if (netif->input(p, netif) != ERR_OK) {
LWIP_DEBUGF(NETIF_DEBUG, ("lpc_enetif_input: IP input error\n"));
/* Free buffer */
pbuf_free(p);
}
break;
default:
/* Return buffer */
pbuf_free(p);
break;
}
}
/** \brief Determine if the passed address is usable for the ethernet
DMA controller.
\param [in] addr Address of packet to check for DMA safe operation
\return 1 if the packet address is not safe, otherwise 0
*/
static s32_t lpc_packet_addr_notsafe(void *addr) {
/* Check for legal address ranges */
if ((((u32_t) addr >= 0x20000000) && ((u32_t) addr < 0x20008000)) ||
(((u32_t) addr >= 0x80000000) && ((u32_t) addr < 0xF0000000)))
return 0;
return 1;
}
#if LPC_PBUF_TX_ZEROCOPY
/** \brief Sets up the TX descriptor ring buffers.
This function sets up the descriptor list used for transmit packets.
\param [in] lpc_enetdata_def Pointer to driver data structure
*/
static err_t lpc_tx_setup(struct lpc_enetdata_def *lpc_enetdata)
{
s32_t idx;
/* Setup pointers to TX structures */
LPC_EMAC->TxDescriptor = (u32_t) &lpc_enetdata->ptxd[0];
LPC_EMAC->TxStatus = (u32_t) &lpc_enetdata->ptxs[0];
LPC_EMAC->TxDescriptorNumber = LPC_NUM_BUFF_TXDESCS - 1;
lpc_enetdata->lpc_last_tx_idx = 0;
/* Build TX descriptors for local buffers */
for (idx = 0; idx < LPC_NUM_BUFF_TXDESCS; idx++) {
lpc_enetdata->ptxd[idx].control = 0;
lpc_enetdata->ptxs[idx].statusinfo = 0xFFFFFFFF;
}
return ERR_OK;
}
/** \brief Free TX buffers that are complete
\param [in] lpc_enetdata_def Pointer to driver data structure
\param [in] cidx EMAC current descriptor comsumer index
*/
static void lpc_tx_reclaim_st(struct lpc_enetdata_def *lpc_enetdata, u32_t cidx)
{
while (cidx != lpc_enetdata->lpc_last_tx_idx) {
if (lpc_enetdata->txb[lpc_enetdata->lpc_last_tx_idx] != NULL) {
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_tx_reclaim_st: Freeing packet %p (index %d)\n",
lpc_enetdata->txb[lpc_enetdata->lpc_last_tx_idx],
lpc_enetdata->lpc_last_tx_idx));
pbuf_free(lpc_enetdata->txb[lpc_enetdata->lpc_last_tx_idx]);
lpc_enetdata->txb[lpc_enetdata->lpc_last_tx_idx] = NULL;
}
lpc_enetdata->lpc_last_tx_idx++;
if (lpc_enetdata->lpc_last_tx_idx >= LPC_NUM_BUFF_TXDESCS)
lpc_enetdata->lpc_last_tx_idx = 0;
}
}
/** \brief User call for freeingTX buffers that are complete
\param [in] netif the lwip network interface structure for this lpc_enetif
*/
void lpc_tx_reclaim(struct netif *netif)
{
lpc_tx_reclaim_st((struct lpc_enetdata_def *) netif->state,
LPC_EMAC->TxConsumeIndex);
}
/** \brief Polls if an available TX descriptor is ready. Can be used to
determine if the low level transmit function will block.
\param [in] netif the lwip network interface structure for this lpc_enetif
\return 0 if no descriptors are read, or >0
*/
s32_t lpc_tx_ready(struct netif *netif)
{
s32_t fb;
u32_t idx, cidx;
cidx = LPC_EMAC->TxConsumeIndex;
lpc_tx_reclaim_st((struct lpc_enetdata_def *) netif->state, cidx);
idx = LPC_EMAC->TxProduceIndex;
/* Determine number of free buffers */
if (idx == cidx)
fb = LPC_NUM_BUFF_TXDESCS;
else if (cidx > idx)
fb = (LPC_NUM_BUFF_TXDESCS - 1) -
((idx + LPC_NUM_BUFF_TXDESCS) - cidx);
else
fb = (LPC_NUM_BUFF_TXDESCS - 1) - (cidx - idx);
return fb;
}
/** \brief Low level output of a packet. Never call this from an
interrupt context, as it may block until TX descriptors
become available.
\param netif the lwip network interface structure for this lpc_enetif
\param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
\return ERR_OK if the packet could be sent
an err_t value if the packet couldn't be sent
*/
static err_t lpc_low_level_output(struct netif *netif, struct pbuf *p)
{
struct lpc_enetdata_def *lpc_enetdata = netif->state;
struct pbuf *q;
u8_t *dst;
u32_t idx, sz = 0;
err_t err = ERR_OK;
struct pbuf *np;
u32_t dn, notdmasafe = 0;
/* Error handling for TX underruns. This should never happen unless
something is holding the bus or the clocks are going too slow. It
can probably be safely removed. */
if (LPC_EMAC->IntStatus & EMAC_INT_TX_UNDERRUN) {
LINK_STATS_INC(link.err);
LINK_STATS_INC(link.drop);
/* Reset the TX side */
LPC_EMAC->MAC1 |= EMAC_MAC1_RES_TX;
LPC_EMAC->IntClear = EMAC_INT_TX_UNDERRUN;
/* De-allocate all queued TX pbufs */
for (idx = 0; idx < LPC_NUM_BUFF_RXDESCS; idx++) {
if (lpc_enetdata->txb[idx] != NULL) {
pbuf_free(lpc_enetdata->txb[idx]);
lpc_enetdata->txb[idx] = NULL;
}
}
/* Start TX side again */
lpc_tx_setup(lpc_enetdata);
}
/* Zero-copy TX buffers may be fragmented across mutliple payload
chains. Determine the number of descriptors needed for the
transfer. The pbuf chaining can be a mess! */
dn = (u32_t) pbuf_clen(p);
/* Test to make sure packet addresses are DMA safe. A DMA safe
address is once that uses external memory or periphheral RAM.
IRAM and FLASH are not safe! */
for (q = p; q != NULL; q = q->next)
notdmasafe += lpc_packet_addr_notsafe(q->payload);
#if LPC_TX_PBUF_BOUNCE_EN==1
/* If the pbuf is not DMA safe, a new bounce buffer (pbuf) will be
created that will be used instead. This requires an copy from the
non-safe DMA region to the new pbuf */
if (notdmasafe) {
/* Allocate a pbuf in DMA memory */
np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if (np == NULL)
return ERR_MEM;
/* This buffer better be contiguous! */
LWIP_ASSERT("lpc_low_level_output: New transmit pbuf is chained",
(pbuf_clen(np) == 1));
/* Copy to DMA safe pbuf */
dst = (u8_t *) np->payload;
for(q = p; q != NULL; q = q->next) {
/* Copy the buffer to the descriptor's buffer */
MEMCPY(dst, (u8_t *) q->payload, q->len);
dst += q->len;
}
np->len = p->tot_len;
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
("lpc_low_level_output: Switched to DMA safe buffer, old=%p, new=%p\n",
q, np));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -