if_atlas.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,055 行 · 第 1/2 页
C
1,055 行
unsigned long __base = spd->base;
SAA9730_EVM_IER_SW |= (SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);
SAA9730_EVM_IER |= (SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);
SAA9730_EVM_ISR |= (SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);
}
#ifdef DEBUG
db_printf(" **** Device enabled for I/O and Memory and Bus Master\n");
#endif
} else {
#ifdef DEBUG
db_printf("eth0 not found\n");
#endif
}
saa9730_stop(sc);
spd->active = 0;
initialized = 1;
}
// Fetch hardware address
#if defined(CYGPKG_REDBOOT) && \
defined(CYGSEM_REDBOOT_FLASH_CONFIG) && \
!defined(CYGSEM_MIPS_ATLAS_SET_ESA)
flash_get_config("atlas_esa", enaddr, CONFIG_ESA);
#else
#define CONFIG_ESA 6
CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,
"atlas_esa", enaddr, CONFIG_ESA );
#ifdef DEBUG
db_printf("ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
enaddr[0],enaddr[1],enaddr[2],enaddr[3],enaddr[4],enaddr[5]);
#endif
#endif
saa9730_reset(spd);
// Initialize upper level driver
(sc->funs->eth_drv->init)(sc, enaddr);
return true;
}
static void
saa9730_stop(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
unsigned long __base = spd->base;
// Stop DMA
SAA9730_DMACTL &= ~(SAA9730_DMACTL_ENRX | SAA9730_DMACTL_ENTX);
// Stop tx/rx
SAA9730_TXCTL &= ~SAA9730_TXCTL_ENTX;
SAA9730_RXCTL &= ~SAA9730_RXCTL_ENRX;
// Set DMA and MAC reset bits
SAA9730_DMATST |= SAA9730_DMATST_RESET;
SAA9730_MACCTL |= SAA9730_MACCTL_RESET;
spd->active = 0;
}
static void
__do_start(struct saa9730_priv_data *spd)
{
unsigned long __base = spd->base;
int i;
spd->active = 1;
spd->tx_busy = 0;
for (i = 0; i < SAA9730_BUFFERS; i++)
spd->tx_used[i] = 0;
// for tx, turn on MAC first
SAA9730_TXCTL |= SAA9730_TXCTL_ENTX;
SAA9730_DMACTL |= SAA9730_DMACTL_ENTX;
// for rx, turn on DMA first
SAA9730_DMACTL |= SAA9730_DMACTL_ENRX;
SAA9730_RXCTL |= SAA9730_RXCTL_ENRX;
__select_buffer(spd, spd->next_rx_bindex);
}
//
// This function is called to "start up" the interface. It may be called
// multiple times, even when the hardware is already running. It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
saa9730_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
if (spd->active)
saa9730_stop(sc);
__do_start(spd);
}
//
// This routine is called to perform special "control" opertions
//
static int
saa9730_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length)
{
switch (key) {
case ETH_DRV_SET_MAC_ADDRESS:
return 0;
break;
default:
return 1;
break;
}
}
//
// This routine is called to see if it is possible to send another packet.
// It will return non-zero if a transmit is possible, zero otherwise.
//
static int
saa9730_can_send(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
unsigned long __base = spd->base;
__tx_poll(sc);
if (spd->next_tx_bindex == 0 && (SAA9730_OK2USE & SAA9730_OK2USE_TXA))
return 0;
if (spd->next_tx_bindex == 1 && (SAA9730_OK2USE & SAA9730_OK2USE_TXB))
return 0;
return 1;
}
static int tx_poll_count;
//
// This routine is called to send data to the hardware.
static void
saa9730_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
unsigned long __base = spd->base;
int bindex, pindex;
cyg_uint32 pktlen = total_len;
cyg_uint8 *pktdata, *to_p;
volatile cyg_uint32 *pktstat ;
struct eth_drv_sg *last_sg;
#ifdef DEBUG
db_printf("saa9730_send: %d sg's, %d bytes, KEY %x\n",
sg_len, total_len, key );
#endif
if (!spd->active)
return;
bindex = spd->next_tx_bindex;
pindex = spd->next_tx_pindex;
spd->next_tx_pindex++;
if (spd->next_tx_pindex >= SAA9730_TXPKTS_PER_BUFFER) {
spd->next_tx_pindex = 0;
spd->next_tx_bindex ^= 1;
}
pktstat = spd->tx_buffer[bindex][pindex];
if (bindex == 0 && (SAA9730_OK2USE & SAA9730_OK2USE_TXA))
return;
if (bindex == 1 && (SAA9730_OK2USE & SAA9730_OK2USE_TXB))
return;
spd->tx_key[bindex][pindex] = key;
spd->tx_used[bindex] += 1;
pktdata = (cyg_uint8 *)((unsigned)pktstat + 4);
// Copy from the sglist into the tx buffer
to_p = pktdata;
for (last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++) {
cyg_uint8 *from_p;
int l;
from_p = (cyg_uint8 *)(sg_list->buf);
l = sg_list->len;
if (l > total_len)
l = total_len;
memcpy((unsigned char *)to_p, from_p, l);
to_p += l;
total_len -= l;
if (total_len < 0)
break; // Should exit via sg_last normally
}
// pad to minimum size
if (pktlen < SAA9730_MIN_PACKET_SIZE) {
memset(to_p, 0, SAA9730_MIN_PACKET_SIZE-pktlen);
pktlen = SAA9730_MIN_PACKET_SIZE;
}
// Set transmit status WORD for hardware (LAN-DMA-ENGINE)
*pktstat = CYG_CPU_TO_LE32(TX_READY | pktlen);
// start hardware
if (bindex == 0)
SAA9730_OK2USE |= SAA9730_OK2USE_TXA;
else
SAA9730_OK2USE |= SAA9730_OK2USE_TXB;
if (!spd->tx_busy) {
tx_poll_count = 0;
spd->tx_busy = bindex + 1;
}
}
static void
__check_rxstate(struct saa9730_priv_data *spd)
{
unsigned long __base = spd->base;
cyg_uint32 status, flag, size;
cyg_uint32 *pkt;
int i, j;
#ifdef DEBUG
db_printf("__check_rxstate\n");
#endif
#ifdef CYGPKG_REDBOOT
// Clear SAA9730 LAN interrupt and re-enable interrupts.
SAA9730_EVM_ISR = SAA9730_EVM_LAN_INT;
SAA9730_EVM_IER_SW |= (SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);
#endif
if ((SAA9730_DBGRXS & SAA9730_DBGRXS_RXDII_MASK) == SAA9730_DBGRXS_RXDII_ERROR) {
// re-init driver and controller
#ifdef DEBUG
db_printf("DBGRXS: reset\n");
#endif
saa9730_reset(spd);
__do_start(spd);
return;
}
// Check RX packet status
for (i = 0; i < SAA9730_BUFFERS; i++) {
for (j = 1; j < SAA9730_RXPKTS_PER_BUFFER; j++) {
pkt = spd->rx_buffer[i][j];
status = CYG_LE32_TO_CPU(*pkt);
size = status & RXPACKET_STATUS_SIZE_MASK;
flag = status & RXPACKET_STATUS_FLAG_MASK;
if (flag == RX_INVALID_STAT || size > 1514 || *(pkt - 1)) {
// re-init driver and controller
#ifdef DEBUG
db_printf("rxpkt: reset\n");
#endif
saa9730_reset(spd);
__do_start(spd);
return;
}
}
}
}
static void
__tx_poll(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
int bindex, pindex;
volatile cyg_uint32 *pktstat;
cyg_uint32 status;
if (!spd->tx_busy)
return;
bindex = spd->tx_busy - 1;
pindex = spd->tx_used[bindex] - 1; // watch last pkt in buffer
pktstat = spd->tx_buffer[bindex][pindex];
status = CYG_LE32_TO_CPU(*pktstat);
if ((status & TXPACKET_STATUS_FLAG_MASK) != TX_HWDONE) {
hal_delay_us(1000);
if (++tx_poll_count > 1000) {
// reset
for (pindex = 0; pindex < spd->tx_used[bindex]; pindex++)
(sc->funs->eth_drv->tx_done)(sc, spd->tx_key[bindex][pindex], 1);
bindex ^= 1;
for (pindex = 0; pindex < spd->tx_used[bindex]; pindex++)
(sc->funs->eth_drv->tx_done)(sc, spd->tx_key[bindex][pindex], 1);
saa9730_reset(spd);
__do_start(spd);
}
return;
}
for (pindex = 0; pindex < spd->tx_used[bindex]; pindex++) {
/* Check for error. */
pktstat = spd->tx_buffer[bindex][pindex];
status = CYG_LE32_TO_CPU(*pktstat);
if (status & TXPACKET_STATUS_ERROR) {
if (status & TXPACKET_STATUS_EXDEFER)
db_printf("tx deferred\n");
if (status & TXPACKET_STATUS_LATECOLLERR)
db_printf("tx late collision\n");
if (status & TXPACKET_STATUS_LOSTCARRIER)
db_printf("tx no carrier\n");
if (status & TXPACKET_STATUS_UNDERRUN)
db_printf("tx underrun\n");
if (status & TXPACKET_STATUS_SQERR)
db_printf("tx sq\n");
}
/* free the space */
*pktstat = CYG_CPU_TO_LE32(TX_EMPTY);
(sc->funs->eth_drv->tx_done)(sc, spd->tx_key[bindex][pindex], 1 /* status */);
}
tx_poll_count = 0;
spd->tx_used[bindex] = 0;
bindex ^= 1;
if (spd->tx_used[bindex])
spd->tx_busy = bindex + 1;
else
spd->tx_busy = 0;
}
static void
__rx_poll(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
int bindex, pindex, done;
volatile cyg_uint32 *pkt;
cyg_uint32 status, pktlen;
#ifdef DEBUG
db_printf("__rx_poll\n");
#endif
if (!spd->active)
return;
done = 0;
while (!done) {
// index of next packet buffer
bindex = spd->next_rx_bindex;
// index of next packet within buffer
pindex = spd->next_rx_pindex;
pkt = spd->rx_buffer[bindex][pindex];
// stop now if no more packets
if (((status = CYG_LE32_TO_CPU(*pkt)) & RXPACKET_STATUS_FLAG_MASK) == RX_READY)
break;
#ifdef DEBUG
db_printf("__rx_poll pkt %08x status %08x\n",pkt,status);
#endif
// if this is the first packet in a buffer, switch the SAA9730 to
// use the next buffer for subsequent incoming packets.
if (pindex == 0)
__select_buffer(spd, bindex == 0);
// check for good packet
if (status & RXPACKET_STATUS_GOOD) {
pktlen = status & RXPACKET_STATUS_SIZE_MASK ;
if (pktlen > 0) {
(sc->funs->eth_drv->recv)(sc, pktlen);
// done = 1;
}
}
#ifdef DEBUG
else
db_printf("rx bad: %08x %08x\n",pkt,status);
#endif
/* go to next packet in sequence */
spd->next_rx_pindex++;
if (spd->next_rx_pindex >= SAA9730_RXPKTS_PER_BUFFER) {
spd->next_rx_pindex = 0;
spd->next_rx_bindex++;
if (spd->next_rx_bindex >= SAA9730_BUFFERS)
spd->next_rx_bindex = 0;
}
}
if (((poll_count++) % 100) == 0)
__check_rxstate(spd);
}
static void
saa9730_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
volatile cyg_uint32 *pkt;
cyg_uint32 pktlen, status;
struct eth_drv_sg *last_sg;
if (!spd->active)
return;
pkt = spd->rx_buffer[spd->next_rx_bindex][spd->next_rx_pindex];
status = CYG_LE32_TO_CPU(*pkt);
if (status & RXPACKET_STATUS_GOOD) {
// packet is good
pktlen = status & RXPACKET_STATUS_SIZE_MASK;
if (pktlen > 0) {
int total_len;
cyg_uint8 *from_p;
// check we have memory to copy into; we would be called even if
// caller was out of memory in order to maintain our state.
if (0 == sg_len || 0 == sg_list)
return; // caller was out of mbufs
total_len = pktlen;
from_p = (cyg_uint8 *)((unsigned)pkt + 4);
for (last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++) {
cyg_uint8 *to_p;
int l;
to_p = (cyg_uint8 *)(sg_list->buf);
l = sg_list->len;
if (0 >= l || 0 == to_p)
return; // caller was out of mbufs
if (l > total_len)
l = total_len;
memcpy(to_p, (unsigned char *)from_p, l);
from_p += l;
total_len -= l;
}
}
}
}
static inline void
__do_deliver(struct eth_drv_sc *sc)
{
// First pass any rx data up the stack
__rx_poll(sc);
// Then scan for completed Txen and inform the stack
__tx_poll(sc);
}
static void
saa9730_poll(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
#ifndef CYGPKG_REDBOOT
cyg_drv_interrupt_mask(spd->vector);
#endif
(void)saa9730_isr(spd->vector, (cyg_addrword_t)spd);
__do_deliver(sc);
cyg_drv_interrupt_acknowledge(spd->vector);
#ifndef CYGPKG_REDBOOT
cyg_drv_interrupt_unmask(spd->vector);
#endif
}
// The deliver function (ex-DSR) handles the ethernet [logical] processing
static void
saa9730_deliver(struct eth_drv_sc *sc)
{
struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;
unsigned long __base = spd->base;
if (spd->active)
__do_deliver(sc);
cyg_drv_interrupt_acknowledge(spd->vector);
#ifndef CYGPKG_REDBOOT
// Clear SAA9730 LAN interrupt and re-enable interrupts.
SAA9730_EVM_IER_SW |= (SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);
cyg_drv_interrupt_unmask(spd->vector);
#endif
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?