/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ staticint max_interrupt_work = 15;
#define NUM_UNITS 2 /* The standard set of ISA module parameters. */ staticint io[NUM_UNITS]; staticint irq[NUM_UNITS]; staticint xcvr[NUM_UNITS]; /* The data transfer mode. */
/* Operational parameters that are set at compile time. */
/* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (400*HZ/1000)
/* The number of low I/O ports used by the ethercard. */ #define ETHERCARD_TOTAL_SIZE 3
/* Sequence to switch an 8012 from printer mux to ethernet mode. */ staticchar mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
struct net_local {
spinlock_t lock; struct net_device *next_module; struct timer_list timer; /* Media selection timer. */ struct net_device *dev; /* Timer dev. */ unsignedlong last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ int saved_tx_size; unsignedint tx_unit_busy:1; unsignedchar re_tx, /* Number of packet retransmissions. */
addr_mode, /* Current Rx filter e.g. promiscuous, etc. */
pac_cnt_in_tx_buf;
};
/* This code, written by wwc@super.org, resets the adapter every TIMED_CHECKERticks.Thisrecoversfromanunknownerrorwhich
hangs the device. */ #define TIMED_CHECKER (HZ/4) #ifdef TIMED_CHECKER #include <linux/timer.h> staticvoid atp_timed_checker(struct timer_list *t); #endif
/* A list of all installed ATP devices, for removing the driver module. */ staticstruct net_device *root_atp_dev;
/* Check for a network adapter of this type, and return '0' iff one exists. Ifdev->base_addr==0,probealllikelylocations. Ifdev->base_addr==1,alwaysreturnfailure. Ifdev->base_addr==2,allocatespaceforthedeviceandreturnsuccess (detachabledevicesonly).
FIXME:weshouldusetheparportlayerforthis
*/ staticint __init atp_init(void)
{ int *port, ports[] = {0x378, 0x278, 0x3bc, 0}; int base_addr = io[0];
if (base_addr > 0x1ff) /* Check a single specified location. */ return atp_probe1(base_addr); elseif (base_addr == 1) /* Don't probe at all. */ return -ENXIO;
for (port = ports; *port; port++) { long ioaddr = *port;
outb(0x57, ioaddr + PAR_DATA); if (inb(ioaddr + PAR_DATA) != 0x57) continue; if (atp_probe1(ioaddr) == 0) return0;
}
staticint __init atp_probe1(long ioaddr)
{ struct net_device *dev = NULL; struct net_local *lp; int saved_ctrl_reg, status, i; int res;
outb(0xff, ioaddr + PAR_DATA); /* Save the original value of the Control register, in case we guessed
wrong. */
saved_ctrl_reg = inb(ioaddr + PAR_CONTROL); if (net_debug > 3)
printk("atp: Control register was %#2.2x.\n", saved_ctrl_reg); /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
outb(0x04, ioaddr + PAR_CONTROL); #ifndef final_version if (net_debug > 3) { /* Turn off the printer multiplexer on the 8012. */ for (i = 0; i < 8; i++)
outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg(ioaddr, MODSEL, 0x00);
printk("atp: Registers are "); for (i = 0; i < 32; i++)
printk(" %2.2x", read_nibble(ioaddr, i));
printk(".\n");
} #endif /* Turn off the printer multiplexer on the 8012. */ for (i = 0; i < 8; i++)
outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET); /* udelay() here? */
status = read_nibble(ioaddr, CMR1);
if (net_debug > 3) {
printk(KERN_DEBUG "atp: Status nibble was %#2.2x..", status); for (i = 0; i < 32; i++)
printk(" %2.2x", read_nibble(ioaddr, i));
printk("\n");
}
if ((status & 0x78) != 0x08) { /* The pocket adapter probe failed, restore the control register. */
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); return -ENODEV;
}
status = read_nibble(ioaddr, CMR2_h); if ((status & 0x78) != 0x10) {
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); return -ENODEV;
}
dev = alloc_etherdev(sizeof(struct net_local)); if (!dev) return -ENOMEM;
/* Find the IRQ used by triggering an interrupt. */
write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */
write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */
/* For the ATP adapter the "if_port" is really the data transfer mode. */ if (xcvr[0])
dev->if_port = xcvr[0]; else
dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; if (dev->mem_end & 0xf)
net_debug = dev->mem_end & 7;
/* Read the station address PROM, usually a word-wide EEPROM. */ staticvoid __init get_node_ID(struct net_device *dev)
{ long ioaddr = dev->base_addr;
__be16 addr[ETH_ALEN / 2]; int sa_offset = 0; int i;
write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */
/* Some adapters have the station address at offset 15 instead of offset
zero. Check for it, and fix it if needed. */ if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
sa_offset = 15;
for (i = 0; i < 3; i++)
addr[i] =
cpu_to_be16(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
eth_hw_addr_set(dev, (u8 *)addr);
/* The interrupt line is turned off (tri-stated) when the device isn't in use.That'sespeciallyimportantfor"attached"interfaceswherethe
port or interrupt may be shared. */
ret = request_irq(dev->irq, atp_interrupt, 0, dev->name, dev); if (ret) return ret;
/* This routine resets the hardware. We initialize everything, assuming that
the hardware may have been temporarily detached. */ staticvoid hardware_init(struct net_device *dev)
{ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; int i;
/* Turn off the printer multiplexer on the 8012. */ for (i = 0; i < 8; i++)
outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
write_reg_high(ioaddr, CMR2, lp->addr_mode);
if (net_debug > 2) {
printk(KERN_DEBUG "%s: Reset: current Rx mode %d.\n", dev->name,
(read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
}
/* The adapter'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(KERN_DEBUG "%s: In interrupt ", dev->name); while (--boguscount > 0) { int status = read_nibble(ioaddr, ISR); if (net_debug > 5)
printk("loop status %02x..", status);
if (status & (ISR_RxOK<<3)) {
handled = 1;
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. */
dev->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);
} elseif ((read_status & (CMR1_BufEnb << 3)) == 0) {
net_rx(dev);
num_tx_since_rx = 0;
} else break;
} while (--boguscount > 0);
} elseif (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
handled = 1; if (net_debug > 6)
printk("handling Tx done.."); /* Clear the Tx interrupt. We should check for too many failures
and reinitialize the adapter. */
write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK); if (status & (ISR_TxErr<<3)) {
dev->stats.collisions++; if (++lp->re_tx > 15) {
dev->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. */
dev->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;
netif_wake_queue(dev); /* Inform upper layers. */
}
num_tx_since_rx++;
} elseif (num_tx_since_rx > 8 &&
time_after(jiffies, lp->last_rx_time + HZ)) { if (net_debug > 2)
printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and " "%ld jiffies status %02x CMR1 %02x.\n", dev->name,
num_tx_since_rx, jiffies - lp->last_rx_time, status,
(read_nibble(ioaddr, CMR1) >> 3) & 15);
dev->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 adapter forgets its ethernet address. */
{ int i; for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); #if0 && defined(TIMED_CHECKER)
mod_timer(&lp->timer, jiffies + TIMED_CHECKER); #endif
}
/* Tell the adapter 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? */
spin_unlock(&lp->lock);
if (net_debug > 5) printk("exiting interrupt.\n"); return IRQ_RETVAL(handled);
}
#ifdef TIMED_CHECKER /* This following code fixes a rare (and very difficult to track down)
problem where the adapter forgets its ethernet address. */ staticvoid atp_timed_checker(struct timer_list *t)
{ struct net_local *lp = timer_container_of(lp, t, timer); struct net_device *dev = lp->dev; long ioaddr = dev->base_addr; int tickssofar = jiffies - lp->last_rx_time; int i;
spin_lock(&lp->lock); if (tickssofar > 2*HZ) { #if1 for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
lp->last_rx_time = jiffies; #else for (i = 0; i < 6; i++) if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
{ struct net_local *lp = netdev_priv(atp_timed_dev);
write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); if (i == 2)
dev->stats.tx_errors++; elseif (i == 3)
dev->stats.tx_dropped++; elseif (i == 4)
dev->stats.collisions++; else
dev->stats.rx_errors++;
} #endif
}
spin_unlock(&lp->lock);
lp->timer.expires = jiffies + TIMED_CHECKER;
add_timer(&lp->timer);
} #endif
/* We have a good packet(s), get it/them out of the buffers. */ staticvoid net_rx(struct net_device *dev)
{ struct net_local *lp = netdev_priv(dev); long ioaddr = dev->base_addr; struct rx_header rx_head;
/* Process the received packet. */
outb(EOC+MAR, ioaddr + PAR_DATA);
read_block(ioaddr, 8, (unsignedchar*)&rx_head, dev->if_port); if (net_debug > 5)
printk(KERN_DEBUG " 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) {
dev->stats.rx_errors++; if (rx_head.rx_status & 0x0004) dev->stats.rx_frame_errors++; elseif (rx_head.rx_status & 0x0002) dev->stats.rx_crc_errors++; if (net_debug > 3)
printk(KERN_DEBUG "%s: Unknown ATP Rx error %04x.\n",
dev->name, rx_head.rx_status); if (rx_head.rx_status & 0x0020) {
dev->stats.rx_fifo_errors++;
write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
} elseif (rx_head.rx_status & 0x0050)
hardware_init(dev); return;
} else { /* Malloc up new buffer. The "-4" omits the FCS (CRC). */ int pkt_len = (rx_head.rx_count & 0x7ff) - 4; struct sk_buff *skb;
staticint __init atp_init_module(void) { if (debug) /* Emit version even if no cards detected. */
printk(KERN_INFO "%s", version); return atp_init();
}
while (root_atp_dev) { struct net_local *atp_local = netdev_priv(root_atp_dev);
next_dev = atp_local->next_module;
unregister_netdev(root_atp_dev); /* No need to release_region(), since we never snarf it. */
free_netdev(root_atp_dev);
root_atp_dev = next_dev;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.