/* Used to do a global count of all the cards in the system. Must be *aglobalvariablesothattheeisaproberoutinescanincrement
* it */ staticint el3_cards = 0; #define EL3_MAX_CARDS 8
/* To minimize the size of the driver source I only define operating constantsiftheyareusedseveraltimes.You'llneedthemanual
anyway if you want to understand driver details. */ /* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_CMD 0x0e #define EL3_STATUS 0x0e #define EEPROM_READ 0x80
/* Return 0 on success, 1 on error, 2 when found already detected PnP card */ staticint el3_isa_id_sequence(__be16 *phys_addr)
{ short lrs_state = 0xff; int i;
/* ISA boards are detected by sending the ID sequence to the ID_PORT.Wefindcardspastthefirstbysettingthe'current_tag' oncardsastheyarefound.Cardswiththeirtagsetwillnot
respond to subsequent ID sequences. */
outb(0x00, id_port);
outb(0x00, id_port); for (i = 0; i < 255; i++) {
outb(lrs_state, id_port);
lrs_state <<= 1;
lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
} /* For the first probe, clear all board's tag registers. */ if (current_tag == 0)
outb(0xd0, id_port); else/* Otherwise kill off already-found boards. */
outb(0xd8, id_port); if (id_read_eeprom(7) != 0x6d50) return1; /* Read in EEPROM data, which does contention-select. Onlythelowestaddressboardwillstay"on-line".
3Com got the byte order backwards. */ for (i = 0; i < 3; i++)
phys_addr[i] = htons(id_read_eeprom(i)); #ifdef CONFIG_PNP if (!nopnp) { /* The ISA PnP 3c509 cards respond to the ID sequence too.
This check is needed in order not to register them twice. */ for (i = 0; i < el3_cards; i++) { struct el3_private *lp = netdev_priv(el3_devs[i]); if (lp->type == EL3_PNP &&
ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) { if (el3_debug > 3)
pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
phys_addr[0] & 0xff, phys_addr[0] >> 8,
phys_addr[1] & 0xff, phys_addr[1] >> 8,
phys_addr[2] & 0xff, phys_addr[2] >> 8); /* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, id_port); return2;
}
}
} #endif/* CONFIG_PNP */ return0;
}
staticvoid el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr, int irq, int if_port, enum el3_cardtype type)
{ struct el3_private *lp = netdev_priv(dev);
while ((err = el3_isa_id_sequence(phys_addr)) == 2)
; /* Skip to next card when PnP card found */ if (err == 1) return0; /* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, id_port); /* Enable the card */
outb((ioaddr >> 4) | 0xe0, id_port);
EL3WINDOW(0); if (inw(ioaddr) != 0x6d50) return1; /* Free the interrupt so that some other card can use it. */
outw(0x0f00, ioaddr + WN0_IRQ); return el3_resume(dev);
} #endif
if (err) {
eisa_set_drvdata (edev, NULL);
free_netdev(dev); return err;
}
el3_devs[el3_cards++] = dev; return0;
} #endif
/* This remove works for all device types. *
* The net dev must be stored in the driver data field */ staticint el3_device_remove(struct device *device)
{ struct net_device *dev;
dev = dev_get_drvdata(device);
el3_common_remove (dev); return0;
}
/* Read a word from the EEPROM using the regular EEPROM access register. Assumethatweareinregisterwindowzero.
*/ static ushort read_eeprom(int ioaddr, int index)
{
outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place.
Some chips seem to require much longer */
mdelay(2); return inw(ioaddr + 12);
}
/* Read a word from the EEPROM when in the ISA ID probe state. */ static ushort id_read_eeprom(int index)
{ int bit, word = 0;
/* Issue read command, and pause for at least 162 us. for it to complete.
Assume extra-fast 16Mhz bus. */
outb(EEPROM_READ + index, id_port);
/* Pause for at least 162 us. for the read to take place. */ /* Some chips seem to require much longer */
mdelay(4);
for (bit = 15; bit >= 0; bit--)
word = (word << 1) + (inb(id_port) & 0x01);
if (el3_debug > 3)
pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
return word;
}
staticint
el3_open(struct net_device *dev)
{ int ioaddr = dev->base_addr; int i;
/* Put out the doubleword header... */
outw(skb->len, ioaddr + TX_FIFO);
outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
if (inw(ioaddr + TX_FREE) > 1536)
netif_start_queue(dev); else /* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
spin_unlock_irqrestore(&lp->lock, flags);
dev_consume_skb_any (skb);
/* Clear the Tx status stack. */
{ short tx_status; int i = 4;
while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { if (tx_status & 0x38) dev->stats.tx_aborted_errors++; if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
}
} return NETDEV_TX_OK;
}
/* The EL3 interrupt handler. */ static irqreturn_t
el3_interrupt(int irq, void *dev_id)
{ struct net_device *dev = dev_id; struct el3_private *lp; int ioaddr, status; int i = max_interrupt_work;
lp = netdev_priv(dev);
spin_lock(&lp->lock);
ioaddr = dev->base_addr;
if (el3_debug > 4) {
status = inw(ioaddr + EL3_STATUS);
pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
}
/* Update statistics. We change to register window 6, so this should be run single-threadedifthedeviceisactive.Thisisexpectedtobearare operation,andit'ssimplerfortherestofthedrivertoassumethat window1isalwaysvalidratherthanuseaspecialwindow-statevariable.
*/ staticvoid update_stats(struct net_device *dev)
{ int ioaddr = dev->base_addr;
if (el3_debug > 2)
pr_debug("%s: Shutting down ethercard.\n", dev->name);
el3_down(dev);
free_irq(dev->irq, dev); /* Switching back to window 0 disables the IRQ. */
EL3WINDOW(0); if (lp->type != EL3_EISA) { /* But we explicitly zero the IRQ line select anyway. Don't do *itonEISAcards,itpreventsthemodulefromgettingan
* IRQ after unload+reload... */
outw(0x0f00, ioaddr + WN0_IRQ);
}
staticvoid
el3_down(struct net_device *dev)
{ int ioaddr = dev->base_addr;
netif_stop_queue(dev);
/* Turn off statistics ASAP. We update lp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Disable the receiver and transmitter. */
outw(RxDisable, ioaddr + EL3_CMD);
outw(TxDisable, ioaddr + EL3_CMD);
if (dev->if_port == 3) /* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD); elseif (dev->if_port == 0) { /* Disable link beat and jabber, if_port may change here next open(). */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
}
outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
update_stats(dev);
}
staticvoid
el3_up(struct net_device *dev)
{ int i, sw_info, net_diag; int ioaddr = dev->base_addr;
/* Activating the board required and does no harm otherwise */
outw(0x0001, ioaddr + 4);
/* Set the IRQ line. */
outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
if ((dev->if_port & 0x03) == 3) /* BNC interface */ /* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD); elseif ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ /* Combine secondary sw_info word (the adapter level) and primary
sw_info word (duplex setting plus other useless bits) */
EL3WINDOW(0);
sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
(read_eeprom(ioaddr, 0x0d) & 0xBff0);
EL3WINDOW(4);
net_diag = inw(ioaddr + WN4_NETDIAG);
net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
pr_info("%s: ", dev->name); switch (dev->if_port & 0x0c) { case12: /* force full-duplex mode if 3c5x9b */ if (sw_info & 0x000f) {
pr_cont("Forcing 3c5x9b full-duplex mode"); break;
}
fallthrough; case8: /* set full-duplex mode based on eeprom config setting */ if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); break;
}
fallthrough; default: /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
}
outw(net_diag, ioaddr + WN4_NETDIAG);
pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); if (el3_debug > 3)
pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); /* Enable link beat and jabber check. */
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
}
/* Switch to the stats window, and clear all stats by reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
EL3WINDOW(6); for (i = 0; i < 9; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12);
/* Switch to register set 1 for normal use. */
EL3WINDOW(1);
staticint __init el3_init_module(void)
{ int ret = 0;
if (debug >= 0)
el3_debug = debug;
#ifdef CONFIG_PNP if (!nopnp) {
ret = pnp_register_driver(&el3_pnp_driver); if (!ret)
pnp_registered = 1;
} #endif /* Select an open I/O location at 0x1*0 to do ISA contention select. */ /* Start with 0x110 to avoid some sound cards.*/ for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) { if (!request_region(id_port, 1, "3c509-control")) continue;
outb(0x00, id_port);
outb(0xff, id_port); if (inb(id_port) & 0x01) break; else
release_region(id_port, 1);
} if (id_port >= 0x200) {
id_port = 0;
pr_err("No I/O port available for 3c509 activation.\n");
} else {
ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS); if (!ret)
isa_registered = 1;
} #ifdef CONFIG_EISA
ret = eisa_driver_register(&el3_eisa_driver); if (!ret)
eisa_registered = 1; #endif
#ifdef CONFIG_PNP if (pnp_registered)
ret = 0; #endif if (isa_registered)
ret = 0; #ifdef CONFIG_EISA if (eisa_registered)
ret = 0; #endif return ret;
}
staticvoid __exit el3_cleanup_module(void)
{ #ifdef CONFIG_PNP if (pnp_registered)
pnp_unregister_driver(&el3_pnp_driver); #endif if (isa_registered)
isa_unregister_driver(&el3_isa_driver); if (id_port)
release_region(id_port, 1); #ifdef CONFIG_EISA if (eisa_registered)
eisa_driver_unregister(&el3_eisa_driver); #endif
}
¤ 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.0.41Bemerkung:
¤
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.