Annapolis MD 21403
- 2000/2/2- Added support for kernel-level ISAPnP
+ 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@redhat.com)
*/
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1512 effectively disables this feature. */
-static const int rx_copybreak = 200;
+static int rx_copybreak = 200;
/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
static const int mtu = 1500;
#define RX_RING_SIZE 16
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/isapnp.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ethtool.h>
+#include <linux/bitops.h>
#include <asm/uaccess.h>
-#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
MODULE_LICENSE("GPL");
-
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(max_interrupt_work, "i");
-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");
+MODULE_VERSION(DRV_VERSION);
/* "Knobs" for adjusting internal parameters. */
/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
- DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
- TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
- AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
+ DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
+ TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
+ AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
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},
+} 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},
};
#endif /* __ISAPNP__ */
static struct net_device *corkscrew_scan(int unit);
-static void corkscrew_setup(struct net_device *dev, int ioaddr,
+static int corkscrew_setup(struct net_device *dev, int ioaddr,
struct pnp_dev *idev, int card_number);
static int corkscrew_open(struct net_device *dev);
static void corkscrew_timer(unsigned long arg);
static int corkscrew_rx(struct net_device *dev);
static void corkscrew_timeout(struct net_device *dev);
static int boomerang_rx(struct net_device *dev);
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id,
- struct pt_regs *regs);
+static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
static int corkscrew_close(struct net_device *dev);
static void update_stats(int addr, struct net_device *dev);
static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
static void set_rx_mode(struct net_device *dev);
-static struct ethtool_ops netdev_ethtool_ops;
-\f
+static const struct ethtool_ops netdev_ethtool_ops;
-/*
+
+/*
Unfortunately maximizing the shared code between the integrated and
module version of the driver results in a complicated set of initialization
procedures.
#ifdef MODULE
static int 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);
static void cleanup_card(struct net_device *dev)
{
- struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
list_del_init(&vp->list);
if (dev->dma)
free_dma(dev->dma);
printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
- corkscrew_setup(dev, ioaddr, idev, cards_found++);
SET_NETDEV_DEV(dev, &idev->dev);
pnp_cards++;
- err = register_netdev(dev);
+ err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
if (!err)
return dev;
cleanup_card(dev);
printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
- corkscrew_setup(dev, ioaddr, NULL, cards_found++);
- err = register_netdev(dev);
+ err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
if (!err)
return dev;
cleanup_card(dev);
return NULL;
}
-static void corkscrew_setup(struct net_device *dev, int ioaddr,
+static int corkscrew_setup(struct net_device *dev, int ioaddr,
struct pnp_dev *idev, int card_number)
{
- struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
int irq;
printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
spin_lock_init(&vp->lock);
-
+
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
for (i = 0; i < 0x18; i++) {
dev->get_stats = &corkscrew_get_stats;
dev->set_multicast_list = &set_rx_mode;
dev->ethtool_ops = &netdev_ethtool_ops;
+
+ return register_netdev(dev);
}
-\f
+
static int corkscrew_open(struct net_device *dev)
{
int ioaddr = dev->base_addr;
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
union wn3_config config;
int i;
} else if (vp->autoselect) {
/* Find first available media type, starting with 100baseTx. */
dev->if_port = 4;
- while (!(vp->available_media & media_tbl[dev->if_port].mask))
+ while (!(vp->available_media & media_tbl[dev->if_port].mask))
dev->if_port = media_tbl[dev->if_port].next;
if (corkscrew_debug > 1)
vp->product_name, dev)) return -EAGAIN;
enable_dma(dev->dma);
set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- } else if (request_irq(dev->irq, &corkscrew_interrupt, SA_SHIRQ,
+ } else if (request_irq(dev->irq, &corkscrew_interrupt, IRQF_SHARED,
vp->product_name, dev)) {
return -EAGAIN;
}
break; /* Bad news! */
skb->dev = dev; /* Mark as being used by this device. */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[i].addr = isa_virt_to_bus(skb->tail);
+ vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
}
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);
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] = 0;
+ vp->tx_skbuff[i] = NULL;
outl(0, ioaddr + DownListPtr);
}
/* Set receiver mode: presumably accept b-case and phys addr only. */
{
#ifdef AUTOMEDIA
struct net_device *dev = (struct net_device *) data;
- struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int ioaddr = dev->base_addr;
unsigned long flags;
int ok = 0;
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;
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)
}
EL3WINDOW(old_window);
}
-
+
spin_unlock_irqrestore(&vp->lock, flags);
if (corkscrew_debug > 1)
printk("%s: Media selection timer finished, %s.\n",
static void corkscrew_timeout(struct net_device *dev)
{
int i;
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int ioaddr = dev->base_addr;
printk(KERN_WARNING
static int corkscrew_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int ioaddr = dev->base_addr;
/* Block a timer-based transmit from overlapping. */
/* Calculate the next Tx descriptor entry. */
int entry = vp->cur_tx % TX_RING_SIZE;
struct boom_tx_desc *prev_entry;
- unsigned long flags, i;
+ unsigned long flags;
+ int i;
if (vp->tx_full) /* No room to transmit with */
return 1;
outw(DownStall, ioaddr + EL3_CMD);
/* Wait for the stall to complete. */
for (i = 20; i >= 0; i--)
- if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
+ if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
break;
if (prev_entry)
prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
int j;
outw(TxReset, ioaddr + EL3_CMD);
for (j = 20; j >= 0; j--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
}
outw(TxEnable, ioaddr + EL3_CMD);
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
{
/* Use the now-standard shared IRQ implementation. */
struct net_device *dev = dev_id;
- struct corkscrew_private *lp;
+ struct corkscrew_private *lp = netdev_priv(dev);
int ioaddr, status;
int latency;
int i = max_interrupt_work;
ioaddr = dev->base_addr;
latency = inb(ioaddr + Timer);
- lp = (struct corkscrew_private *) dev->priv;
spin_lock(&lp->lock);
-
+
status = inw(ioaddr + EL3_STATUS);
if (corkscrew_debug > 4)
break; /* It still hasn't been processed. */
if (lp->tx_skbuff[entry]) {
dev_kfree_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = 0;
+ lp->tx_skbuff[entry] = NULL;
}
dirty_tx++;
}
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
-
+
spin_unlock(&lp->lock);
if (corkscrew_debug > 4)
static int corkscrew_rx(struct net_device *dev)
{
- struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int ioaddr = dev->base_addr;
int i;
short rx_status;
vp->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))
+ if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
continue;
} else if (corkscrew_debug)
static int boomerang_rx(struct net_device *dev)
{
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int entry = vp->cur_rx % RX_RING_SIZE;
int ioaddr = dev->base_addr;
int rx_status;
break; /* Bad news! */
skb->dev = dev; /* Mark as being used by this device. */
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[entry].addr = isa_virt_to_bus(skb->tail);
+ 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. */
static int corkscrew_close(struct net_device *dev)
{
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
int ioaddr = dev->base_addr;
int i;
for (i = 0; i < RX_RING_SIZE; i++)
if (vp->rx_skbuff[i]) {
dev_kfree_skb(vp->rx_skbuff[i]);
- vp->rx_skbuff[i] = 0;
+ vp->rx_skbuff[i] = NULL;
}
}
if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
for (i = 0; i < TX_RING_SIZE; i++)
if (vp->tx_skbuff[i]) {
dev_kfree_skb(vp->tx_skbuff[i]);
- vp->tx_skbuff[i] = 0;
+ vp->tx_skbuff[i] = NULL;
}
}
static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
{
- struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
unsigned long flags;
if (netif_running(dev)) {
*/
static void update_stats(int ioaddr, struct net_device *dev)
{
- struct corkscrew_private *vp =
- (struct corkscrew_private *) dev->priv;
+ struct corkscrew_private *vp = netdev_priv(dev);
/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
/* Switch to the stats window, and read everything. */
corkscrew_debug = level;
}
-static struct ethtool_ops netdev_ethtool_ops = {
+static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
.get_msglevel = netdev_get_msglevel,
.set_msglevel = netdev_set_msglevel,
};
-\f
+
#ifdef MODULE
void cleanup_module(void)
{
}
}
#endif /* MODULE */
-\f
+
/*
* Local variables:
* compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c515.c"