if_pcnet.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,329 行 · 第 1/3 页
C
1,329 行
// Restore controller state
if (was_active) {
// Unsuspend the device
reg = get_reg(sc, PCNET_CSR_ECI);
reg &= ~PCNET_CSR_ECI_SPND;
put_reg(sc, PCNET_CSR_ECI, reg);
}
return res;
}
//
// 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
pcnet_can_send(struct eth_drv_sc *sc)
{
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
cyg_uint16 stat;
DEBUG_FUNCTION();
stat = get_reg(sc, PCNET_ANR_PHYSTAT);
if ((stat & PCNET_ANR_PHYSTAT_LINK) == 0) {
return 0; // Link not connected
}
return (0 == cpd->txbusy);
}
//
// This routine is called to send data to the hardware.
static void
pcnet_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
int i, len, plen, ring_entry;
cyg_uint8* sdata = NULL;
cyg_uint8 *d, *buf, *txd;
cyg_uint16 ints;
cyg_uint32 b;
DEBUG_FUNCTION();
INCR_STAT( tx_count );
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
db_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*PCNET_TD_SIZE;
buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
CYG_ASSERT(0 == (_SU32(txd, PCNET_TD_PTR) & PCNET_TD_PTR_OWN),
"TX descriptor not free");
#if DEBUG & 4
db_printf("#####Tx descriptor 0x%08x buffer 0x%08x\n",
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" );
#if DEBUG & 1
db_printf("CSCR %04x\n", get_reg(sc, PCNET_CSR_CSCR));
#endif
_SU16(txd, PCNET_TD_LEN) = (-plen);
_SU16(txd, PCNET_TD_MISC) = 0;
HAL_PCI_CPU_TO_BUS(buf, b);
_SU32(txd, PCNET_TD_PTR) = ((b & PCNET_TD_PTR_MASK)
| PCNET_TD_PTR_OWN | PCNET_TD_PTR_STP | PCNET_TD_PTR_ENP);
#if DEBUG & 1
db_printf("Last TX: LEN %04x MISC %04x PTR %08x\n",
_SU16(txd, PCNET_TD_LEN),
_SU16(txd, PCNET_TD_MISC),
_SU32(txd, PCNET_TD_PTR));
#endif
// This delay seems to be necessary on some platforms
// (Malta 5kc for example).
// Why it is needed is not clear, but removing it or
// reducing it cause transmission failures in RedBoot (at least).
CYGACC_CALL_IF_DELAY_US(100);
// Set transmit demand
ints = get_reg(sc, PCNET_CSR_CSCR);
ints &= PCNET_CSR_CSCR_EV_MASK;
ints |= PCNET_CSR_CSCR_TDMD;
put_reg(sc, PCNET_CSR_CSCR, ints);
#if DEBUG & 1
ints = get_reg(sc, PCNET_CSR_CSCR);
db_printf("%s:END: ints at TX: 0x%04x\n", __FUNCTION__, ints);
#endif
// This is another mystery delay like the one above. This one is
// even stranger, since waiting here at the _end_ of the function
// should have no effect.
CYGACC_CALL_IF_DELAY_US(200);
}
static void
pcnet_TxEvent(struct eth_drv_sc *sc, int stat)
{
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
int success = 1;
cyg_uint8 *txd;
cyg_uint16 ints;
cyg_uint32 pkt_stat;
DEBUG_FUNCTION();
if (0 == cpd->tx_ring_owned) {
#if DEBUG & 1
db_printf("%s: got TX completion when no outstanding packets\n", __FUNCTION__);
#endif
return;
}
INCR_STAT( tx_complete );
txd = cpd->tx_ring + cpd->tx_ring_alloc*PCNET_TD_SIZE;
pkt_stat = _SU32(txd, PCNET_TD_PTR);
if (pkt_stat & PCNET_TD_PTR_OWN) {
#if DEBUG & 1
db_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__);
#endif
// first dirty ring entry not freed - wtf?
}
if (pkt_stat & PCNET_TD_PTR_ERR) {
// We had an error. Tell the stack.
success = 0;
#if DEBUG & 1
db_printf("%s: TX failure, retrying...\n", __FUNCTION__);
#endif
}
cpd->tx_ring_alloc++;
if (cpd->tx_ring_alloc == cpd->tx_ring_cnt)
cpd->tx_ring_alloc = 0;
cpd->tx_ring_owned--;
#if FIXME
#ifdef KEEP_STATISTICS
{
cyg_uint16 reg;
reg = get_reg( sc, PCNET_CSR_CSCR );
// Covering each bit in turn...
if ( reg & PCNET_STATUS_TX_UNRN ) INCR_STAT( tx_underrun );
//if ( reg & PCNET_STATUS_LINK_OK ) INCR_STAT( );
//if ( reg & PCNET_STATUS_CTR_ROL ) INCR_STAT( );
//if ( reg & PCNET_STATUS_EXC_DEF ) INCR_STAT( );
if ( reg & PCNET_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss );
if ( reg & PCNET_STATUS_LATCOL ) INCR_STAT( tx_late_collisions );
//if ( reg & PCNET_STATUS_WAKEUP ) INCR_STAT( );
if ( reg & PCNET_STATUS_TX_DEFR ) INCR_STAT( tx_deferred );
//if ( reg & PCNET_STATUS_LTX_BRD ) INCR_STAT( );
if ( reg & PCNET_STATUS_SQET ) INCR_STAT( tx_sqetesterrors );
if ( reg & PCNET_STATUS_16COL ) INCR_STAT( tx_max_collisions );
//if ( reg & PCNET_STATUS_LTX_MULT) INCR_STAT( );
if ( reg & PCNET_STATUS_MUL_COL ) INCR_STAT( tx_mult_collisions );
if ( reg & PCNET_STATUS_SNGL_COL ) INCR_STAT( tx_single_collisions );
if ( reg & PCNET_STATUS_TX_SUC ) INCR_STAT( tx_good );
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;
// We do not need to look in the Counter Register (PCNET_COUNTER)
// because it just mimics the info we already have above.
}
#endif // KEEP_STATISTICS
#endif // FIXME
// Ack the TX int which clears the packet from the TX completion
// queue.
ints = get_reg(sc, PCNET_CSR_CSCR);
ints |= PCNET_CSR_CSCR_TINT;
put_reg(sc, PCNET_CSR_CSCR, ints);
#if DEBUG & 4
db_printf("#####Tx packet freed 0x%08x\n", txd );
#endif
if ( cpd->txbusy ) {
cpd->txbusy = 0;
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success);
}
}
//
// 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
// 'pcnet_recv' will be called to actually fetch it from the hardware.
//
static void
pcnet_RxEvent(struct eth_drv_sc *sc)
{
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
cyg_uint8 *rxd;
cyg_uint32 rstat;
cyg_uint16 ints, len;
DEBUG_FUNCTION();
ints = get_reg(sc, PCNET_CSR_CSCR);
#if DEBUG & 1
db_printf("RxEvent - CSR: 0x%04x\n", ints);
#endif
while (1) {
// Get state of next (supposedly) full ring entry
cpd->rxpacket = cpd->rx_ring_next;
rxd = cpd->rx_ring + cpd->rxpacket*PCNET_RD_SIZE;
rstat = _SU32(rxd, PCNET_RD_PTR);
// Keep going until we hit an entry that is owned by the
// controller.
if (rstat & PCNET_RD_PTR_OWN) {
#if DEBUG & 1
int i;
for (i = 0; i < cpd->rx_ring_cnt; i++) {
rxd = cpd->rx_ring + i*PCNET_RD_SIZE;
rstat = _SU32(rxd, PCNET_RD_PTR);
if (!(rstat & PCNET_RD_PTR_OWN)) {
int i;
cyg_uint32 rstat;
cyg_uint16 mlen, blen;
cyg_uint8* rxd;
db_printf("%s: Inconsistent RX state\n", __FUNCTION__);
for (i = 0; i < cpd->rx_ring_cnt; i++) {
rxd = cpd->rx_ring + i*PCNET_RD_SIZE;
rstat = _SU32(rxd, PCNET_RD_PTR);
blen = _SU16(rxd, PCNET_RD_BLEN);
mlen = _SU16(rxd, PCNET_RD_MLEN);
db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen);
}
}
}
#endif
break;
}
#if DEBUG & 4
db_printf("#####Rx packet at index %d\n", cpd->rxpacket);
#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 = _SU16(rxd, PCNET_RD_MLEN);
#ifdef KEEP_STATISTICS
//if ( rstat & PCNET_RD_PTR_FRAM ) INCR_STAT( rx_frame_errors );
//if ( rstat & PCNET_RD_PTR_OFLO ) INCR_STAT( );
if ( rstat & PCNET_RD_PTR_CRC ) INCR_STAT( rx_crc_errors );
//if ( rstat & PCNET_RD_PTR_BUFF ) INCR_STAT( );
#endif // KEEP_STATISTICS
if (0 == (rstat & PCNET_RD_PTR_ERR)) {
// It's OK
INCR_STAT( rx_good );
#if DEBUG & 1
db_printf("RxEvent good rx - stat: 0x%08x, len: 0x%04x\n", rstat, len);
#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
db_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, PCNET_RD_PTR) &= PCNET_RD_PTR_MASK;
_SU32(rxd, PCNET_RD_PTR) |= PCNET_RD_PTR_OWN;
}
// Ack RX interrupt set
ints = get_reg(sc, PCNET_CSR_CSCR);
ints &= PCNET_CSR_CSCR_EV_MASK;
ints |= PCNET_CSR_CSCR_RINT;
put_reg(sc, PCNET_CSR_CSCR, ints);
}
//
// 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
pcnet_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
int i, mlen=0, plen;
cyg_uint8 *data, *rxd, *buf;
DEBUG_FUNCTION();
rxd = cpd->rx_ring + cpd->rxpacket*PCNET_RD_SIZE;
buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;
INCR_STAT( rx_deliver );
plen = _SU16(rxd, PCNET_RD_MLEN);
for (i = 0; i < sg_len; i++) {
data = (cyg_uint8*)sg_list[i].buf;
mlen = sg_list[i].len;
#if DEBUG & 1
db_printf("%s : mlen %x, plen %x\n", __FUNCTION__, mlen, plen);
#endif
if (data) {
while (mlen > 0) {
*data++ = *buf++;
mlen--;
plen--;
}
}
}
}
static void
pcnet_poll(struct eth_drv_sc *sc)
{
cyg_uint16 event;
// DEBUG_FUNCTION();
while (1) {
// Get the (unmasked) requests
event = get_reg(sc, PCNET_CSR_CSCR);
if (!((PCNET_CSR_CSCR_ERR|PCNET_CSR_CSCR_INTR) & event))
break;
if (event & PCNET_CSR_CSCR_RINT) {
pcnet_RxEvent(sc);
}
else if (event & PCNET_CSR_CSCR_TINT) {
pcnet_TxEvent(sc, event);
}
else if (event & PCNET_CSR_CSCR_MISS) {
#if DEBUG & 1
int i;
cyg_uint32 rstat;
cyg_uint16 mlen, blen;
cyg_uint8* rxd;
struct pcnet_priv_data *cpd =
(struct pcnet_priv_data *)sc->driver_private;
db_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*PCNET_TD_SIZE;
rstat = _SU32(rxd, PCNET_RD_PTR);
blen = _SU16(rxd, PCNET_RD_BLEN);
mlen = _SU16(rxd, PCNET_RD_MLEN);
db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen);
}
#endif
event &= PCNET_CSR_CSCR_EV_MASK;
event |= PCNET_CSR_CSCR_MISS;
put_reg(sc, PCNET_CSR_CSCR, event);
}
else {
#if DEBUG & 1
db_printf("%s: Unknown interrupt: 0x%04x\n", __FUNCTION__, event);
#endif
put_reg(sc, PCNET_CSR_CSCR, event);
}
}
}
// EOF if_pcnet.c
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?