📄 3c507.c
字号:
el16_send_packet(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
short *shmem = (short*)dev->mem_start;
if (dev->tbusy) {
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 5)
return 1;
if (net_debug > 1)
printk("%s: transmit timed out, %s? ", dev->name,
shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
"network cable problem");
/* Try to restart the adaptor. */
if (lp->last_restart == lp->stats.tx_packets) {
if (net_debug > 1) printk("Resetting board.\n");
/* Completely reset the adaptor. */
init_82586_mem(dev);
} else {
/* Issue the channel attention signal and hope it "gets better". */
if (net_debug > 1) printk("Kicking board.\n");
shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
lp->last_restart = lp->stats.tx_packets;
}
dev->tbusy=0;
dev->trans_start = jiffies;
}
/* If some higher layer thinks we've missed an tx-done interrupt
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
itself. */
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* For ethernet, fill in the header. This should really be done by a
higher level, rather than duplicated for each ethernet adaptor. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
/* Block a timer-based transmit from overlapping. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
else {
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
unsigned char *buf = skb->data;
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
hardware_send_packet(dev, buf, length);
dev->trans_start = jiffies;
/* Enable the 82586 interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
}
if (skb->free)
kfree_skb (skb, FREE_WRITE);
/* You might need to clean up and record Tx statistics here. */
return 0;
}
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
el16_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 0;
ushort ack_cmd = 0;
ushort *shmem;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
shmem = ((ushort*)dev->mem_start);
status = shmem[iSCB_STATUS>>1];
if (net_debug > 4) {
printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
}
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
/* Reap the Tx packet buffers. */
while (lp->tx_reap != lp->tx_head) {
unsigned short tx_status = shmem[lp->tx_reap>>1];
if (tx_status == 0) {
if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
break;
}
if (tx_status & 0x2000) {
lp->stats.tx_packets++;
lp->stats.collisions += tx_status & 0xf;
dev->tbusy = 0;
mark_bh(INET_BH); /* Inform upper layers. */
} else {
lp->stats.tx_errors++;
if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
}
if (net_debug > 5)
printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
lp->tx_reap += TX_BUF_SIZE;
if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
lp->tx_reap = TX_BUF_START;
if (++boguscount > 4)
break;
}
if (status & 0x4000) { /* Packet received. */
if (net_debug > 5)
printk("Received packet, rx_head %04x.\n", lp->rx_head);
el16_rx(dev);
}
/* Acknowledge the interrupt sources. */
ack_cmd = status & 0xf000;
if ((status & 0x0700) != 0x0200 && dev->start) {
if (net_debug)
printk("%s: Command unit stopped, status %04x, restarting.\n",
dev->name, status);
/* If this ever occurs we should really re-write the idle loop, reset
the Tx list, and do a complete restart of the command unit.
For now we rely on the Tx timeout if the resume doesn't work. */
ack_cmd |= CUC_RESUME;
}
if ((status & 0x0070) != 0x0040 && dev->start) {
static void init_rx_bufs(struct device *);
/* The Rx unit is not ready, it must be hung. Restart the receiver by
initializing the rx buffers, and issuing an Rx start command. */
if (net_debug)
printk("%s: Rx unit stopped, status %04x, restarting.\n",
dev->name, status);
init_rx_bufs(dev);
shmem[iSCB_RFA >> 1] = RX_BUF_START;
ack_cmd |= RX_START;
}
shmem[iSCB_CMD>>1] = ack_cmd;
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
/* Clear the latched interrupt. */
outb(0, ioaddr + RESET_IRQ);
/* Enable the 82586's interrupt input. */
outb(0x84, ioaddr + MISC_CTRL);
return;
}
static int
el16_close(struct device *dev)
{
int ioaddr = dev->base_addr;
ushort *shmem = (short*)dev->mem_start;
dev->tbusy = 1;
dev->start = 0;
/* Flush the Tx and disable Rx. */
shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
outb(0, ioaddr + SIGNAL_CA);
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
/* We always physically use the IRQ line, so we don't do free_irq().
We do remove ourselves from the map. */
irq2dev_map[dev->irq] = 0;
/* Update the statistics here. */
return 0;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct enet_statistics *
el16_get_stats(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
/* ToDo: decide if there are any useful statistics from the SCB. */
return &lp->stats;
}
/* Initialize the Rx-block list. */
static void
init_rx_bufs(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
unsigned short *write_ptr;
unsigned short SCB_base = SCB_BASE;
int cur_rxbuf = lp->rx_head = RX_BUF_START;
/* Initialize each Rx frame + data buffer. */
do { /* While there is room for one more. */
write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
*write_ptr++ = 0x0000; /* Status */
*write_ptr++ = 0x0000; /* Command */
*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
*write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
*write_ptr++ = 0x0000; /* Pad for dest addr. */
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000; /* Pad for source addr. */
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000;
*write_ptr++ = 0x0000; /* Pad for protocol. */
*write_ptr++ = 0x0000; /* Buffer: Actual count */
*write_ptr++ = -1; /* Buffer: Next (none). */
*write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
*write_ptr++ = 0x0000;
/* Finally, the number of bytes in the buffer. */
*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
lp->rx_tail = cur_rxbuf;
cur_rxbuf += RX_BUF_SIZE;
} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
the list a ring. */
write_ptr = (unsigned short *)
(dev->mem_start + lp->rx_tail + 2);
*write_ptr++ = 0xC000; /* Command, mark as last. */
*write_ptr++ = lp->rx_head; /* Link */
}
void
init_82586_mem(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort *shmem = (short*)dev->mem_start;
/* Enable loopback to protect the wire while starting up,
and hold the 586 in reset during the memory initialization. */
outb(0x20, ioaddr + MISC_CTRL);
/* Fix the ISCP address and base. */
init_words[3] = SCB_BASE;
init_words[7] = SCB_BASE;
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
memcpy((void*)dev->mem_end-10, init_words, 10);
/* Write the words at 0x0000. */
memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
/* Fill in the station address. */
memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
sizeof(dev->dev_addr));
/* The Tx-block list is written as needed. We just set up the values. */
lp->tx_cmd_link = IDLELOOP + 4;
lp->tx_head = lp->tx_reap = TX_BUF_START;
init_rx_bufs(dev);
/* Start the 586 by releasing the reset line, but leave loopback. */
outb(0xA0, ioaddr + MISC_CTRL);
/* This was time consuming to track down: you need to give two channel
attention signals to reliably start up the i82586. */
outb(0, ioaddr + SIGNAL_CA);
{
int boguscnt = 50;
while (shmem[iSCB_STATUS>>1] == 0)
if (--boguscnt == 0) {
printk("%s: i82586 initialization timed out with status %04x,"
"cmd %04x.\n", dev->name,
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
break;
}
/* Issue channel-attn -- the 82586 won't start. */
outb(0, ioaddr + SIGNAL_CA);
}
/* Disable loopback and enable interrupts. */
outb(0x84, ioaddr + MISC_CTRL);
if (net_debug > 4)
printk("%s: Initialized 82586, status %04x.\n", dev->name,
shmem[iSCB_STATUS>>1]);
return;
}
static void
hardware_send_packet(struct device *dev, void *buf, short length)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort tx_block = lp->tx_head;
ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
/* Set the write pointer to the Tx block, and put out the header. */
*write_ptr++ = 0x0000; /* Tx status */
*write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
*write_ptr++ = tx_block+16; /* Next command is a NoOp. */
*write_ptr++ = tx_block+8; /* Data Buffer offset. */
/* Output the data buffer descriptor. */
*write_ptr++ = length | 0x8000; /* Byte count parameter. */
*write_ptr++ = -1; /* No next data buffer. */
*write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
*write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
/* Output the Loop-back NoOp command. */
*write_ptr++ = 0x0000; /* Tx status */
*write_ptr++ = CmdNOp; /* Tx command */
*write_ptr++ = tx_block+16; /* Next is myself. */
/* Output the packet at the write pointer. */
memcpy(write_ptr, buf, length);
/* Set the old command link pointing to this send packet. */
*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
lp->tx_cmd_link = tx_block + 20;
/* Set the next free tx region. */
lp->tx_head = tx_block + TX_BUF_SIZE;
if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
lp->tx_head = TX_BUF_START;
if (net_debug > 4) {
printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
dev->name, ioaddr, length, tx_block, lp->tx_head);
}
if (lp->tx_head != lp->tx_reap)
dev->tbusy = 0;
}
static void
el16_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short *shmem = (short*)dev->mem_start;
ushort rx_head = lp->rx_head;
ushort rx_tail = lp->rx_tail;
ushort boguscount = 10;
short frame_status;
while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
ushort *read_frame = (short *)(dev->mem_start + rx_head);
ushort rfd_cmd = read_frame[1];
ushort next_rx_frame = read_frame[2];
ushort data_buffer_addr = read_frame[3];
ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
ushort pkt_len = data_frame[0];
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|| pkt_len & 0xC000 != 0xC000) {
printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
"next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
pkt_len);
} else if ((frame_status & 0x2000) == 0) {
/* Frame Rxed, but with error. */
lp->stats.rx_errors++;
if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
if (frame_status & 0x0100) lp->stats.rx_over_errors++;
if (frame_status & 0x0080) lp->stats.rx_length_errors++;
} else {
/* Malloc up new buffer. */
int sksize;
struct sk_buff *skb;
pkt_len &= 0x3fff;
sksize = sizeof(struct sk_buff) + pkt_len;
skb = alloc_skb(sksize, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
memcpy(skb->data, data_frame + 5, pkt_len);
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_skbmem(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
lp->stats.rx_packets++;
}
/* Clear the status word and set End-of-List on the rx frame. */
read_frame[0] = 0;
read_frame[1] = 0xC000;
/* Clear the end-of-list on the prev. RFD. */
*(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
rx_tail = rx_head;
rx_head = next_rx_frame;
if (--boguscount == 0)
break;
}
lp->rx_head = rx_head;
lp->rx_tail = rx_tail;
}
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* c-indent-level: 4
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -