/* "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/OforVortexcards.Full-bus-mastertransfersarealways enabledbydefaultonBoomerangcards.IfVORTEX_BUS_MASTERisdefined,
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 aliasedregistersat<base>+0x400.
*/ #define CORKSCREW_TOTAL_SIZE 0x20
/* The top five bits written to EL3_CMD are a command, the lower 11bitsaretheparameter,ifapplicable. Notethat11parametersbitswasfineforethernet,butthenewchips canhandleFDDIlengthframes(~4500octets)andnowparameterscount
32-bit 'Dwords' rather than octets. */
/* The Rx and Tx descriptor lists. CautionAlphahackers:thesetypesare32bits!Notealsothe8byte
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. Notethatwedeviatefromthe3Comorderbychecking10base2beforeAUI.
*/ 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},
};
/* Unfortunatelymaximizingthesharedcodebetweentheintegratedand moduleversionofthedriverresultsinacomplicatedsetofinitialization procedures. init_module()--modules/tc59x_init()--built-in Thewrappersforcorkscrew_scan() corkscrew_scan()ThecommonroutinethatscansforPCIandEISAcards corkscrew_found_device()Allocateadevicestructurewhenwefindacard. Differentversionsexistformodulesandbuilt-in. corkscrew_probe1()Fillinthedevicestructure--thisisseparated sothatthemodulescodecanputitindev->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")) return0; /* Check the resource configuration for a matching ioaddr. */ if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
release_region(ioaddr, CORKSCREW_TOTAL_SIZE); return0;
} /* 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); return0;
} return1;
}
/* 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) { case0: case4: case5: /* 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. Ignoreasingleearlyinterrupt,butdon'thangthemachinefor
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;
} return0;
}
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. */
} return0;
}
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. UnlikewiththeEL3weneednotworryaboutinterruptschanging thewindowsettingfromunderneathus,butwemuststillguard againstaraceconditionwithaStatsUpdateinterruptupdatingthe table.ThisisdonebycheckingthattheASM(!)codegenerateduses atomicupdateswith'+='.
*/ 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. Ifwedousethe6&7valuestheatomicupdateassumptionabove
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. TheVortexchiphasnodocumentedmulticastfilter,sotheonly multicastsettingistoreceiveallmulticastframes.Atleast
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.