Цитата(pitt @ Nov 24 2012, 18:25)

А почему нельзя DMA изначально направить на память приложения без дополнительной пересылки?
Zero-copy transmission? Можно, я так и делаю:
CODE
/**
* @file stm32eth.c
* @brief Драйвер Ethernet для STM32
*/
#include "stm32eth.h"
#include "stm32f2regs.h"
#include "timer.h"
#include "netif/etharp.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "assert_static.h"
#include <string.h>
#include <assert.h>
#define SMI_TMPL 0x00000004 /* sets SMI clock range and PHY address */
#define RX_RING_SIZE 32
#define TX_RING_SIZE 32
struct buf_desc
{
uint32_t volatile status;
uint16_t len[2];
void* ptr[2];
struct pbuf* p[2];
};
static struct netif *mynetif;
static bool link, act;
static bool rx_more; /* if set, need to scan or allocate RX descriptors */
static struct buf_desc tx_desc[TX_RING_SIZE], rx_desc[RX_RING_SIZE];
/* first RX descriptor to check for new frame */
static int rx_current;
/* number of initialized RX descriptors */
static int rx_count;
static bool
smi_busy(void)
{
return !!REGBIT(ETH_MACMIIAR, 0);
}
static void
smi_write(int reg, int val)
{
ETH_MACMIIDR = val;
ETH_MACMIIAR = (reg << 6) | SMI_TMPL | 3;
while (smi_busy())
{
/* wait */
}
}
static void
smi_start_read(int reg)
{
ETH_MACMIIAR = (reg << 6) | SMI_TMPL | 1;
}
static int
smi_read_data(void)
{
return ETH_MACMIIDR;
}
static void
phy_init(void)
{
smi_write(0, 0);
smi_write(0x1F, 0x8900);
}
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
int i;
uint32_t mask;
while ((ETH_DMASR & 0x00700000) != 0x00600000)
{
/* wait until Tx DMA is suspended */
}
mask = (1u << 31) /* OWN bit */
| (1 << 28); /* first segment */
i = -1;
for (;;)
{
i++;
if (i == TX_RING_SIZE)
{
/* not enough TX descriptors */
return ERR_MEM;
}
tx_desc[i].len[0] = p->len;
tx_desc[i].ptr[0] = p->payload;
p = p->next;
if (p)
{
tx_desc[i].len[1] = p->len;
tx_desc[i].ptr[1] = p->payload;
p = p->next;
if (p)
{
tx_desc[i].status = mask;
mask = (1u << 31); /* OWN bit */
}
else
{
break;
}
}
else
{
tx_desc[i].len[1] = 0;
break;
}
}
mask |= (1 << 21) /* end of ring */
| (1 << 29); /* last segment */
tx_desc[i].status = mask;
REGBIT(ETH_DMASR, 10) = 1; /* reset ETS flag in status register */
ETH_DMATPDR = 0; /* start transmission */
while (REGBIT(ETH_DMASR, 10) == 0)
{
/* wait for data to be copied into Tx FIFO */
}
act = true;
return ERR_OK;
}
/**
* Initialize the Rx buffer descriptor ring by allocating buffers
* and assigning them to descriptors
*/
static void
rx_setup(void)
{
/* Index of first uninitialized Rx buffer descriptor */
int i, rx_start = rx_current + rx_count;
for (i = rx_start; rx_count < RX_RING_SIZE; i++)
{
struct pbuf *p;
int j = i & (RX_RING_SIZE - 1);
p = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
if (!p)
{
rx_more = true;
break;
}
rx_desc[j].p[0] = p;
rx_desc[j].ptr[0] = p->payload;
rx_desc[j].len[0] = p->len;
if (j == (RX_RING_SIZE - 1))
{
rx_desc[j].len[0] |= (1 << 15);
}
rx_count++;
p = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
if (p)
{
rx_desc[j].p[1] = p;
rx_desc[j].ptr[1] = p->payload;
rx_desc[j].len[1] = p->len;
}
else
{
rx_desc[j].len[1] = 0;
rx_more = true;
}
rx_desc[j].status = 1u << 31; /* OWN bit */
}
i = rx_current + rx_count;
while (i != rx_start)
{
i--;
rx_desc[i & (RX_RING_SIZE - 1)].status = 1u << 31; /* OWN bit */
}
}
/**
* Discard frame in case of reception error by deallocating buffers
*
* @param first Index of first buffer descriptor to discard
* @param last Index of last buffer descriptor to discard
*/
static void
discard_frame(int first, int last)
{
int i = first - 1;
do
{
i = (i + 1) & (RX_RING_SIZE - 1);
pbuf_free(rx_desc[i].p[0]);
if (rx_desc[i].len[1] != 0)
{
pbuf_free(rx_desc[i].p[1]);
}
}
while (i != last);
/* Keep index in sync with Rx DMA */
rx_current = (last + 1) & (RX_RING_SIZE - 1);
}
/**
* Collect frame by chaining the corresponding pbufs
*
* @param first Index of first buffer descriptor to collect
* @param last Index of last buffer descriptor to collect
* @param lastlen Length of last buffer in chain
*/
static struct pbuf*
collect_frame(int first, int last, int lastlen)
{
unsigned int i;
struct pbuf *h, *t;
/* Trim last buffer, so complete frame length is correct */
if (rx_desc[last].len[1] != 0)
{
t = rx_desc[last].p[1];
pbuf_realloc(t, lastlen);
h = rx_desc[last].p[0];
pbuf_cat(h, t);
t = h;
}
else
{
t = rx_desc[last].p[0];
pbuf_realloc(t, lastlen);
}
/* Walk the buffers from (last - 1) to first */
i = last;
while (i != first)
{
i = (i - 1) & (RX_RING_SIZE - 1);
h = rx_desc[i].p[1];
pbuf_cat(h, t);
t = h;
h = rx_desc[i].p[0];
pbuf_cat(h, t);
t = h;
}
/* Keep index in sync with Rx DMA */
rx_current = (last + 1) & (RX_RING_SIZE - 1);
return t;
}
/**
* Zero-copy reception. Collect DMA'ed data and return frame as pbuf chain.
*
* @return a pbuf filled with the received packet (including MAC header)
* NULL if no received frames
*/
static struct pbuf*
low_level_input(void)
{
int i, n, len0, len, lastlen, status;
i = rx_current;
len = 0;
n = 0;
/* Scan the Rx buffer rescriptor ring */
for (;;)
{
if (i == (rx_current + rx_count))
{
/* Descriptor uninitialized, quit */
return 0;
}
status = rx_desc[i].status;
if (status & (1u << 31))
{
/* Buffer still owned by DMA, quit */
rx_more = false;
return 0;
}
/* Buffer contains frame data, continue */
n++;
len0 = rx_desc[i].len[0] & 0x1FFF;
if (status & (1 << 8))
{
/* Last buffer in frame, finalize */
break;
}
len += len0;
len += rx_desc[i].len[1];
i = (i + 1) & (RX_RING_SIZE - 1);
/* Move on to next buffer in frame */
}
/* Mark processed descriptors as uninitialized */
rx_count -= n;
if ((status & 0x4000F89F) != 0) /* error bits */
{
discard_frame(rx_current, i);
return 0;
}
lastlen = ((status >> 16) & 0x3FFF) - len;
if (lastlen <= len0)
{
if (rx_desc[i].len[1] != 0)
{
pbuf_free(rx_desc[i].p[1]);
rx_desc[i].len[1] = 0;
}
}
else
{
lastlen -= len0;
}
return collect_frame(rx_current, i, lastlen);
}
err_t
stm32eth_init(struct netif *netif)
{
assert_static(IS_PWR_OF_TWO(RX_RING_SIZE));
assert_static(IS_PWR_OF_TWO(TX_RING_SIZE));
mynetif = netif;
netif->name[0] = 'e';
netif->name[1] = 'n';
netif->hwaddr_len = ETHARP_HWADDR_LEN;
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
/* enable clocking of MAC core, ports A, B, C */
RCC_AHB1ENR |= 0x1E000007;
rx_setup();
GPIOA_AFRL |= 0xB000BBBB;
GPIOB_AFRL |= 0x000000BB;
GPIOB_AFRH |= 0x00BBBB0B;
GPIOC_AFRL |= 0x00BBBBB0;
GPIOA_MODER |= 0x000080AA;
GPIOB_MODER |= 0x0AA2000A;
GPIOC_MODER |= 0x00000AA8;
timer_delay(TIMER_TPS / 1000);
ETH_MACA0LR = *(uint32_t*)netif->hwaddr;
ETH_MACA0HR = *(uint16_t*)(netif->hwaddr + 4);
ETH_DMATDLAR = (uint32_t)&tx_desc;
ETH_DMARDLAR = (uint32_t)&rx_desc;
ETH_MACCR = (1 << 16) /* disable carrier sense */
| (1 << 3) /* enable transmitter */
| (1 << 2);/* enable receiver */
ETH_DMABMR |= (2 << 2); /* descriptor skip length = 2 */
ETH_DMAOMR = (1 << 21) /* transmit store and forward */
| (1 << 13) /* start transmission */
| (1 << 1);/* start reception */
phy_init();
smi_start_read(0x1F);
return ERR_OK;
}
void
stm32eth_poll(void)
{
static unsigned int prev;
unsigned int now;
now = timer_get();
if (now - prev > TIMER_TPS / 50)
{
link = !!(smi_read_data() & (1 << 12));
smi_start_read(0x1F);
prev = now;
}
if (REGBIT(ETH_DMASR, 6))
{
/* RS bit set: complete frame received */
REGBIT(ETH_DMASR, 6) = 1; /* clear RS bit */
rx_more = true;
}
if (rx_more)
{
struct pbuf *p;
p = low_level_input();
if (p)
{
ethernet_input(p, mynetif);
}
rx_setup();
ETH_DMARPDR = 0; /* start RX descriptor polling */
}
}
bool
stm32eth_link(void)
{
return link;
}
bool
stm32eth_act(void)
{
bool ret;
ret = act;
act = false;
return ret;
}