This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference.
This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
The author may be reached as becker@scyld.com, or C/O Scyld Computing Corporation 410 Severn Ave., Suite 210 Annapolis MD 21403
2000/2/2- Added support for kernel-level ISAPnP by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
2001/11/17 - Added ethtool support (jgarzik)
2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
*/
#define DRV_NAME "3c515"
#define CORKSCREW 1
/* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1512 effectively disables this feature. */ staticint rx_copybreak = 200;
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ staticint max_interrupt_work = 20;
/* Enable the automatic media selection code -- usually set. */ #define AUTOMEDIA 1
/* Allow the use of fragment bus master transfers instead of only programmed-I/O for Vortex cards. Full-bus-master transfers are always enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
the feature may be turned on using 'options'. */ #define VORTEX_BUS_MASTER
/* A few values that may be tweaked. */ /* Keep the ring sizes a power of two for efficiency. */ #define TX_RING_SIZE 16 #define RX_RING_SIZE 16 #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
/* "Knobs" for adjusting internal parameters. */ /* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ #define DRIVER_DEBUG 1 /* Some values here only for performance evaluation and path-coverage
debugging. */ staticint rx_nocopy, rx_copy, queued_packet;
/* Number of times to check to see if the Tx FIFO has space, used in some
limited cases. */ #define WAIT_TX_AVAIL 200
/* Operational parameter that usually are not changed. */ #define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */
/* The size here is somewhat misleading: the Corkscrew also uses the ISA aliased registers at <base>+0x400.
*/ #define CORKSCREW_TOTAL_SIZE 0x20
This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, 3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, it's not practical to integrate this driver with the other EtherLink drivers.
II. Board-specific settings
The Corkscrew has an EEPROM for configuration, but no special settings are needed for Linux.
III. Driver operation
The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" PCI cards, with the bus master interface extensively modified to work with the ISA bus.
The card is capable of full-bus-master transfers with separate lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, DEC Tulip and Intel Speedo3.
This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate receive buffer. This scheme allocates full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is chosen to trade-off the memory wasted by passing the full-sized skbuff to the queue layer for all frames vs. the copying cost of copying a frame to a correctly-sized skbuff.
IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One is the send-packet routine, which enforces single-threaded use by the netif layer. The other thread is the interrupt handler, which is single threaded by the hardware and other software.
IV. Notes
Thanks to Terry Murphy of 3Com for providing documentation and a development board.
The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com project names. I use these names to eliminate confusion -- 3Com product numbers and names are very similar and often confused.
The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! This driver only supports ethernet frames because of the recent MTU limit of 1.5K, but the changes to support 4.5K are minimal.
*/
/* Operational definitions. These are not used by other compilation units and thus are not exported in a ".h" file.
First the windows. There are eight register windows, with the command and status registers available in each.
*/ #define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) #define EL3_CMD 0x0e #define EL3_STATUS 0x0e
/* The top five bits written to EL3_CMD are a command, the lower 11 bits are the parameter, if applicable. Note that 11 parameters bits was fine for ethernet, but the new chips can handle FDDI length frames (~4500 octets) and now parameters count
32-bit 'Dwords' rather than octets. */
/* The Rx and Tx descriptor lists. Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
alignment contraint on tx_ring[] and rx_ring[]. */ struct boom_rx_desc {
u32 next;
s32 status;
u32 addr;
s32 length;
};
/* Values for the Rx status entry. */ enum rx_desc_status {
RxDComplete = 0x00008000, RxDError = 0x4000, /* See boomerang_rx() for actual error bits */
};
struct corkscrew_private { constchar *product_name; struct list_head list; struct net_device *our_dev; /* The Rx and Tx rings are here to keep them quad-word-aligned. */ struct boom_rx_desc rx_ring[RX_RING_SIZE]; struct boom_tx_desc tx_ring[TX_RING_SIZE]; /* The addresses of transmit- and receive-in-place skbuffs. */ struct sk_buff *rx_skbuff[RX_RING_SIZE]; struct sk_buff *tx_skbuff[TX_RING_SIZE]; unsignedint cur_rx, cur_tx; /* The next free ring entry */ unsignedint dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ struct timer_list timer; /* Media selection timer. */ int capabilities ; /* Adapter capabilities word. */ int options; /* User-settable misc. driver options. */ int last_rx_packets; /* For media autoselection. */ unsignedint available_media:8, /* From Wn3_Options */
media_override:3, /* Passed-in media type. */
default_media:3, /* Read from the EEPROM. */
full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
tx_full:1;
spinlock_t lock; struct device *dev;
};
/* The action to take with a media selection timer tick. Note that we deviate from the 3Com order by checking 10base2 before AUI.
*/ enum xcvr_types {
XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
};
staticstruct media_table { char *name; unsignedint media_bits:16, /* Bits to set in Wn4_Media register. */
mask:8, /* The transceiver-present bit in Wn3_Config. */
next:8; /* The media type to try next. */ short wait; /* Time before we check media status. */
} media_tbl[] = {
{ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
{ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
{ "undefined", 0, 0x80, XCVR_10baseT, 10000},
{ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
{ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
{ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
{ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
{ "undefined", 0, 0x01, XCVR_10baseT, 10000},
{ "Default", 0, 0xFF, XCVR_10baseT, 10000},
};
/* Unfortunately maximizing the shared code between the integrated and module version of the driver results in a complicated set of initialization procedures. init_module() -- modules / tc59x_init() -- built-in The wrappers for corkscrew_scan() corkscrew_scan() The common routine that scans for PCI and EISA cards corkscrew_found_device() Allocate a device structure when we find a card. Different versions exist for modules and built-in. corkscrew_probe1() Fill in the device structure -- this is separated so that the modules code can put it in dev->init.
*/ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Note: this is the only limit on the number of cards supported!! */ staticint options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
#ifdef MODULE staticint debug = -1;
module_param(debug, int, 0);
module_param_array(options, int, NULL, 0);
module_param(rx_copybreak, int, 0);
module_param(max_interrupt_work, int, 0);
MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
/* A list of all installed Vortex devices, for removing the driver module. */ /* we will need locking (and refcounting) if we ever use it for more */ static LIST_HEAD(root_corkscrew_dev);
staticint corkscrew_init_module(void)
{ int found = 0; if (debug >= 0)
corkscrew_debug = debug; while (corkscrew_scan(-1))
found++; return found ? 0 : -ENODEV;
}
module_init(corkscrew_init_module);
staticint check_device(unsigned ioaddr)
{ int timer;
if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) return 0; /* Check the resource configuration for a matching ioaddr. */ if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
release_region(ioaddr, CORKSCREW_TOTAL_SIZE); return 0;
} /* Verify by reading the device ID from the EEPROM. */
outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); /* Pause for at least 162 us. for the read to take place. */ for (timer = 4; timer >= 0; timer--) {
udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) break;
} if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
release_region(ioaddr, CORKSCREW_TOTAL_SIZE); return 0;
} return 1;
}
/* Before initializing select the active media port. */
EL3WINDOW(3); if (vp->full_duplex)
outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
config = inl(ioaddr + Wn3_Config);
if (vp->media_override != 7) { if (corkscrew_debug > 1)
pr_info("%s: Media override to transceiver %d (%s).\n",
dev->name, vp->media_override,
media_tbl[vp->media_override].name);
dev->if_port = vp->media_override;
} elseif (vp->autoselect) { /* Find first available media type, starting with 100baseTx. */
dev->if_port = 4; while (!(vp->available_media & media_tbl[dev->if_port].mask))
dev->if_port = media_tbl[dev->if_port].next;
if (corkscrew_debug > 1)
pr_debug("%s: Initial media type %s.\n",
dev->name, media_tbl[dev->if_port].name);
armtimer = true;
} else
dev->if_port = vp->default_media;
outw(TxReset, ioaddr + EL3_CMD); for (i = 20; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break;
outw(RxReset, ioaddr + EL3_CMD); /* Wait a few ticks for the RxReset command to complete. */ for (i = 20; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break;
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
/* Use the now-standard shared IRQ implementation. */ if (vp->capabilities == 0x11c7) { /* Corkscrew: Cannot share ISA resources. */ if (dev->irq == 0 ||
dev->dma == 0 ||
request_irq(dev->irq, corkscrew_interrupt, 0,
vp->product_name, dev)) return -EAGAIN;
enable_dma(dev->dma);
set_dma_mode(dev->dma, DMA_MODE_CASCADE);
} elseif (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
vp->product_name, dev)) { return -EAGAIN;
}
if (armtimer)
mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
if (corkscrew_debug > 1) {
EL3WINDOW(4);
pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + Wn4_Media));
}
/* Set the station address and mask in window 2 each time opened. */
EL3WINDOW(2); for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i); for (; i < 12; i += 2)
outw(0, ioaddr + i);
if (dev->if_port == 3) /* Start the thinnet transceiver. We should really wait 50ms... */
outw(StartCoax, ioaddr + EL3_CMD);
EL3WINDOW(4);
outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
media_tbl[dev->if_port].media_bits, 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 < 10; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */
EL3WINDOW(4);
inb(ioaddr + 12); /* ..and on the Boomerang we enable the extra statistics bits. */
outw(0x0040, ioaddr + Wn4_NetDiag);
/* Switch to register set 7 for normal use. */
EL3WINDOW(7);
if (vp->full_bus_master_rx) { /* Boomerang bus master. */
vp->cur_rx = vp->dirty_rx = 0; if (corkscrew_debug > 2)
pr_debug("%s: Filling in the Rx ring.\n", dev->name); for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; if (i < (RX_RING_SIZE - 1))
vp->rx_ring[i].next =
isa_virt_to_bus(&vp->rx_ring[i + 1]); else
vp->rx_ring[i].next = 0;
vp->rx_ring[i].status = 0; /* Clear complete bit. */
vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
vp->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
} if (i != 0)
vp->rx_ring[i - 1].next =
isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
} if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
vp->cur_tx = vp->dirty_tx = 0;
outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */ /* Clear the Tx ring. */ for (i = 0; i < TX_RING_SIZE; i++)
vp->tx_skbuff[i] = NULL;
outl(0, ioaddr + DownListPtr);
} /* Set receiver mode: presumably accept b-case and phys addr only. */
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
staticvoid corkscrew_timer(struct timer_list *t)
{ #ifdef AUTOMEDIA struct corkscrew_private *vp = timer_container_of(vp, t, timer); struct net_device *dev = vp->our_dev; int ioaddr = dev->base_addr; unsignedlong flags; int ok = 0;
if (corkscrew_debug > 1)
pr_debug("%s: Media selection timer tick happened, %s.\n",
dev->name, media_tbl[dev->if_port].name);
spin_lock_irqsave(&vp->lock, flags);
{ int old_window = inw(ioaddr + EL3_CMD) >> 13; int media_status;
EL3WINDOW(4);
media_status = inw(ioaddr + Wn4_Media); switch (dev->if_port) { case 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */ if (media_status & Media_LnkBeat) {
ok = 1; if (corkscrew_debug > 1)
pr_debug("%s: Media %s has link beat, %x.\n",
dev->name,
media_tbl[dev->if_port].name,
media_status);
} elseif (corkscrew_debug > 1)
pr_debug("%s: Media %s is has no link beat, %x.\n",
dev->name,
media_tbl[dev->if_port].name,
media_status);
break; default: /* Other media types handled by Tx timeouts. */ if (corkscrew_debug > 1)
pr_debug("%s: Media %s is has no indication, %x.\n",
dev->name,
media_tbl[dev->if_port].name,
media_status);
ok = 1;
} if (!ok) {
__u32 config;
do {
dev->if_port =
media_tbl[dev->if_port].next;
} while (!(vp->available_media & media_tbl[dev->if_port].mask));
if (dev->if_port == 8) { /* Go back to default. */
dev->if_port = vp->default_media; if (corkscrew_debug > 1)
pr_debug("%s: Media selection failing, using default %s port.\n",
dev->name,
media_tbl[dev->if_port].name);
} else { if (corkscrew_debug > 1)
pr_debug("%s: Media selection failed, now trying %s port.\n",
dev->name,
media_tbl[dev->if_port].name);
vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
add_timer(&vp->timer);
}
outw((media_status & ~(Media_10TP | Media_SQE)) |
media_tbl[dev->if_port].media_bits,
ioaddr + Wn4_Media);
if (corkscrew_debug > 4)
pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
dev->name, status, latency); if ((status & 0xE000) != 0xE000) { staticint donedidthis; /* Some interrupt controllers store a bogus interrupt from boot-time. Ignore a single early interrupt, but don't hang the machine for
other interrupt problems. */ if (donedidthis++ > 100) {
pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
dev->name, status, netif_running(dev));
free_irq(dev->irq, dev);
dev->irq = -1;
}
}
do { if (corkscrew_debug > 5)
pr_debug("%s: In interrupt loop, status %4.4x.\n",
dev->name, status); if (status & RxComplete)
corkscrew_rx(dev);
if (status & TxAvailable) { if (corkscrew_debug > 5)
pr_debug(" TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
} if (status & DownComplete) { unsignedint dirty_tx = lp->dirty_tx;
while (lp->cur_tx - dirty_tx > 0) { int entry = dirty_tx % TX_RING_SIZE; if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry])) break; /* It still hasn't been processed. */ if (lp->tx_skbuff[entry]) {
dev_consume_skb_irq(lp->tx_skbuff[entry]);
lp->tx_skbuff[entry] = NULL;
}
dirty_tx++;
}
lp->dirty_tx = dirty_tx;
outw(AckIntr | DownComplete, ioaddr + EL3_CMD); if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
lp->tx_full = 0;
netif_wake_queue(dev);
}
} #ifdef VORTEX_BUS_MASTER if (status & DMADone) {
outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */
netif_wake_queue(dev);
} #endif if (status & UpComplete) {
boomerang_rx(dev);
outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
} if (status & (AdapterFailure | RxEarly | StatsFull)) { /* Handle all uncommon interrupts at once. */ if (status & RxEarly) { /* Rx early is unused. */
corkscrew_rx(dev);
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
} if (status & StatsFull) { /* Empty statistics. */ staticint DoneDidThat; if (corkscrew_debug > 4)
pr_debug("%s: Updating stats.\n", dev->name);
update_stats(ioaddr, dev); /* DEBUG HACK: Disable statistics as an interrupt source. */ /* This occurs when we have the wrong media type! */ if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { int win, reg;
pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
dev->name); for (win = 0; win < 8; win++) {
EL3WINDOW(win);
pr_notice("Vortex window %d:", win); for (reg = 0; reg < 16; reg++)
pr_cont(" %2.2x", inb(ioaddr + reg));
pr_cont("\n");
}
EL3WINDOW(7);
outw(SetIntrEnb | TxAvailable |
RxComplete | AdapterFailure |
UpComplete | DownComplete |
TxComplete, ioaddr + EL3_CMD);
DoneDidThat++;
}
} if (status & AdapterFailure) { /* Adapter failure requires Rx reset and reinit. */
outw(RxReset, ioaddr + EL3_CMD); /* Set the Rx filter to the current state. */
set_rx_mode(dev);
outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
outw(AckIntr | AdapterFailure,
ioaddr + EL3_CMD);
}
}
if (--i < 0) {
pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); /* Disable all pending interrupts. */
outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); break;
} /* Acknowledge the IRQ. */
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
if (corkscrew_debug > 4)
pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status); return IRQ_HANDLED;
}
staticint corkscrew_rx(struct net_device *dev)
{ int ioaddr = dev->base_addr; int i; short rx_status;
if (corkscrew_debug > 5)
pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); while ((rx_status = inw(ioaddr + RxStatus)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ unsignedchar rx_error = inb(ioaddr + RxErrors); if (corkscrew_debug > 2)
pr_debug(" Rx error: status %2.2x.\n",
rx_error);
dev->stats.rx_errors++; if (rx_error & 0x01)
dev->stats.rx_over_errors++; if (rx_error & 0x02)
dev->stats.rx_length_errors++; if (rx_error & 0x04)
dev->stats.rx_frame_errors++; if (rx_error & 0x08)
dev->stats.rx_crc_errors++; if (rx_error & 0x10)
dev->stats.rx_length_errors++;
} else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; struct sk_buff *skb;
skb = netdev_alloc_skb(dev, pkt_len + 5 + 2); if (corkscrew_debug > 4)
pr_debug("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status); if (skb != NULL) {
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */
insl(ioaddr + RX_FIFO,
skb_put(skb, pkt_len),
(pkt_len + 3) >> 2);
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len; /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; continue;
} elseif (corkscrew_debug)
pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
}
outw(RxDiscard, ioaddr + EL3_CMD);
dev->stats.rx_dropped++; /* Wait a limited time to skip this packet. */ for (i = 200; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break;
} return 0;
}
staticint boomerang_rx(struct net_device *dev)
{ struct corkscrew_private *vp = netdev_priv(dev); int entry = vp->cur_rx % RX_RING_SIZE; int ioaddr = dev->base_addr; int rx_status;
if (corkscrew_debug > 5)
pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { if (rx_status & RxDError) { /* Error, update stats. */ unsignedchar rx_error = rx_status >> 16; if (corkscrew_debug > 2)
pr_debug(" Rx error: status %2.2x.\n",
rx_error);
dev->stats.rx_errors++; if (rx_error & 0x01)
dev->stats.rx_over_errors++; if (rx_error & 0x02)
dev->stats.rx_length_errors++; if (rx_error & 0x04)
dev->stats.rx_frame_errors++; if (rx_error & 0x08)
dev->stats.rx_crc_errors++; if (rx_error & 0x10)
dev->stats.rx_length_errors++;
} else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; struct sk_buff *skb;
dev->stats.rx_bytes += pkt_len; if (corkscrew_debug > 4)
pr_debug("Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak &&
(skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */
skb_put_data(skb,
isa_bus_to_virt(vp->rx_ring[entry].addr),
pkt_len);
rx_copy++;
} else { void *temp; /* Pass up the skbuff already on the Rx ring. */
skb = vp->rx_skbuff[entry];
vp->rx_skbuff[entry] = NULL;
temp = skb_put(skb, pkt_len); /* Remove this checking code for final release. */ if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
dev->name,
isa_bus_to_virt(vp->rx_ring[entry].addr),
skb->head, temp);
rx_nocopy++;
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
}
entry = (++vp->cur_rx) % RX_RING_SIZE;
} /* Refill the Rx ring buffers. */ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { struct sk_buff *skb;
entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) {
skb = netdev_alloc_skb(dev, PKT_BUF_SZ); if (skb == NULL) break; /* Bad news! */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
vp->rx_skbuff[entry] = skb;
}
vp->rx_ring[entry].status = 0; /* Clear complete bit. */
} return 0;
}
staticint corkscrew_close(struct net_device *dev)
{ struct corkscrew_private *vp = netdev_priv(dev); int ioaddr = dev->base_addr; int i;
netif_stop_queue(dev);
if (corkscrew_debug > 1) {
pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
dev->name, inw(ioaddr + EL3_STATUS),
inb(ioaddr + TxStatus));
pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
dev->name, rx_nocopy, rx_copy, queued_packet);
}
timer_delete_sync(&vp->timer);
/* 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 == XCVR_10base2) /* Turn off thinnet power. Green! */
outw(StopCoax, ioaddr + EL3_CMD);
free_irq(dev->irq, dev);
outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
update_stats(ioaddr, dev); if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
outl(0, ioaddr + UpListPtr); for (i = 0; i < RX_RING_SIZE; i++) if (vp->rx_skbuff[i]) {
dev_kfree_skb(vp->rx_skbuff[i]);
vp->rx_skbuff[i] = NULL;
}
} if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
outl(0, ioaddr + DownListPtr); for (i = 0; i < TX_RING_SIZE; i++) if (vp->tx_skbuff[i]) {
dev_kfree_skb(vp->tx_skbuff[i]);
vp->tx_skbuff[i] = NULL;
}
}
/* Update statistics. Unlike with the EL3 we need not worry about interrupts changing the window setting from underneath us, but we must still guard against a race condition with a StatsUpdate interrupt updating the table. This is done by checking that the ASM (!) code generated uses atomic updates with '+='.
*/ staticvoid update_stats(int ioaddr, struct net_device *dev)
{ /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ /* Switch to the stats window, and read everything. */
EL3WINDOW(6);
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); /* Multiple collisions. */ inb(ioaddr + 2);
dev->stats.collisions += inb(ioaddr + 3);
dev->stats.tx_window_errors += inb(ioaddr + 4);
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
dev->stats.tx_packets += inb(ioaddr + 6);
dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */ /* Tx deferrals */ inb(ioaddr + 8); /* Don't bother with register 9, an extension of registers 6&7. If we do use the 6&7 values the atomic update assumption above
is invalid. */
inw(ioaddr + 10); /* Total Rx and Tx octets. */
inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */
EL3WINDOW(4);
inb(ioaddr + 12);
/* We change back to window 7 (not 1) with the Vortex. */
EL3WINDOW(7);
}
/* This new version of set_rx_mode() supports v1.4 kernels. The Vortex chip has no documented multicast filter, so the only multicast setting is to receive all multicast frames. At least
the chip has a very clean way to set the mode, unlike many others. */ staticvoid set_rx_mode(struct net_device *dev)
{ int ioaddr = dev->base_addr; unsignedshort new_mode;
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.