📄 ne2000.c
字号:
int isr;
int ret = 0;
static int intr_cnt=0;
ENTRY("ne2000_rxtst");
ne->in_intr = 1;
/* Change to page 0 and read the intr status reg. */
OUTB(e8390_base + E8390_CMD, E8390_NODMA+E8390_PAGE0);
while ((isr = INPB(e8390_base + EN0_ISR)) & ENISR_ALL) {
if (isr & ENISR_OVER) {
ei_rx_overrun(ne);
}
if (isr & (ENISR_RX | ENISR_RX_ERR)) {
/*
* Bug alert! Reset ENISR_OVER to avoid
* spurious overruns!
*/
ei_receive(ne);
#if 0
OUTB(e8390_base+EN0_ISR, ENISR_RX+ENISR_RX_ERR+ENISR_OVER);
#else
OUTB(e8390_base+EN0_ISR, ENISR_RX+ENISR_RX_ERR);
#endif
ret = 1;
}
if (isr & (ENISR_TX | ENISR_TX_ERR)) {
/*
* ei_tx_intr does the following:
* 1) Ack TX intr;
* 2) start next TX;
* 3) Update TX statistics
*/
ei_tx_intr(ne);
}
/*
* Handle benign interrupt sources last ...
*/
#if 0
/* ACK Meaningless DMA complete. */
if (isr & ENISR_RDC) {
OUTB(e8390_base + EN0_ISR, ENISR_RDC);
}
#endif
/* Counter Overflow: msb of a network tally counter has been set */
if (isr & ENISR_COUNTERS) {
OUTB(e8390_base + EN0_ISR, ENISR_COUNTERS);
update_stats(ne);
}
}
if ((++intr_cnt & 0x0fff) == 0)
update_stats(ne);
ne->in_intr = 0;
INTERRUPT_END(dev, INT_PKT_RX);
EXIT2("ne2000_rxtst", ret);
return ret;
}
/*************************************************************************
**
** ne2000_int_info_get
**
** This function returns interrupt information that tells the
** caller which interrupts are enabled for a particular device.
**
** Inputs:
** dev the ether_1 device descriptor
** intrpt which interrupt actually requested
** Outputs:
** intrpt flags field is updated
**
** Return codes:
** LOGIO_STATUS_OK The call succeeded
**
*/
logio_status_t
ne2000_int_info_get(ether_1_dev_desc_t *dev, logio_int_entry_t *intrpt)
{
struct device *ne = (struct device *)dev->config.specific;
ENTRY("ne2000_int_info_get");
intrpt->flags = ne->shadow_int_flags;
EXIT("ne2000_int_info_get");
return( LOGIO_STATUS_OK );
}
/*************************************************************************
** ne2000_int_info_set
**
** This function controls a specific event/id pair by enabling or
** disabling the specific event requested.
**
** Inputs:
** dev the ether_1 device descriptor
** intrpt which event to set
** Outputs:
** intrpt flags field is updated to old value
**
** Return codes:
** LOGIO_STATUS_OK The call succeeded
**
*/
logio_status_t
ne2000_int_info_set(ether_1_dev_desc_t *dev, logio_int_entry_t *intrpt)
{
int new_flags = intrpt->flags;
struct device *ne = (struct device *)dev->config.specific;
int e8390_base = ne->base_addr;
ENTRY("ne2000_int_info_set");
intrpt->flags = ne->shadow_int_flags;
if (new_flags == LOGIO_INT_ENABLED) {
BOARD_SPEC_INT_ENABLE(dev, intrpt);
OUTB(e8390_base + EN0_IMR, ENISR_ALL);
} else if (new_flags == LOGIO_INT_DISABLED) {
BOARD_SPEC_INT_DISABLE(dev, intrpt);
OUTB(e8390_base + EN0_IMR, 0x00);
}
ne->shadow_int_flags = new_flags;
EXIT("ne2000_int_info_set");
return( LOGIO_STATUS_OK );
}
/*****************************************************************/
/* Transmit logic */
/*****************************************************************/
vbsp_return_t
ei_start_xmit(struct device *dev)
{
struct ei_device *ei_local = (struct ei_device *) dev->priv;
vbuf_t *list, *bptr;
int len, current_offset;
int output_page = ei_local->tx_start_page;
IFDEBUG( static char funcname[] = "ei_start_xmit"; )
ENTRY(funcname);
list = dev->tx_head;
if (!list)
return VBSP_SUCCESS;
len = vbuf_get_msgcnt(list);
bptr = list;
while (bptr = vbuf_get_nextbuf(bptr))
len += vbuf_get_msgcnt(bptr);
if (ei_local->pingpong) {
if (ei_local->tx1 == 0) {
ei_local->tx1 = len;
#if EI_DEBUG > 0
if (ei_local->tx2 > 0)
printf("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx2, ei_local->lasttx,
ei_local->txing);
#endif
} else if (ei_local->tx2 == 0) {
output_page += 6;
ei_local->tx2 = len;
#if EI_DEBUG > 0
if (ei_local->tx1 > 0)
printf("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
dev->name, ei_local->tx1, ei_local->lasttx,
ei_local->txing);
#endif
} else { /* We should never get here. */
#if EI_DEBUG > 0
printf("%s: No packet buffer space for ping-pong use.\n",
dev->name);
#endif
dev->tbusy = 1;
EXIT2(funcname, VBSP_NO_DEVICE_BUFS);
return VBSP_NO_DEVICE_BUFS;
}
} else { /* No pingpong, just a single Tx buffer. */
if (dev->tbusy) {
EXIT2(funcname, VBSP_NO_DEVICE_BUFS);
return VBSP_NO_DEVICE_BUFS;
}
}
/*
* Copy the packet to the board's RAM.
*/
current_offset = output_page << 8;
ne_block_output(dev, vbuf_get_msgcnt(list), vbuf_get_dataptr(list),
current_offset);
bptr = list;
current_offset += vbuf_get_msgcnt(bptr);
while ((bptr = vbuf_get_nextbuf(bptr)) != (vbuf_t *)0) {
ne_block_output(dev, vbuf_get_msgcnt(bptr), vbuf_get_dataptr(bptr),
current_offset);
current_offset += vbuf_get_msgcnt(bptr);
}
#if EI_DEBUG > 1 /* Read back and display the data we just sent to the board */
{
char buf[ 300 ];
ne_block_input(dev, 256, buf, ei_local->tx_start_page << 8);
printf("ei_start_xmit: read the data back before the xmit:\n");
dump(buf, 80);
}
#endif
#ifdef RWS /* debug */
{
static int n;
if ((++n & 0xf) == 0)
printf(">");
}
#endif
/*
* Start the transmission.
*/
if (ei_local->pingpong) {
if (! ei_local->txing) {
NS8390_trigger_send(dev, len, output_page);
if (output_page == ei_local->tx_start_page)
ei_local->tx1 = -1, ei_local->lasttx = -1;
else
ei_local->tx2 = -1, ei_local->lasttx = -2;
ei_local->txing = 1;
} else
ei_local->txqueue++;
dev->tbusy = (ei_local->tx1 && ei_local->tx2);
} else { /* No pingpong, just a single Tx buffer. */
NS8390_trigger_send(dev, len, ei_local->tx_start_page);
dev->tbusy = 1;
}
/*
* Since we've copied the packet to the board's memory,
* we're finished with this vbuf -- free it.
* First, we have to remove it from the TX queue.
*/
dev->tx_head = vbuf_get_nextpkt(list);
vbuf_set_nextpkt(list, (vbuf_t *)0);
logio_release_list(/*dummy*/0, list);
#if 0
(*list->free)(list); /* release this buffer */
#endif
EXIT2(funcname, VBSP_SUCCESS);
return VBSP_SUCCESS;
}
/* We have finished a transmit: check for errors and then trigger the next
packet to be sent. */
void
ei_tx_intr(struct device *dev)
{
int e8390_base = dev->base_addr;
int status = INPB(e8390_base + EN0_TSR);
struct ei_device *ei_local = (struct ei_device *) dev->priv;
ENTRY("ei_tx_intr");
/* ++COUNTDOWN; */
OUTB(e8390_base + EN0_ISR, (ENISR_TX | ENISR_TX_ERR));
if (ei_local->pingpong) {
ei_local->txqueue--;
if (ei_local->tx1 < 0) {
if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
printf("%s: bogus last_tx_buffer %d, tx1=%d.\n",
ei_local->name, ei_local->lasttx, ei_local->tx1);
ei_local->tx1 = 0;
dev->tbusy = 0;
if (ei_local->tx2 > 0) {
NS8390_trigger_send(dev, ei_local->tx2,
ei_local->tx_start_page + 6);
ei_local->txing = 1;
ei_local->tx2 = -1,
ei_local->lasttx = 2;
} else {
ei_local->lasttx = 20, ei_local->txing = 0;
if (dev->tx_head)
ei_start_xmit(dev); /* keep TX busy */
}
} else if (ei_local->tx2 < 0) {
if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
printf("%s: bogus last_tx_buffer %d, tx2=%d.\n",
ei_local->name, ei_local->lasttx, ei_local->tx2);
ei_local->tx2 = 0;
dev->tbusy = 0;
if (ei_local->tx1 > 0) {
NS8390_trigger_send(dev, ei_local->tx1,
ei_local->tx_start_page);
ei_local->txing = 1;
ei_local->tx1 = -1;
ei_local->lasttx = 1;
} else {
ei_local->lasttx = 10, ei_local->txing = 0;
if (dev->tx_head)
ei_start_xmit(dev); /* keep TX busy */
}
} else
printf("%s: unexpected TX-done interrupt, lasttx=%d.\n",
dev->name, ei_local->lasttx);
} else {
ei_local->txing = 0;
dev->tbusy = 0;
if (dev->tx_head)
ei_start_xmit(dev); /* keep TX busy */
}
/* Minimize Tx latency: update the statistics after we restart TXing. */
if (status & ENTSR_COL)
ei_local->stat.collisions++;
if (status & ENTSR_PTX)
ei_local->stat.tx_packets++;
else {
ei_local->stat.tx_errors++;
if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
if (status & ENTSR_FU) ei_local->stat.tx_fifo_errors++;
if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
}
EXIT("ei_tx_intr");
}
/*****************************************************************/
/* Receive logic */
/*****************************************************************/
/* We have a good packet(s), get them out of the buffers. */
vbsp_return_t
ei_receive(struct device *dev)
{
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int rxing_page, this_frame, next_frame, current_offset;
struct e8390_pkt_hdr rx_frame;
int num_rx_pages = ei_local->stop_page - ei_local->rx_start_page;
int pkt_len;
vbsp_return_t rv = VBSP_SUCCESS;
vbuf_t *new_bufs, *bptr;
int dummy = 0;
#ifdef RWS /* debug */
static int most;
#endif
for (;;) {
/* Get the rx page (incoming packet pointer). */
OUTB(e8390_base + E8390_CMD, E8390_NODMA+E8390_PAGE1);
rxing_page = INPB(e8390_base + EN1_CURPAG);
OUTB(e8390_base + E8390_CMD, E8390_NODMA+E8390_PAGE0);
/* Remove one frame from the ring. Boundary is alway a page behind. */
this_frame = INPB(e8390_base + EN0_BOUNDARY) + 1;
if (this_frame >= ei_local->stop_page)
this_frame = ei_local->rx_start_page;
#if EI_DEBUG > 0
if (this_frame != ei_local->current_page)
printf("%s: mismatched read page pointers %2x vs %2x.\n",
dev->name, this_frame, ei_local->current_page);
#endif
if (this_frame == rxing_page) { /* Read all the frames? */
break;
}
current_offset = this_frame << 8;
ne_block_input(dev, sizeof(rx_frame), (char *)&rx_frame,
current_offset);
current_offset += sizeof(rx_frame);
pkt_len = rx_frame.count - 4; /* Remove CRC size */
next_frame = this_frame + ((pkt_len+4+4+255)>>8);
if (next_frame >= ei_local->stop_page)
next_frame -= num_rx_pages;
/* Check for bogosity warned by 3c503 book: the status byte is never
written. This happened a lot during testing! This code should be
cleaned up someday. */
if (rx_frame.next != next_frame) {
#if 0
&& rx_frame.next != next_frame + 1
&& rx_frame.next != next_frame - num_rx_pages
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
#endif
#if EI_DEBUG > 0
printf("%s: warning rx_frame.next=%#x next_frame=%#x\n",
rx_frame.next, next_frame);
printf("\t\tpkt_len=%#x num_rx_pages=%#x\n",
pkt_len, num_rx_pages);
#endif
ei_local->current_page = rxing_page;
if (rxing_page > ei_local->rx_start_page)
OUTB(e8390_base+EN0_BOUNDARY, rxing_page-1);
else
OUTB(e8390_base+EN0_BOUNDARY, ei_local->stop_page-1);
ei_local->stat.rx_errors++;
continue;
}
if (pkt_len < 60 || pkt_len > 1518) {
#if EI_DEBUG > 0
printf("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
dev->name, rx_frame.count, rx_frame.status,
rx_frame.next);
#endif
ei_local->stat.rx_errors++;
} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
/*
* We should need only 1 vbuf, but we also handle cases
* where vbufs are smaller than our packet (not likely
* since default vbuf data size is 1600), requiring
* multiple vbufs.
*/
int num_bufs = 1;
#if VBUFMAX < 1600
if (pkt_len > VBUFMAX)
num_bufs = (pkt_len + VBUFMAX - 1) / VBUFMAX;
#endif /* VBUFMAX < 1600 */
dummy++; /* count # of good packets */
if (logio_get_free_list(dummy, num_bufs, &new_bufs) == 0) {
bptr = new_bufs;
#if VBUFMAX < 1600
while (pkt_len > VBUFMAX) {
/*
* Copy all but the last vbuf worth of data in from
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -