mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-10-15 02:16:05 +08:00
if_lpe.c: Use interface transmit
This avoids the need for a transmit task and transmit interrupts.
This commit is contained in:
@@ -7,13 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 embedded brains GmbH. All rights reserved.
|
* Copyright (C) 2009, 2022 embedded brains GmbH
|
||||||
*
|
|
||||||
* embedded brains GmbH
|
|
||||||
* Obere Lagerstr. 30
|
|
||||||
* 82178 Puchheim
|
|
||||||
* Germany
|
|
||||||
* <rtems@embedded-brains.de>
|
|
||||||
*
|
*
|
||||||
* The license and distribution terms for this file may be
|
* The license and distribution terms for this file may be
|
||||||
* found in the file LICENSE in this distribution or at
|
* found in the file LICENSE in this distribution or at
|
||||||
@@ -264,8 +258,6 @@ static volatile lpc_eth_controller *const lpc_eth =
|
|||||||
|
|
||||||
#define LPC_ETH_EVENT_INIT_TX RTEMS_EVENT_1
|
#define LPC_ETH_EVENT_INIT_TX RTEMS_EVENT_1
|
||||||
|
|
||||||
#define LPC_ETH_EVENT_TXSTART RTEMS_EVENT_2
|
|
||||||
|
|
||||||
#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3
|
#define LPC_ETH_EVENT_INTERRUPT RTEMS_EVENT_3
|
||||||
|
|
||||||
#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4
|
#define LPC_ETH_EVENT_STOP RTEMS_EVENT_4
|
||||||
@@ -275,9 +267,6 @@ static volatile lpc_eth_controller *const lpc_eth =
|
|||||||
#define LPC_ETH_INTERRUPT_RECEIVE \
|
#define LPC_ETH_INTERRUPT_RECEIVE \
|
||||||
(ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE)
|
(ETH_INT_RX_ERROR | ETH_INT_RX_FINISHED | ETH_INT_RX_DONE)
|
||||||
|
|
||||||
#define LPC_ETH_INTERRUPT_TRANSMIT \
|
|
||||||
(ETH_INT_TX_DONE | ETH_INT_TX_FINISHED | ETH_INT_TX_ERROR)
|
|
||||||
|
|
||||||
#define LPC_ETH_RX_STAT_ERRORS \
|
#define LPC_ETH_RX_STAT_ERRORS \
|
||||||
(ETH_RX_STAT_CRC_ERROR \
|
(ETH_RX_STAT_CRC_ERROR \
|
||||||
| ETH_RX_STAT_SYMBOL_ERROR \
|
| ETH_RX_STAT_SYMBOL_ERROR \
|
||||||
@@ -317,7 +306,6 @@ typedef struct {
|
|||||||
uint32_t anlpar;
|
uint32_t anlpar;
|
||||||
struct callout watchdog_callout;
|
struct callout watchdog_callout;
|
||||||
rtems_id receive_task;
|
rtems_id receive_task;
|
||||||
rtems_id transmit_task;
|
|
||||||
unsigned rx_unit_count;
|
unsigned rx_unit_count;
|
||||||
unsigned tx_unit_count;
|
unsigned tx_unit_count;
|
||||||
volatile lpc_eth_transfer_descriptor *rx_desc_table;
|
volatile lpc_eth_transfer_descriptor *rx_desc_table;
|
||||||
@@ -331,7 +319,6 @@ typedef struct {
|
|||||||
unsigned received_frames;
|
unsigned received_frames;
|
||||||
unsigned receive_interrupts;
|
unsigned receive_interrupts;
|
||||||
unsigned transmitted_frames;
|
unsigned transmitted_frames;
|
||||||
unsigned transmit_interrupts;
|
|
||||||
unsigned receive_drop_errors;
|
unsigned receive_drop_errors;
|
||||||
unsigned receive_overrun_errors;
|
unsigned receive_overrun_errors;
|
||||||
unsigned receive_fragment_errors;
|
unsigned receive_fragment_errors;
|
||||||
@@ -435,26 +422,14 @@ static void lpc_eth_interrupt_handler(void *arg)
|
|||||||
} else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) {
|
} else if ((is & LPC_ETH_INTERRUPT_RECEIVE) != 0) {
|
||||||
re = LPC_ETH_EVENT_INTERRUPT;
|
re = LPC_ETH_EVENT_INTERRUPT;
|
||||||
ie |= LPC_ETH_INTERRUPT_RECEIVE;
|
ie |= LPC_ETH_INTERRUPT_RECEIVE;
|
||||||
|
++e->receive_interrupts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send events to receive task */
|
/* Send events to receive task */
|
||||||
if (re != 0) {
|
if (re != 0) {
|
||||||
++e->receive_interrupts;
|
|
||||||
(void) rtems_event_send(e->receive_task, re);
|
(void) rtems_event_send(e->receive_task, re);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check transmit interrupts */
|
|
||||||
if ((is & LPC_ETH_INTERRUPT_TRANSMIT) != 0) {
|
|
||||||
te = LPC_ETH_EVENT_INTERRUPT;
|
|
||||||
ie |= LPC_ETH_INTERRUPT_TRANSMIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send events to transmit task */
|
|
||||||
if (te != 0) {
|
|
||||||
++e->transmit_interrupts;
|
|
||||||
(void) rtems_event_send(e->transmit_task, te);
|
|
||||||
}
|
|
||||||
|
|
||||||
LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te);
|
LPC_ETH_PRINTK("interrupt: rx = 0x%08x, tx = 0x%08x\n", re, te);
|
||||||
|
|
||||||
/* Update interrupt mask */
|
/* Update interrupt mask */
|
||||||
@@ -482,24 +457,6 @@ static void lpc_eth_disable_receive_interrupts(void)
|
|||||||
rtems_interrupt_enable(level);
|
rtems_interrupt_enable(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lpc_eth_enable_transmit_interrupts(void)
|
|
||||||
{
|
|
||||||
rtems_interrupt_level level;
|
|
||||||
|
|
||||||
rtems_interrupt_disable(level);
|
|
||||||
lpc_eth->intenable |= LPC_ETH_INTERRUPT_TRANSMIT;
|
|
||||||
rtems_interrupt_enable(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lpc_eth_disable_transmit_interrupts(void)
|
|
||||||
{
|
|
||||||
rtems_interrupt_level level;
|
|
||||||
|
|
||||||
rtems_interrupt_disable(level);
|
|
||||||
lpc_eth->intenable &= ~LPC_ETH_INTERRUPT_TRANSMIT;
|
|
||||||
rtems_interrupt_enable(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
|
static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
|
||||||
{
|
{
|
||||||
volatile uint32_t *const status = e->tx_status_table;
|
volatile uint32_t *const status = e->tx_status_table;
|
||||||
@@ -512,9 +469,6 @@ static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
|
|||||||
#endif
|
#endif
|
||||||
uint32_t produce_index;
|
uint32_t produce_index;
|
||||||
|
|
||||||
/* Disable transmit interrupts */
|
|
||||||
lpc_eth_disable_transmit_interrupts();
|
|
||||||
|
|
||||||
/* Disable transmitter */
|
/* Disable transmitter */
|
||||||
lpc_eth->command &= ~ETH_CMD_TX_ENABLE;
|
lpc_eth->command &= ~ETH_CMD_TX_ENABLE;
|
||||||
|
|
||||||
@@ -526,9 +480,6 @@ static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
|
|||||||
/* Reset */
|
/* Reset */
|
||||||
lpc_eth->command |= ETH_CMD_TX_RESET;
|
lpc_eth->command |= ETH_CMD_TX_RESET;
|
||||||
|
|
||||||
/* Clear transmit interrupts */
|
|
||||||
lpc_eth->intclear = LPC_ETH_INTERRUPT_TRANSMIT;
|
|
||||||
|
|
||||||
/* Transmit descriptors */
|
/* Transmit descriptors */
|
||||||
lpc_eth->txdescriptornum = index_max;
|
lpc_eth->txdescriptornum = index_max;
|
||||||
lpc_eth->txdescriptor = (uint32_t) desc;
|
lpc_eth->txdescriptor = (uint32_t) desc;
|
||||||
@@ -537,12 +488,8 @@ static void lpc_eth_initialize_transmit(lpc_eth_driver_entry *e)
|
|||||||
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
||||||
/* Discard outstanding fragments (= data loss) */
|
/* Discard outstanding fragments (= data loss) */
|
||||||
for (produce_index = 0; produce_index <= index_max; ++produce_index) {
|
for (produce_index = 0; produce_index <= index_max; ++produce_index) {
|
||||||
struct mbuf *victim = mbufs [produce_index];
|
m_freem(mbufs [produce_index]);
|
||||||
|
mbufs [produce_index] = NULL;
|
||||||
if (victim != NULL) {
|
|
||||||
m_free(victim);
|
|
||||||
mbufs [produce_index] = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Initialize descriptor table */
|
/* Initialize descriptor table */
|
||||||
@@ -815,20 +762,10 @@ static struct mbuf *lpc_eth_next_fragment(
|
|||||||
uint32_t *ctrl
|
uint32_t *ctrl
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
struct mbuf *n = NULL;
|
struct mbuf *n;
|
||||||
int size = 0;
|
int size;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (m == NULL) {
|
|
||||||
/* Dequeue first fragment of the next frame */
|
|
||||||
IF_DEQUEUE(&ifp->if_snd, m);
|
|
||||||
|
|
||||||
/* Empty queue? */
|
|
||||||
if (m == NULL) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get fragment size */
|
/* Get fragment size */
|
||||||
size = m->m_len;
|
size = m->m_len;
|
||||||
|
|
||||||
@@ -836,8 +773,12 @@ static struct mbuf *lpc_eth_next_fragment(
|
|||||||
/* Now we have a not empty fragment */
|
/* Now we have a not empty fragment */
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* Discard empty fragments */
|
/* Skip empty fragments */
|
||||||
m = m_free(m);
|
m = m->m_next;
|
||||||
|
|
||||||
|
if (m == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,266 +800,224 @@ static struct mbuf *lpc_eth_next_fragment(
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lpc_eth_transmit_task(rtems_task_argument arg)
|
static void lpc_eth_tx_reclaim(lpc_eth_driver_entry *e, struct ifnet *ifp)
|
||||||
{
|
{
|
||||||
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
||||||
rtems_event_set events = 0;
|
|
||||||
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) arg;
|
|
||||||
struct ifnet *ifp = e->ifp;
|
|
||||||
volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
|
|
||||||
volatile uint32_t *const status = e->tx_status_table;
|
volatile uint32_t *const status = e->tx_status_table;
|
||||||
|
volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
|
||||||
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
||||||
struct mbuf **const mbufs = e->tx_buf_table;
|
struct mbuf **const mbufs = e->tx_buf_table;
|
||||||
#else
|
#else
|
||||||
char *const buf = e->tx_buf_table;
|
char *const buf = e->tx_buf_table;
|
||||||
#endif
|
#endif
|
||||||
struct mbuf *m = NULL;
|
|
||||||
uint32_t const index_max = e->tx_unit_count - 1;
|
uint32_t const index_max = e->tx_unit_count - 1;
|
||||||
uint32_t ctrl = 0;
|
uint32_t consume_index = e->tx_consume_index;
|
||||||
|
|
||||||
LPC_ETH_PRINTF("%s\n", __func__);
|
/* Free consumed fragments */
|
||||||
|
|
||||||
/* Main event loop */
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint32_t produce_index;
|
/* Save last known consume index */
|
||||||
uint32_t consume_index;
|
uint32_t c = consume_index;
|
||||||
#ifndef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
|
||||||
uint32_t frame_length;
|
|
||||||
char *frame_buffer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Wait for events */
|
/* Get new consume index */
|
||||||
sc = rtems_event_receive(
|
consume_index = lpc_eth->txconsumeindex;
|
||||||
LPC_ETH_EVENT_STOP
|
|
||||||
| LPC_ETH_EVENT_TXSTART
|
|
||||||
| LPC_ETH_EVENT_INTERRUPT,
|
|
||||||
RTEMS_EVENT_ANY | RTEMS_WAIT,
|
|
||||||
RTEMS_NO_TIMEOUT,
|
|
||||||
&events
|
|
||||||
);
|
|
||||||
BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
|
|
||||||
|
|
||||||
LPC_ETH_PRINTF("tx: wake up: 0x%08" PRIx32 "\n", events);
|
/* Nothing consumed in the meantime? */
|
||||||
|
if (c == consume_index) {
|
||||||
/* Stop transmitter? */
|
break;
|
||||||
if ((events & LPC_ETH_EVENT_STOP) != 0) {
|
|
||||||
lpc_eth_control_request_complete(e);
|
|
||||||
|
|
||||||
/* Wait for events */
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LPE_LOCK(e);
|
while (c != consume_index) {
|
||||||
|
uint32_t s = status [c];
|
||||||
|
|
||||||
/* Get indices */
|
/* Update error counters */
|
||||||
produce_index = e->tx_produce_index;
|
if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) {
|
||||||
consume_index = e->tx_consume_index;
|
if ((s & ETH_TX_STAT_UNDERRUN) != 0) {
|
||||||
|
++e->transmit_underrun_errors;
|
||||||
#ifndef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
}
|
||||||
/* Fresh frame length and buffer start */
|
if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) {
|
||||||
frame_length = 0;
|
++e->transmit_late_collision_errors;
|
||||||
frame_buffer = (char *) desc [produce_index].start;
|
}
|
||||||
#endif
|
if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) {
|
||||||
|
++e->transmit_excessive_collision_errors;
|
||||||
/* Free consumed fragments */
|
}
|
||||||
while (true) {
|
if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) {
|
||||||
/* Save last known consume index */
|
++e->transmit_excessive_defer_errors;
|
||||||
uint32_t c = consume_index;
|
}
|
||||||
|
if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) {
|
||||||
/* Clear transmit interrupt status */
|
++e->transmit_no_descriptor_errors;
|
||||||
lpc_eth->intclear = LPC_ETH_INTERRUPT_TRANSMIT;
|
}
|
||||||
|
|
||||||
/* Get new consume index */
|
|
||||||
consume_index = lpc_eth->txconsumeindex;
|
|
||||||
|
|
||||||
/* Nothing consumed in the meantime? */
|
|
||||||
if (c == consume_index) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (c != consume_index) {
|
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
||||||
uint32_t s = status [c];
|
/* Release mbuf */
|
||||||
|
m_freem(mbufs [c]);
|
||||||
|
mbufs [c] = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Update error counters */
|
/* Next consume index */
|
||||||
if ((s & (ETH_TX_STAT_ERROR | ETH_TX_STAT_NO_DESCRIPTOR)) != 0) {
|
c = lpc_eth_increment(c, index_max);
|
||||||
if ((s & ETH_TX_STAT_UNDERRUN) != 0) {
|
}
|
||||||
++e->transmit_underrun_errors;
|
}
|
||||||
}
|
|
||||||
if ((s & ETH_TX_STAT_LATE_COLLISION) != 0) {
|
e->tx_consume_index = consume_index;
|
||||||
++e->transmit_late_collision_errors;
|
}
|
||||||
}
|
|
||||||
if ((s & ETH_TX_STAT_EXCESSIVE_COLLISION) != 0) {
|
static int lpc_eth_tx_enqueue(
|
||||||
++e->transmit_excessive_collision_errors;
|
lpc_eth_driver_entry *e,
|
||||||
}
|
struct ifnet *ifp,
|
||||||
if ((s & ETH_TX_STAT_EXCESSIVE_DEFER) != 0) {
|
struct mbuf *m0
|
||||||
++e->transmit_excessive_defer_errors;
|
)
|
||||||
}
|
{
|
||||||
if ((s & ETH_TX_STAT_NO_DESCRIPTOR) != 0) {
|
volatile lpc_eth_transfer_descriptor *const desc = e->tx_desc_table;
|
||||||
++e->transmit_no_descriptor_errors;
|
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
||||||
}
|
struct mbuf **const mbufs = e->tx_buf_table;
|
||||||
|
#else
|
||||||
|
char *const buf = e->tx_buf_table;
|
||||||
|
uint32_t frame_length;
|
||||||
|
char *frame_buffer;
|
||||||
|
#endif
|
||||||
|
uint32_t const index_max = e->tx_unit_count - 1;
|
||||||
|
uint32_t produce_index = e->tx_produce_index;
|
||||||
|
uint32_t consume_index = e->tx_consume_index;
|
||||||
|
struct mbuf *m = m0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint32_t ctrl;
|
||||||
|
|
||||||
|
/* Compute next produce index */
|
||||||
|
uint32_t p = lpc_eth_increment(produce_index, index_max);
|
||||||
|
|
||||||
|
/* Queue full? */
|
||||||
|
if (p == consume_index) {
|
||||||
|
LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m);
|
||||||
|
|
||||||
|
/* The queue is full */
|
||||||
|
return ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next fragment and control value */
|
||||||
|
m = lpc_eth_next_fragment(ifp, m, &ctrl);
|
||||||
|
|
||||||
|
/* New fragment? */
|
||||||
|
if (m != NULL) {
|
||||||
|
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
||||||
|
/* Set the transfer data */
|
||||||
|
rtems_cache_flush_multiple_data_lines(
|
||||||
|
mtod(m, const void *),
|
||||||
|
(size_t) m->m_len
|
||||||
|
);
|
||||||
|
desc [produce_index].start = mtod(m, uint32_t);
|
||||||
|
desc [produce_index].control = ctrl;
|
||||||
|
rtems_cache_flush_multiple_data_lines(
|
||||||
|
(void *) &desc [produce_index],
|
||||||
|
sizeof(desc [0])
|
||||||
|
);
|
||||||
|
|
||||||
|
LPC_ETH_PRINTF(
|
||||||
|
"tx: %02" PRIu32 ": %u %s\n",
|
||||||
|
produce_index, m->m_len,
|
||||||
|
(ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : ""
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Next produce index */
|
||||||
|
produce_index = p;
|
||||||
|
|
||||||
|
/* Last fragment of a frame? */
|
||||||
|
if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
|
||||||
|
/* Update the produce index */
|
||||||
|
lpc_eth->txproduceindex = produce_index;
|
||||||
|
e->tx_produce_index = produce_index;
|
||||||
|
|
||||||
|
mbufs [produce_index] = m0;
|
||||||
|
|
||||||
|
/* Increment transmitted frames counter */
|
||||||
|
++e->transmitted_frames;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
/* Next fragment of the frame */
|
||||||
/* Release mbuf */
|
m = m->m_next;
|
||||||
m_free(mbufs [c]);
|
#else
|
||||||
mbufs [c] = NULL;
|
size_t fragment_length = (size_t) m->m_len;
|
||||||
#endif
|
void *fragment_start = mtod(m, void *);
|
||||||
|
uint32_t new_frame_length = frame_length + fragment_length;
|
||||||
|
|
||||||
/* Next consume index */
|
/* Check buffer size */
|
||||||
c = lpc_eth_increment(c, index_max);
|
if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) {
|
||||||
}
|
LPC_ETH_PRINTF("tx: overflow\n");
|
||||||
}
|
|
||||||
|
|
||||||
/* Transmit new fragments */
|
/* Discard overflow data */
|
||||||
while (true) {
|
new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE;
|
||||||
/* Compute next produce index */
|
fragment_length = new_frame_length - frame_length;
|
||||||
uint32_t p = lpc_eth_increment(produce_index, index_max);
|
|
||||||
|
|
||||||
/* Get next fragment and control value */
|
/* Finalize frame */
|
||||||
m = lpc_eth_next_fragment(ifp, m, &ctrl);
|
ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
|
||||||
|
|
||||||
/* Queue full? */
|
/* Update error counter */
|
||||||
if (p == consume_index) {
|
++e->transmit_overflow_errors;
|
||||||
LPC_ETH_PRINTF("tx: full queue: 0x%08x\n", m);
|
}
|
||||||
|
|
||||||
/* The queue is full, wait for transmit interrupt */
|
LPC_ETH_PRINTF(
|
||||||
break;
|
"tx: copy: %" PRIu32 "%s%s\n",
|
||||||
}
|
fragment_length,
|
||||||
|
(m->m_flags & M_EXT) != 0 ? ", E" : "",
|
||||||
|
(m->m_flags & M_PKTHDR) != 0 ? ", H" : ""
|
||||||
|
);
|
||||||
|
|
||||||
/* New fragment? */
|
/* Copy fragment to buffer in Ethernet RAM */
|
||||||
if (m != NULL) {
|
memcpy(frame_buffer, fragment_start, fragment_length);
|
||||||
#ifdef LPC_ETH_CONFIG_USE_TRANSMIT_DMA
|
|
||||||
/* Set the transfer data */
|
if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
|
||||||
rtems_cache_flush_multiple_data_lines(
|
/* Finalize descriptor */
|
||||||
mtod(m, const void *),
|
desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK)
|
||||||
(size_t) m->m_len
|
| (new_frame_length - 1);
|
||||||
|
|
||||||
|
LPC_ETH_PRINTF(
|
||||||
|
"tx: %02" PRIu32 ": %" PRIu32 "\n",
|
||||||
|
produce_index,
|
||||||
|
new_frame_length
|
||||||
);
|
);
|
||||||
desc [produce_index].start = mtod(m, uint32_t);
|
|
||||||
desc [produce_index].control = ctrl;
|
/* Cache flush of data */
|
||||||
|
rtems_cache_flush_multiple_data_lines(
|
||||||
|
(const void *) desc [produce_index].start,
|
||||||
|
new_frame_length
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Cache flush of descriptor */
|
||||||
rtems_cache_flush_multiple_data_lines(
|
rtems_cache_flush_multiple_data_lines(
|
||||||
(void *) &desc [produce_index],
|
(void *) &desc [produce_index],
|
||||||
sizeof(desc [0])
|
sizeof(desc [0])
|
||||||
);
|
|
||||||
mbufs [produce_index] = m;
|
|
||||||
|
|
||||||
LPC_ETH_PRINTF(
|
|
||||||
"tx: %02" PRIu32 ": %u %s\n",
|
|
||||||
produce_index, m->m_len,
|
|
||||||
(ctrl & ETH_TX_CTRL_LAST) != 0 ? "L" : ""
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Next produce index */
|
/* Next produce index */
|
||||||
produce_index = p;
|
produce_index = p;
|
||||||
|
|
||||||
/* Last fragment of a frame? */
|
/* Update the produce index */
|
||||||
if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
|
lpc_eth->txproduceindex = produce_index;
|
||||||
/* Update the produce index */
|
|
||||||
lpc_eth->txproduceindex = produce_index;
|
|
||||||
|
|
||||||
/* Increment transmitted frames counter */
|
/* Fresh frame length and buffer start */
|
||||||
++e->transmitted_frames;
|
frame_length = 0;
|
||||||
}
|
frame_buffer = (char *) desc [produce_index].start;
|
||||||
|
|
||||||
/* Next fragment of the frame */
|
/* Increment transmitted frames counter */
|
||||||
m = m->m_next;
|
++e->transmitted_frames;
|
||||||
#else
|
} else {
|
||||||
size_t fragment_length = (size_t) m->m_len;
|
/* New frame length */
|
||||||
void *fragment_start = mtod(m, void *);
|
frame_length = new_frame_length;
|
||||||
uint32_t new_frame_length = frame_length + fragment_length;
|
|
||||||
|
|
||||||
/* Check buffer size */
|
/* Update current frame buffer start */
|
||||||
if (new_frame_length > LPC_ETH_CONFIG_TX_BUF_SIZE) {
|
frame_buffer += fragment_length;
|
||||||
LPC_ETH_PRINTF("tx: overflow\n");
|
}
|
||||||
|
|
||||||
/* Discard overflow data */
|
/* Free mbuf and get next */
|
||||||
new_frame_length = LPC_ETH_CONFIG_TX_BUF_SIZE;
|
m = m_free(m);
|
||||||
fragment_length = new_frame_length - frame_length;
|
#endif
|
||||||
|
|
||||||
/* Finalize frame */
|
|
||||||
ctrl |= LPC_ETH_LAST_FRAGMENT_FLAGS;
|
|
||||||
|
|
||||||
/* Update error counter */
|
|
||||||
++e->transmit_overflow_errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPC_ETH_PRINTF(
|
|
||||||
"tx: copy: %" PRIu32 "%s%s\n",
|
|
||||||
fragment_length,
|
|
||||||
(m->m_flags & M_EXT) != 0 ? ", E" : "",
|
|
||||||
(m->m_flags & M_PKTHDR) != 0 ? ", H" : ""
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Copy fragment to buffer in Ethernet RAM */
|
|
||||||
memcpy(frame_buffer, fragment_start, fragment_length);
|
|
||||||
|
|
||||||
if ((ctrl & ETH_TX_CTRL_LAST) != 0) {
|
|
||||||
/* Finalize descriptor */
|
|
||||||
desc [produce_index].control = (ctrl & ~ETH_TX_CTRL_SIZE_MASK)
|
|
||||||
| (new_frame_length - 1);
|
|
||||||
|
|
||||||
LPC_ETH_PRINTF(
|
|
||||||
"tx: %02" PRIu32 ": %" PRIu32 "\n",
|
|
||||||
produce_index,
|
|
||||||
new_frame_length
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Cache flush of data */
|
|
||||||
rtems_cache_flush_multiple_data_lines(
|
|
||||||
(const void *) desc [produce_index].start,
|
|
||||||
new_frame_length
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Cache flush of descriptor */
|
|
||||||
rtems_cache_flush_multiple_data_lines(
|
|
||||||
(void *) &desc [produce_index],
|
|
||||||
sizeof(desc [0])
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Next produce index */
|
|
||||||
produce_index = p;
|
|
||||||
|
|
||||||
/* Update the produce index */
|
|
||||||
lpc_eth->txproduceindex = produce_index;
|
|
||||||
|
|
||||||
/* Fresh frame length and buffer start */
|
|
||||||
frame_length = 0;
|
|
||||||
frame_buffer = (char *) desc [produce_index].start;
|
|
||||||
|
|
||||||
/* Increment transmitted frames counter */
|
|
||||||
++e->transmitted_frames;
|
|
||||||
} else {
|
|
||||||
/* New frame length */
|
|
||||||
frame_length = new_frame_length;
|
|
||||||
|
|
||||||
/* Update current frame buffer start */
|
|
||||||
frame_buffer += fragment_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free mbuf and get next */
|
|
||||||
m = m_free(m);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
/* Nothing to transmit */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save indices */
|
|
||||||
e->tx_produce_index = produce_index;
|
|
||||||
e->tx_consume_index = consume_index;
|
|
||||||
|
|
||||||
/* No more fragments? */
|
|
||||||
if (m == NULL) {
|
|
||||||
/* Interface is now inactive */
|
|
||||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
||||||
} else {
|
} else {
|
||||||
LPC_ETH_PRINTF("tx: enable interrupts\n");
|
/* Nothing to transmit */
|
||||||
|
m_freem(m0);
|
||||||
/* Enable transmit interrupts */
|
return 0;
|
||||||
lpc_eth_enable_transmit_interrupts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LPE_UNLOCK(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1484,9 +1383,8 @@ static int lpc_eth_up_or_down(lpc_eth_driver_entry *e, bool up)
|
|||||||
);
|
);
|
||||||
BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
|
BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
/* Stop tasks */
|
/* Stop task */
|
||||||
lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
|
lpc_eth_control_request(e, e->receive_task, LPC_ETH_EVENT_STOP);
|
||||||
lpc_eth_control_request(e, e->transmit_task, LPC_ETH_EVENT_STOP);
|
|
||||||
|
|
||||||
lpc_eth_soft_reset();
|
lpc_eth_soft_reset();
|
||||||
lpc_eth_phy_down(e);
|
lpc_eth_phy_down(e);
|
||||||
@@ -1604,17 +1502,38 @@ static int lpc_eth_interface_ioctl(
|
|||||||
return eno;
|
return eno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lpc_eth_interface_start(struct ifnet *ifp)
|
static int lpc_eth_interface_transmit(struct ifnet *ifp, struct mbuf *m)
|
||||||
{
|
{
|
||||||
rtems_status_code sc = RTEMS_SUCCESSFUL;
|
|
||||||
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
|
lpc_eth_driver_entry *e = (lpc_eth_driver_entry *) ifp->if_softc;
|
||||||
|
int eno;
|
||||||
|
|
||||||
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
LPE_LOCK(e);
|
||||||
|
|
||||||
if (e->state == LPC_ETH_STATE_UP) {
|
if (e->state == LPC_ETH_STATE_UP) {
|
||||||
sc = rtems_event_send(e->transmit_task, LPC_ETH_EVENT_TXSTART);
|
eno = lpc_eth_tx_enqueue(e, ifp, m);
|
||||||
BSD_ASSERT(sc == RTEMS_SUCCESSFUL);
|
lpc_eth_tx_reclaim(e, ifp);
|
||||||
|
|
||||||
|
if (__predict_false(eno != 0)) {
|
||||||
|
struct mbuf *n;
|
||||||
|
|
||||||
|
n = m_defrag(m, M_NOWAIT);
|
||||||
|
if (n != NULL) {
|
||||||
|
m = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
eno = lpc_eth_tx_enqueue(e, ifp, m);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eno = ENETDOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eno != 0) {
|
||||||
|
m_freem(m);
|
||||||
|
if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
LPE_UNLOCK(e);
|
||||||
|
return eno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lpc_eth_interface_watchdog(void *arg)
|
static void lpc_eth_interface_watchdog(void *arg)
|
||||||
@@ -1772,7 +1691,7 @@ static int lpc_eth_attach(device_t dev)
|
|||||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
||||||
ifp->if_init = lpc_eth_interface_init;
|
ifp->if_init = lpc_eth_interface_init;
|
||||||
ifp->if_ioctl = lpc_eth_interface_ioctl;
|
ifp->if_ioctl = lpc_eth_interface_ioctl;
|
||||||
ifp->if_start = lpc_eth_interface_start;
|
ifp->if_transmit = lpc_eth_interface_transmit;
|
||||||
ifp->if_qflush = if_qflush;
|
ifp->if_qflush = if_qflush;
|
||||||
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
|
ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
|
||||||
IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1);
|
IFQ_SET_MAXLEN(&ifp->if_snd, LPC_ETH_CONFIG_TX_UNIT_COUNT_MAX - 1);
|
||||||
@@ -1798,21 +1717,6 @@ static int lpc_eth_attach(device_t dev)
|
|||||||
(rtems_task_argument)e
|
(rtems_task_argument)e
|
||||||
);
|
);
|
||||||
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
|
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
|
||||||
status = rtems_task_create(
|
|
||||||
rtems_build_name('n', 't', 't', 'x'),
|
|
||||||
rtems_bsd_get_task_priority(device_get_name(e->dev)),
|
|
||||||
4096,
|
|
||||||
RTEMS_DEFAULT_MODES,
|
|
||||||
RTEMS_DEFAULT_ATTRIBUTES,
|
|
||||||
&e->transmit_task
|
|
||||||
);
|
|
||||||
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
|
|
||||||
status = rtems_task_start(
|
|
||||||
e->transmit_task,
|
|
||||||
lpc_eth_transmit_task,
|
|
||||||
(rtems_task_argument)e
|
|
||||||
);
|
|
||||||
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
|
|
||||||
|
|
||||||
if_link_state_change(e->ifp, LINK_STATE_UP);
|
if_link_state_change(e->ifp, LINK_STATE_UP);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user