/** \brief Low level output of a packet. Never call this from an * interrupt context, as it may block until TX descriptors * become available. * * \param[in] netif the lwip network interface structure for this lpc_enetif * \param[in] p the MAC packet to send (e.g. IP packet including MAC addresses and type) * \return ERR_OK if the packet could be sent or 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 *lpc_enetif = netif->state; struct pbuf *q; u8_t *dst; u32_t idx, notdmasafe = 0; struct pbuf *np; s32_t dn; /* 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 = (s32_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\r\n", q, np)); /* use the new buffer for descrptor queueing. The original pbuf will be de-allocated outsuide this driver. */ p = np; dn = 1; } #else if (notdmasafe) LWIP_ASSERT("lpc_low_level_output: Not a DMA safe pbuf", (notdmasafe == 0)); #endif /* Wait until enough descriptors are available for the transfer. */ /* THIS WILL BLOCK UNTIL THERE ARE ENOUGH DESCRIPTORS AVAILABLE */ while (dn > lpc_tx_ready(netif)) #if NO_SYS == 0 osSemaphoreWait(lpc_enetif->xTXDCountSem.id, osWaitForever); #else osDelay(1); #endif /* Get free TX buffer index */ idx = LPC_EMAC->TxProduceIndex; #if NO_SYS == 0 /* Get exclusive access */ sys_mutex_lock(&lpc_enetif->TXLockMutex); #endif /* Prevent LWIP from de-allocating this pbuf. The driver will free it once it's been transmitted. */ if (!notdmasafe) pbuf_ref(p); /* Setup transfers */ q = p; while (dn > 0) { dn--; /* Only save pointer to free on last descriptor */ if (dn == 0) { /* Save size of packet and signal it's ready */ lpc_enetif->ptxd[idx].control = (q->len - 1) | EMAC_TCTRL_INT | EMAC_TCTRL_LAST; lpc_enetif->txb[idx] = p; } else { /* Save size of packet, descriptor is not last */ lpc_enetif->ptxd[idx].control = (q->len - 1) | EMAC_TCTRL_INT; lpc_enetif->txb[idx] = NULL; } LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE, ("lpc_low_level_output: pbuf packet(%p) sent, chain#=%d," " size = %d (index=%d)\r\n", q->payload, dn, q->len, idx)); lpc_enetif->ptxd[idx].packet = (u32_t) q->payload; q = q->next; idx++; if (idx >= LPC_NUM_BUFF_TXDESCS) idx = 0; } LPC_EMAC->TxProduceIndex = idx; LINK_STATS_INC(link.xmit); #if NO_SYS == 0 /* Restore access */ sys_mutex_unlock(&lpc_enetif->TXLockMutex); #endif return ERR_OK; }
/* Low level output of a packet. Never call this from an interrupt context, as it may block until TX descriptors become available */ static err_t lpc_low_level_output(struct netif *netif, struct pbuf *sendp) { struct lpc_enetdata *lpc_netifdata = netif->state; u32_t idx, fidx, dn; struct pbuf *p = sendp; #if LPC_CHECK_SLOWMEM == 1 struct pbuf *q, *wp; u8_t *dst; int pcopy = 0; /* Check packet address to determine if it's in slow memory and relocate if necessary */ for (q = p; ((q != NULL) && (pcopy == 0)); q = q->next) { fidx = 0; for (idx = 0; idx < sizeof(slmem); idx += sizeof(struct lpc_slowmem_array_t)) { if ((q->payload >= (void *) slmem[fidx].start) && (q->payload <= (void *) slmem[fidx].end)) { /* Needs copy */ pcopy = 1; } } } if (pcopy) { /* Create a new pbuf with the total pbuf size */ wp = pbuf_alloc(PBUF_RAW, (u16_t) EMAC_ETH_MAX_FLEN, PBUF_RAM); if (!wp) { /* Exit with error */ return ERR_MEM; } /* Copy pbuf */ dst = (u8_t *) wp->payload; wp->tot_len = 0; for (q = p; q != NULL; q = q->next) { MEMCPY(dst, (u8_t *) q->payload, q->len); dst += q->len; wp->tot_len += q->len; } wp->len = wp->tot_len; /* LWIP will free original pbuf on exit of function */ p = sendp = wp; } #endif /* 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); /* Wait until enough descriptors are available for the transfer. */ /* THIS WILL BLOCK UNTIL THERE ARE ENOUGH DESCRIPTORS AVAILABLE */ while (dn > lpc_tx_ready(netif)) #if NO_SYS == 0 {xSemaphoreTake(lpc_netifdata->xTXDCountSem, 0); } #else {msDelay(1); } #endif /* Get the next free descriptor index */ fidx = idx = lpc_netifdata->tx_fill_idx; #if NO_SYS == 0 /* Get exclusive access */ sys_mutex_lock(&lpc_netifdata->TXLockMutex); #endif /* Fill in the next free descriptor(s) */ while (dn > 0) { dn--; /* Setup packet address and length */ lpc_netifdata->ptdesc[idx].B1ADD = (u32_t) p->payload; lpc_netifdata->ptdesc[idx].BSIZE = (u32_t) TDES_ENH_BS1(p->len); /* Save pointer to pbuf so we can reclain the memory for the pbuf after the buffer has been sent. Only the first pbuf in a chain is saved since the full chain doesn't need to be freed. */ /* For first packet only, first flag */ lpc_netifdata->tx_free_descs--; if (idx == fidx) { lpc_netifdata->ptdesc[idx].CTRLSTAT |= TDES_ENH_FS; #if LPC_CHECK_SLOWMEM == 1 /* If this is a copied pbuf, then avoid getting the extra reference or the TX reclaim will be off by 1 */ if (!pcopy) { pbuf_ref(p); } #else /* Increment reference count on this packet so LWIP doesn't attempt to free it on return from this call */ pbuf_ref(p); #endif } else { lpc_netifdata->ptdesc[idx].CTRLSTAT |= TDES_OWN; } /* Save address of pbuf, but make sure it's associated with the first chained pbuf so it gets freed once all pbuf chains are transferred. */ if (!dn) { lpc_netifdata->txpbufs[idx] = sendp; } else { lpc_netifdata->txpbufs[idx] = NULL; } /* For last packet only, interrupt and last flag */ if (dn == 0) { lpc_netifdata->ptdesc[idx].CTRLSTAT |= TDES_ENH_LS | TDES_ENH_IC; } /* IP checksumming requires full buffering in IP */ lpc_netifdata->ptdesc[idx].CTRLSTAT |= TDES_ENH_CIC(3); LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE, ("lpc_low_level_output: pbuf packet %p sent, chain %d," " size %d, index %d, free %d\n", p, dn, p->len, idx, lpc_netifdata->tx_free_descs)); /* Update next available descriptor */ idx++; if (idx >= LPC_NUM_BUFF_TXDESCS) { idx = 0; } /* Next packet fragment */ p = p->next; } lpc_netifdata->tx_fill_idx = idx; LINK_STATS_INC(link.xmit); /* Give first descriptor to DMA to start transfer */ lpc_netifdata->ptdesc[fidx].CTRLSTAT |= TDES_OWN; /* Tell DMA to poll descriptors to start transfer */ LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1; #if NO_SYS == 0 /* Restore access */ sys_mutex_unlock(&lpc_netifdata->TXLockMutex); #endif return ERR_OK; }