if_rhine.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,320 行 · 第 1/3 页
C
1,320 行
// This MII read forces the MIISR to get updated
(void) rhine_read_MII(cpd, cpd->phys_id, MII_BMSR);
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIISR, stat);
if (stat & RHINE_MIISR_LNKFL) {
#if DEBUG & 1
diag_printf("*** Link failure\n");
#endif
return false; // Link not connected
}
return (cpd->txbusy == 0);
}
//
// This routine is called to send data to the hardware.
static void
rhine_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
int i, len, plen, ring_entry;
cyg_uint8* sdata = NULL;
cyg_uint8 *d, *buf, *txd;
cyg_uint16 status;
cyg_uint8 cr0;
DEBUG_FUNCTION();
INCR_STAT( tx_count );
// Worry about the engine stopping.
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, status);
if ( 0 == (RHINE_CR0_STRT & status) ) {
#if DEBUG & 1
diag_printf("%s: ENGINE RESTART: status %04x\n", __FUNCTION__, status);
#endif
status &= ~RHINE_CR0_STOP;
status |= RHINE_CR0_STRT;
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, status);
}
cpd->txbusy = 1;
cpd->txkey = key;
// Find packet length
plen = 0;
for (i = 0; i < sg_len; i++)
plen += sg_list[i].len;
CYG_ASSERT( plen == total_len, "sg data length mismatch" );
// Get next TX descriptor
ring_entry = cpd->tx_ring_free;
do {
if (cpd->tx_ring_owned == cpd->tx_ring_cnt) {
// Is this a dead end? Probably is.
#if DEBUG & 1
diag_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );
#endif
continue;
}
cpd->tx_ring_free++;
cpd->tx_ring_owned++;
if (cpd->tx_ring_free == cpd->tx_ring_cnt)
cpd->tx_ring_free = 0;
} while (0);
txd = cpd->tx_ring + ring_entry*RHINE_TD_SIZE;
buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
CYG_ASSERT(0 == (_SU32(txd, RHINE_TDES0) & RHINE_TDES0_OWN),
"TX descriptor not free");
#if DEBUG & 4
diag_printf("##Tx descriptor index %d TDES %08x buffer %08x\n",
ring_entry, txd, buf);
#endif
// Put data into buffer
d = buf;
for (i = 0; i < sg_len; i++) {
sdata = (cyg_uint8 *)sg_list[i].buf;
len = sg_list[i].len;
CYG_ASSERT( sdata, "No sg data pointer here" );
while(len--)
*d++ = *sdata++;
}
CYG_ASSERT( sdata, "No sg data pointer outside" );
// Annoyingly the chip doesn't pad to minimal packet size, so do
// that by steam
if (plen < 60) {
plen = 60;
#if DEBUG & 4
diag_printf("Padded %d bytes packet to 60 bytes\n", plen);
#endif
}
CYG_ASSERT( (plen & RHINE_TDES1_TLNG_mask) == plen, "packet too long");
CYG_ASSERT( (plen & RHINE_TDES1_TLNG_mask) >= 60, "packet too short");
_SU32(txd, RHINE_TDES1) &= ~RHINE_TDES1_TLNG_mask;
_SU32(txd, RHINE_TDES1) |= plen;
_SU32(txd, RHINE_TDES0) = RHINE_TDES0_OWN;
#if DEBUG & 1 // FIXME
diag_printf("Before TX: Desc (@0x%08lx) %08x %08x %08x %08x\n Next (@0x%08lx) %08x %08x %08x %08x\n",
(unsigned long) txd,
_SU32(txd, RHINE_TDES0), _SU32(txd, RHINE_TDES1),
_SU32(txd, RHINE_TDES2), _SU32(txd, RHINE_TDES3),
( (unsigned long)txd)+0x10,
_SU32(txd, (0x10+RHINE_TDES0)), _SU32(txd, (0x10+RHINE_TDES1)),
_SU32(txd,(0x10+ RHINE_TDES2)), _SU32(txd,(0x10+ RHINE_TDES3)));
#endif
// Ack TX empty int
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PTX);
// Set transmit demand
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, cr0);
cr0 |= RHINE_CR0_TDMD;
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, cr0);
#if DEBUG & 1
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, status);
diag_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, status);
#endif
}
static void
rhine_TxEvent(struct eth_drv_sc *sc, int stat)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
int success = 1;
cyg_uint8 *txd;
cyg_uint8 status;
DEBUG_FUNCTION();
INCR_STAT( tx_complete );
txd = cpd->tx_ring + cpd->tx_ring_alloc*RHINE_TD_SIZE;
#if DEBUG & 4
diag_printf("##Tx packet %d freed %08x %08x!\n", cpd->tx_ring_alloc, txd, _SU32(txd, RHINE_TDES0) );
#endif
if ((_SU32(txd, RHINE_TDES0) & RHINE_TDES0_OWN)) {
#if DEBUG & 1
diag_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__);
#endif
// first dirty ring entry not freed - wtf?
}
cpd->tx_ring_alloc++;
if (cpd->tx_ring_alloc == cpd->tx_ring_cnt)
cpd->tx_ring_alloc = 0;
cpd->tx_ring_owned--;
#ifdef KEEP_STATISTICS
{
cyg_uint32 reg = _SU32(txd, RHINE_TDES0);
int collisions;
// Covering each bit in turn...
if ( reg & RHINE_TDES0_TXOK ) INCR_STAT( tx_good );
if ( reg & RHINE_TDES0_CRS ) INCR_STAT( tx_carrier_loss );
if ( reg & RHINE_TDES0_OWC ) INCR_STAT( tx_late_collisions );
if ( reg & RHINE_TDES0_ABT ) INCR_STAT( tx_max_collisions );
if ( reg & RHINE_TDES0_DFR ) INCR_STAT( tx_deferred );
collisions = ((reg & RHINE_TDES0_NCR_mask) >> RHINE_TDES0_NCR_shift);
if (1 == collisions)
INCR_STAT( tx_single_collisions );
else if (1 < collisions)
INCR_STAT( tx_mult_collisions );
cpd->stats.tx_total_collisions =
cpd->stats.tx_late_collisions +
cpd->stats.tx_max_collisions +
cpd->stats.tx_mult_collisions +
cpd->stats.tx_single_collisions;
}
#endif // KEEP_STATISTICS
// We do not really care about Tx failure. Ethernet is not a reliable
// medium. But we do care about the TX engine stopping.
HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, status);
if ( 0 == (RHINE_CR0_STRT & status) ) {
#if DEBUG & 1
diag_printf("%s: ENGINE RESTART: status %04x\n", __FUNCTION__, status);
#endif
status &= ~RHINE_CR0_STOP;
status |= RHINE_CR0_STRT;
HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, status);
success = 0; // And treat this as an error...
}
if ( cpd->txbusy ) {
cpd->txbusy = 0;
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success);
}
// Ack TX interrupt set
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PTX);
}
//
// This function is called when a packet has been received. Its job is
// to prepare to unload the packet from the hardware. Once the length of
// the packet is known, the upper layer of the driver can be told. When
// the upper layer is ready to unload the packet, the internal function
// 'rhine_recv' will be called to actually fetch it from the hardware.
//
static void
rhine_RxEvent(struct eth_drv_sc *sc)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
cyg_uint8 *rxd;
cyg_uint32 rstat;
cyg_uint16 ints, len, mask;
DEBUG_FUNCTION();
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, ints);
#if DEBUG & 1
diag_printf("RxEvent - CSR: 0x%04x\n", ints);
#endif
if ( 0 == (RHINE_ISR_PRX & ints) )
// Then there's no RX event pending
return;
// Mask interrupt
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);
mask &= ~RHINE_IMR_PRX;
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_IMR, mask);
while (1) {
// Get state of next (supposedly) full ring entry
cpd->rxpacket = cpd->rx_ring_next;
rxd = cpd->rx_ring + cpd->rxpacket*RHINE_RD_SIZE;
rstat = _SU32(rxd, RHINE_RDES0);
// Keep going until we hit an entry that is owned by the
// controller.
if (rstat & RHINE_RDES0_OWN) {
#ifdef CYGDBG_USE_ASSERTS
// Sanity check of queue
int i;
for (i = 0; i < cpd->rx_ring_cnt; i++) {
rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
rstat = _SU32(rxd, RHINE_RDES0);
if (!(rstat & RHINE_RDES0_OWN)) {
int i;
cyg_uint32 rstat;
cyg_uint8* rxd;
diag_printf("####Rx %s Inconsistent RX state - next was %d\n",
__FUNCTION__, cpd->rx_ring_next);
for (i = 0; i < cpd->rx_ring_cnt; i++) {
rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
rstat = _SU32(rxd, RHINE_RDES0);
diag_printf("#### %02d: 0x%08x\n", i, rstat);
}
}
break;
}
#endif
break;
}
#if DEBUG & 4
diag_printf("##Rx packet %d RDES %08x stat %08x\n",
cpd->rxpacket, rxd, rstat);
#endif
// Increment counts
INCR_STAT( rx_count );
cpd->rx_ring_next++;
if (cpd->rx_ring_next == cpd->rx_ring_cnt) cpd->rx_ring_next = 0;
len = (rstat & RHINE_RDES0_FLNG_mask) >> RHINE_RDES0_FLNG_shift;
#ifdef KEEP_STATISTICS
if ( rstat & RHINE_RDES0_CRC ) INCR_STAT( rx_crc_errors );
if ( rstat & RHINE_RDES0_FAE ) INCR_STAT( rx_align_errors );
if ( rstat & RHINE_RDES0_LONG ) INCR_STAT( rx_too_long_frames );
#endif // KEEP_STATISTICS
if (RHINE_RDES0_RXOK & rstat) {
// It's OK
INCR_STAT( rx_good );
#if DEBUG & 1
diag_printf("RxEvent good rx - stat: 0x%08x, len: 0x%04x\n", rstat, len);
{
unsigned char *buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;
int i;
diag_printf("RDES: %08x %08x %08x %08x\n",
_SU32(rxd, RHINE_RDES0), _SU32(rxd, RHINE_RDES1),
_SU32(rxd, RHINE_RDES2), _SU32(rxd, RHINE_RDES3));
diag_printf("Packet data at %p\n", buf);
for (i=0;i<len;i++) diag_printf("%02x ", buf[i]);
diag_printf("\n");
}
#endif
// Check for bogusly short packets; can happen in promisc
// mode: Asserted against and checked by upper layer
// driver.
#ifdef CYGPKG_NET
if ( len > sizeof( struct ether_header ) )
// then it is acceptable; offer the data to the network stack
#endif
(sc->funs->eth_drv->recv)(sc, len);
} else {
// Not OK for one reason or another...
#if DEBUG & 1
diag_printf("RxEvent - No RX bit: stat: 0x%08x, len: 0x%04x\n",
rstat, len);
#endif
}
// Free packet (clear all status flags, and set OWN)
_SU32(rxd, RHINE_RDES0) = RHINE_RDES0_OWN;
}
// Ack RX int
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PRX);
// And reenable the interrupt
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);
mask |= RHINE_IMR_PRX;
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_IMR, mask);
}
//
// This function is called as a result of the "eth_drv_recv()" call above.
// Its job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet. Note that the buffers
// may come in pieces, using a scatter-gather list. This allows for more
// efficient processing in the upper layers of the stack.
//
static void
rhine_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
int i, mlen=0, plen;
cyg_uint8 *data, *rxd, *buf;
DEBUG_FUNCTION();
rxd = cpd->rx_ring + cpd->rxpacket*RHINE_RD_SIZE;
buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;
INCR_STAT( rx_deliver );
plen = (_SU32(rxd, RHINE_RDES0) & RHINE_RDES0_FLNG_mask) >> RHINE_RDES0_FLNG_shift;
for (i = 0; i < sg_len; i++) {
data = (cyg_uint8*)sg_list[i].buf;
mlen = sg_list[i].len;
#if DEBUG & 1
diag_printf("%s : mlen %x, plen %x\n", __FUNCTION__, mlen, plen);
#endif
if (data) {
while (mlen > 0) {
*data++ = *buf++;
mlen--;
plen--;
}
}
}
}
static void
rhine_poll(struct eth_drv_sc *sc)
{
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
cyg_uint16 event, mask;
static volatile bool locked = false;
// DEBUG_FUNCTION();
while (1) {
// Get the (unmasked) requests
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, event);
HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);
event &= mask;
if (0 == event)
break;
if (event & RHINE_ISR_PRX) {
rhine_RxEvent(sc);
}
else if (event & RHINE_ISR_PTX) {
rhine_TxEvent(sc, event);
}
else if (event & RHINE_ISR_RU) {
#if DEBUG & 1
int i;
cyg_uint32 rstat;
cyg_uint8* rxd;
struct rhine_priv_data *cpd =
(struct rhine_priv_data *)sc->driver_private;
diag_printf("%s: Ran out of RX buffers (%04x)\n", __FUNCTION__, event);
for (i = 0; i < cpd->rx_ring_cnt; i++) {
rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
rstat = _SU32(rxd, RHINE_RDES0);
diag_printf(" %02d: 0x%08x\n", i, rstat);
}
#endif
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_RU);
}
else {
#if DEBUG & 1
diag_printf("%s: Unknown interrupt: 0x%04x\n", __FUNCTION__, event);
#endif
// Clear unhandled interrupts and hope for the best
// This should never happen though, since we only enable
// the sources we handle.
HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, event);
}
}
locked = false;
}
// EOF if_rhine.c
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?