📄 atp.c
字号:
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
write_reg_high(ioaddr, IMR, ISRh_RxErr);
lp->tx_unit_busy = 0;
lp->pac_cnt_in_tx_buf = 0;
lp->saved_tx_size = 0;
dev->tbusy = 0;
dev->interrupt = 0;
}
static void trigger_send(short ioaddr, int length)
{
write_reg_byte(ioaddr, TxCNT0, length & 0xff);
write_reg(ioaddr, TxCNT1, length >> 8);
write_reg(ioaddr, CMR1, CMR1_Xmit);
}
static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
{
length = (length + 1) & ~1; /* Round up to word length. */
outb(EOC+MAR, ioaddr + PAR_DATA);
if ((data_mode & 1) == 0) {
/* Write the packet out, starting with the write addr. */
outb(WrAddr+MAR, ioaddr + PAR_DATA);
do {
write_byte_mode0(ioaddr, *packet++);
} while (--length > 0) ;
} else {
/* Write the packet out in slow mode. */
unsigned char outbyte = *packet++;
outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
outb(WrAddr+MAR, ioaddr + PAR_DATA);
outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA);
outb(outbyte & 0x0f, ioaddr + PAR_DATA);
outbyte >>= 4;
outb(outbyte & 0x0f, ioaddr + PAR_DATA);
outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
while (--length > 0)
write_byte_mode1(ioaddr, *packet++);
}
/* Terminate the Tx frame. End of write: ECB. */
outb(0xff, ioaddr + PAR_DATA);
outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
}
static int
net_send_packet(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
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;
printk("%s: transmit timed out, %s?\n", dev->name,
inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
: "IRQ conflict");
lp->stats.tx_errors++;
/* Try to restart the adaptor. */
hardware_init(dev);
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. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
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;
int flags;
/* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
This sequence must not be interrupted by an incoming packet. */
save_flags(flags);
cli();
write_reg(ioaddr, IMR, 0);
write_reg_high(ioaddr, IMR, 0);
restore_flags(flags);
write_packet(ioaddr, length, buf, dev->if_port);
lp->pac_cnt_in_tx_buf++;
if (lp->tx_unit_busy == 0) {
trigger_send(ioaddr, length);
lp->saved_tx_size = 0; /* Redundent */
lp->re_tx = 0;
lp->tx_unit_busy = 1;
} else
lp->saved_tx_size = length;
dev->trans_start = jiffies;
/* Re-enable the LPT interrupts. */
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
write_reg_high(ioaddr, IMR, ISRh_RxErr);
}
if (skb->free)
kfree_skb (skb, FREE_WRITE);
return 0;
}
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
net_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 = 20;
static int num_tx_since_rx = 0;
if (dev == NULL) {
printk ("ATP_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
/* Disable additional spurious interrupts. */
outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
/* The adaptor's output is currently the IRQ line, switch it to data. */
write_reg(ioaddr, CMR2, CMR2_NULL);
write_reg(ioaddr, IMR, 0);
if (net_debug > 5) printk("%s: In interrupt ", dev->name);
while (--boguscount > 0) {
status = read_nibble(ioaddr, ISR);
if (net_debug > 5) printk("loop status %02x..", status);
if (status & (ISR_RxOK<<3)) {
write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
do {
int read_status = read_nibble(ioaddr, CMR1);
if (net_debug > 6)
printk("handling Rx packet %02x..", read_status);
/* We acknowledged the normal Rx interrupt, so if the interrupt
is still outstanding we must have a Rx error. */
if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */
lp->stats.rx_over_errors++;
/* Set to no-accept mode long enough to remove a packet. */
write_reg_high(ioaddr, CMR2, CMR2h_OFF);
net_rx(dev);
/* Clear the interrupt and return to normal Rx mode. */
write_reg_high(ioaddr, ISR, ISRh_RxErr);
write_reg_high(ioaddr, CMR2, lp->addr_mode);
} else if ((read_status & (CMR1_BufEnb << 3)) == 0) {
net_rx(dev);
dev->last_rx = jiffies;
num_tx_since_rx = 0;
} else
break;
} while (--boguscount > 0);
} else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
if (net_debug > 6) printk("handling Tx done..");
/* Clear the Tx interrupt. We should check for too many failures
and reinitialize the adaptor. */
write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
if (status & (ISR_TxErr<<3)) {
lp->stats.collisions++;
if (++lp->re_tx > 15) {
lp->stats.tx_aborted_errors++;
hardware_init(dev);
break;
}
/* Attempt to retransmit. */
if (net_debug > 6) printk("attempting to ReTx");
write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
} else {
/* Finish up the transmit. */
lp->stats.tx_packets++;
lp->pac_cnt_in_tx_buf--;
if ( lp->saved_tx_size) {
trigger_send(ioaddr, lp->saved_tx_size);
lp->saved_tx_size = 0;
lp->re_tx = 0;
} else
lp->tx_unit_busy = 0;
dev->tbusy = 0;
mark_bh(INET_BH); /* Inform upper layers. */
}
num_tx_since_rx++;
} else if (num_tx_since_rx > 8
&& jiffies > dev->last_rx + 100) {
if (net_debug > 2)
printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
" status %02x CMR1 %02x.\n", dev->name,
num_tx_since_rx, jiffies - dev->last_rx, status,
(read_nibble(ioaddr, CMR1) >> 3) & 15);
lp->stats.rx_missed_errors++;
hardware_init(dev);
num_tx_since_rx = 0;
break;
} else
break;
}
/* This following code fixes a rare (and very difficult to track down)
problem where the adaptor forgets its ethernet address. */
{
int i;
for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
}
/* Tell the adaptor that it can go back to using the output line as IRQ. */
write_reg(ioaddr, CMR2, CMR2_IRQOUT);
/* Enable the physical interrupt line, which is sure to be low until.. */
outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
/* .. we enable the interrupt sources. */
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */
if (net_debug > 5) printk("exiting interrupt.\n");
dev->interrupt = 0;
return;
}
/* We have a good packet(s), get it/them out of the buffers. */
static void net_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
#ifdef notdef
ushort header[4];
#else
struct rx_header rx_head;
#endif
/* Process the received packet. */
outb(EOC+MAR, ioaddr + PAR_DATA);
read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
if (net_debug > 5)
printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
if ((rx_head.rx_status & 0x77) != 0x01) {
lp->stats.rx_errors++;
/* Ackkk! I don't have any documentation on what the error bits mean!
The best I can do is slap the device around a bit. */
if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
dev->name, rx_head.rx_status);
hardware_init(dev);
return;
} else {
/* Malloc up new buffer. */
int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */
int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
skb = alloc_skb(sksize, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
goto done;
}
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
read_block(ioaddr, pkt_len, skb->data, dev->if_port);
if (net_debug > 6) {
unsigned char *data = skb->data;
printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
"%02x%02x%02x %02x%02x..",
data[0], data[1], data[2], data[3], data[4], data[5],
data[6], data[7], data[8], data[9], data[10], data[11],
data[12], data[13]);
}
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_s(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
lp->stats.rx_packets++;
}
done:
write_reg(ioaddr, CMR1, CMR1_NextPkt);
return;
}
static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
{
if (data_mode <= 3) { /* Mode 0 or 1 */
outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR,
ioaddr + PAR_DATA);
if (data_mode <= 1) { /* Mode 0 or 1 */
do *p++ = read_byte_mode0(ioaddr); while (--length > 0);
} else /* Mode 2 or 3 */
do *p++ = read_byte_mode2(ioaddr); while (--length > 0);
} else if (data_mode <= 5)
do *p++ = read_byte_mode4(ioaddr); while (--length > 0);
else
do *p++ = read_byte_mode6(ioaddr); while (--length > 0);
outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
}
/* The inverse routine to net_open(). */
static int
net_close(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
dev->tbusy = 1;
dev->start = 0;
/* Flush the Tx and disable Rx here. */
lp->addr_mode = CMR2h_OFF;
write_reg_high(ioaddr, CMR2, CMR2h_OFF);
/* Free the IRQ line. */
outb(0x00, ioaddr + PAR_CONTROL);
free_irq(dev->irq);
irq2dev_map[dev->irq] = 0;
/* Leave the hardware in a reset state. */
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
return 0;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct enet_statistics *
net_get_stats(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
return &lp->stats;
}
#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
best-effort filtering.
*/
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
write_reg_high(ioaddr, CMR2, lp->addr_mode);
}
#endif
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -