patch-2.4.4 linux/drivers/net/dmfe.c
Next file: linux/drivers/net/e2100.c
Previous file: linux/drivers/net/dgrs.c
Back to the patch index
Back to the overall index
- Lines: 531
- Date:
Wed Apr 25 16:14:08 2001
- Orig file:
v2.4.3/linux/drivers/net/dmfe.c
- Orig date:
Tue Mar 20 12:05:01 2001
diff -u --recursive --new-file v2.4.3/linux/drivers/net/dmfe.c linux/drivers/net/dmfe.c
@@ -57,6 +57,14 @@
Resource usage cleanups.
Report driver version to user.
+ Tobias Ringstr÷m <zajbot@goteborg.utfors.se> :
+ Added additional proper locking
+ Rewrote the transmit code to actually use the ring buffer,
+ and to generate a lot fewer interrupts.
+
+ Frank Davis <fdavis112@juno.com>
+ Added SMP-safe locking mechanisms in addition to Andrew Morton's work
+
TODO
Implement pci_driver::suspend() and pci_driver::resume()
@@ -68,7 +76,7 @@
*/
-#define DMFE_VERSION "1.30 (June 11, 2000)"
+#define DMFE_VERSION "1.30p3 (April 17, 2001)"
#include <linux/module.h>
@@ -88,12 +96,16 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
+#include <linux/spinlock.h>
#include <asm/processor.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
+static char version[] __initdata =
+KERN_INFO "Davicom DM91xx net driver, version " DMFE_VERSION "\n";
+
/* Board/System/Debug information/definition ---------------- */
@@ -108,6 +120,7 @@
#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
+#define TX_IRQ_THR 12
#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
@@ -187,7 +200,7 @@
u32 chip_id; /* Chip vendor/Device ID */
u32 chip_revision; /* Chip revision */
struct net_device *next_dev; /* next device */
-
+ spinlock_t lock; /* Spinlock */
struct pci_dev *net_dev; /* PCI device */
unsigned long ioaddr; /* I/O base address */
@@ -207,8 +220,9 @@
struct rx_desc *first_rx_desc;
struct rx_desc *rx_insert_ptr;
struct rx_desc *rx_ready_ptr; /* packet come pointer */
- u32 tx_packet_cnt; /* transmitted packet count */
- u32 tx_queue_cnt; /* wait to send packet count */
+ int tx_int_pkt_num; /* number of packets to transmit until
+ * a transmit interrupt is requested */
+ u32 tx_live_cnt; /* number of used/live tx slots */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
@@ -220,7 +234,7 @@
u8 link_failed; /* Ever link failed */
u8 wait_reset; /* Hardware failed, need to reset */
u8 in_reset_state; /* Now driver in reset routine */
- u8 rx_error_cnt; /* recievd abnormal case count */
+ u8 rx_error_cnt; /* recieved abnormal case count */
u8 dm910x_chk_mode; /* Operating mode check */
struct timer_list timer;
struct net_device_stats stats; /* statistic counter */
@@ -240,13 +254,13 @@
};
/* Global variable declaration ----------------------------- */
-static int dmfe_debug = 0;
+static int dmfe_debug;
static unsigned char dmfe_media_mode = 8;
-static u32 dmfe_cr6_user_set = 0;
+static u32 dmfe_cr6_user_set;
/* For module input parameter */
-static int debug = 0;
-static u32 cr6set = 0;
+static int debug;
+static u32 cr6set;
static unsigned char mode = 8;
static u8 chkmode = 1;
@@ -364,6 +378,13 @@
u32 dev_rev;
u16 pci_command;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
DMFE_DBUG(0, "dmfe_probe()", 0);
/* Enable Master/IO access, Disable memory access */
@@ -380,14 +401,14 @@
/* Interrupt check */
if (pci_irqline == 0) {
- printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n",
- pci_irqline);
+ printk(KERN_ERR "dmfe(%s): Interrupt wrong : IRQ=%d\n",
+ pdev->slot_name, pci_irqline);
goto err_out;
}
/* iobase check */
- if (pci_iobase == 0) {
- printk(KERN_ERR "dmfe: I/O base is zero\n");
+ if (pci_iobase == 0 || pci_resource_len(pdev, 0) == 0) {
+ printk(KERN_ERR "dmfe(%s): I/O base is zero\n", pdev->slot_name);
goto err_out;
}
@@ -404,20 +425,17 @@
pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev);
/* Init network device */
- dev = init_etherdev(NULL, sizeof(*db));
+ dev = alloc_etherdev(sizeof(*db));
if (dev == NULL)
goto err_out;
SET_MODULE_OWNER(dev);
- /* IO range check */
- if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) {
- printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n",
- pci_iobase, CHK_IO_SIZE(pdev, dev_rev));
+ if (pci_request_regions(pdev, "dmfe"))
goto err_out_netdev;
- }
db = dev->priv;
pci_set_drvdata(pdev, dev);
+ spin_lock_init(&db->lock);
db->chip_id = ent->driver_data;
db->ioaddr = pci_iobase;
@@ -442,10 +460,23 @@
for (i = 0; i < 6; i++)
dev->dev_addr[i] = db->srom[20 + i];
+ i = register_netdev(dev);
+ if (i)
+ goto err_out_res;
+
+ printk(KERN_INFO "%s: Davicom DM%04lx at 0x%lx,",
+ dev->name,
+ ent->driver_data >> 16,
+ pci_iobase);
+ for (i = 0; i < 6; i++)
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ printk(", IRQ %d\n", pci_irqline);
+
return 0;
+err_out_res:
+ pci_release_regions(pdev);
err_out_netdev:
- unregister_netdev(dev);
kfree(dev);
err_out:
return -ENODEV;
@@ -462,7 +493,7 @@
db = dev->priv;
unregister_netdev(dev);
- release_region(dev->base_addr, CHK_IO_SIZE(pdev, db->chip_revision));
+ pci_release_regions(pdev);
kfree(dev); /* free board information */
pci_set_drvdata(pdev, NULL);
@@ -509,8 +540,8 @@
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
- db->tx_packet_cnt = 0;
- db->tx_queue_cnt = 0;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ db->tx_live_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
@@ -552,7 +583,7 @@
{
struct dmfe_board_info *db = dev->priv;
u32 ioaddr = db->ioaddr;
-
+
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x board : need 32 PCI clock to complete */
@@ -611,46 +642,41 @@
{
struct dmfe_board_info *db = dev->priv;
struct tx_desc *txptr;
+ unsigned long flags;
DMFE_DBUG(0, "dmfe_start_xmit", 0);
-
- netif_stop_queue(dev);
+ spin_lock_irqsave(&db->lock, flags);
- /* Too large packet check */
- if (skb->len > MAX_PACKET_SIZE) {
- printk(KERN_ERR "%s: oversized frame (%d bytes) for transmit.\n", dev->name, (u16) skb->len);
- dev_kfree_skb(skb);
- return 0;
- }
- /* No Tx resource check, it never happen nromally */
- if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
- return 1;
- }
-
/* transmit this packet */
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
- txptr->tdes1 = 0xe1000000 | skb->len;
+ if (--db->tx_int_pkt_num < 0)
+ {
+ txptr->tdes1 = 0xe1000000 | skb->len;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ }
+ else
+ txptr->tdes1 = 0x61000000 | skb->len;
+
+ /* Transmit Packet Process */
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ dev->trans_start = jiffies; /* saved the time stamp */
/* Point to next transmit free descriptor */
- db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+ txptr = (struct tx_desc *)txptr->next_tx_desc;
- /* Transmit Packet Process */
- if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
- db->tx_packet_cnt++; /* Ready to send count */
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- } else {
- db->tx_queue_cnt++; /* queue the tx packet */
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- }
+ if (txptr->tdes0 & 0x80000000)
+ netif_stop_queue(dev);
- /* Tx resource check */
- if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
- netif_wake_queue(dev);
+ db->tx_insert_ptr = txptr;
+ db->tx_live_cnt++;
+
+ spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
dev_kfree_skb(skb);
+
return 0;
}
@@ -674,7 +700,7 @@
/* deleted timer */
del_timer_sync(&db->timer);
-
+
/* free interrupt */
free_irq(dev->irq, dev);
@@ -699,6 +725,7 @@
struct tx_desc *txptr;
struct dmfe_board_info *db;
u32 ioaddr;
+ unsigned long flags;
if (!dev) {
DMFE_DBUG(1, "dmfe_interrupt() without device arg", 0);
@@ -710,6 +737,8 @@
DMFE_DBUG(0, "dmfe_interrupt()", 0);
+ spin_lock_irqsave(&db->lock, flags);
+
/* Disable all interrupt in CR7 to solve the interrupt edge problem */
outl(0, ioaddr + DCR7);
@@ -725,14 +754,15 @@
netif_stop_queue(dev);
db->wait_reset = 1; /* Need to RESET */
outl(0, ioaddr + DCR7); /* disable all interrupt */
+ spin_unlock_irqrestore(&db->lock, flags);
return;
}
+
/* Free the transmitted descriptor */
txptr = db->tx_remove_ptr;
- while (db->tx_packet_cnt) {
+ while (db->tx_live_cnt > 0 && (txptr->tdes0 & 0x80000000) == 0)
+ {
/* printk("tdes0=%x\n", txptr->tdes0); */
- if (txptr->tdes0 & 0x80000000)
- break;
db->stats.tx_packets++;
if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
@@ -748,21 +778,12 @@
db->stats.tx_errors++;
}
txptr = (struct tx_desc *) txptr->next_tx_desc;
- db->tx_packet_cnt--;
+ db->tx_live_cnt--;
}
/* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
- /* Send the Tx packet in queue */
- if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
- db->tx_packet_cnt++; /* Ready to send count */
- outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
- dev->trans_start = jiffies; /* saved the time stamp */
- db->tx_queue_cnt--;
- }
- /* Resource available check */
- if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
+ if ((db->tx_insert_ptr->tdes0 & 0x80000000) == 0)
netif_wake_queue(dev);
/* Received the coming packet */
@@ -786,6 +807,8 @@
else
db->cr7_data = 0x1a2cd;
outl(db->cr7_data, ioaddr + DCR7);
+
+ spin_unlock_irqrestore(&db->lock, flags);
}
/*
@@ -877,19 +900,23 @@
static void dmfe_set_filter_mode(struct net_device *dev)
{
struct dmfe_board_info *db = dev->priv;
+ unsigned long flags;
DMFE_DBUG(0, "dmfe_set_filter_mode()", 0);
+ spin_lock_irqsave(&db->lock, flags);
if (dev->flags & IFF_PROMISC) {
DMFE_DBUG(0, "Enable PROM Mode", 0);
db->cr6_data |= CR6_PM | CR6_PBF;
update_cr6(db->cr6_data, db->ioaddr);
+ spin_unlock_irqrestore(&db->lock, flags);
return;
}
if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) {
DMFE_DBUG(0, "Pass all multicast address", dev->mc_count);
db->cr6_data &= ~(CR6_PM | CR6_PBF);
db->cr6_data |= CR6_PAM;
+ spin_unlock_irqrestore(&db->lock, flags);
return;
}
DMFE_DBUG(0, "Set multicast address", dev->mc_count);
@@ -897,11 +924,17 @@
dm9132_id_table(dev, dev->mc_count); /* DM9132 */
else
send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
+ spin_unlock_irqrestore(&db->lock, flags);
}
/*
Process the upper socket ioctl command
*/
+
+/*
+ * The following function just returns 0. Shouldn't it do more?
+ */
+
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
@@ -918,12 +951,16 @@
unsigned char tmp_cr12;
struct net_device *dev = (struct net_device *) data;
struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
+ unsigned long flags;
DMFE_DBUG(0, "dmfe_timer()", 0);
+ spin_lock_irqsave(&db->lock, flags);
/* Do reset now */
- if (db->in_reset_state)
+ if (db->in_reset_state) {
+ spin_unlock_irqrestore(&db->lock, flags);
return;
+ }
/* Operating Mode Check */
if ((db->dm910x_chk_mode & 0x1) && (db->stats.rx_packets > MAX_CHECK_PACKET)) {
@@ -944,15 +981,18 @@
db->interval_rx_cnt = 0;
- if (db->wait_reset | (db->tx_packet_cnt &&
- ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
+ if (db->wait_reset ||
+ (db->tx_live_cnt > 0 &&
+ ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) ||
+ (db->rx_error_cnt > 3)) {
/*
printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
*/
- DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+ DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx monitor step1", 0);
dmfe_dynamic_reset(dev);
db->timer.expires = jiffies + DMFE_TIMER_WUT;
add_timer(&db->timer);
+ spin_unlock_irqrestore(&db->lock, flags);
return;
}
db->rx_error_cnt = 0; /* Clear previos counter */
@@ -1008,6 +1048,7 @@
/* Timer active again */
db->timer.expires = jiffies + DMFE_TIMER_WUT;
add_timer(&db->timer);
+ spin_unlock_irqrestore(&db->lock, flags);
}
/*
@@ -1036,8 +1077,8 @@
dmfe_free_rxbuffer(db);
/* system variable init */
- db->tx_packet_cnt = 0;
- db->tx_queue_cnt = 0;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ db->tx_live_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
@@ -1207,11 +1248,17 @@
u32 *suptr;
int i;
- DMFE_DBUG(0, "send_filetr_frame()", 0);
+ DMFE_DBUG(0, "send_filter_frame()", 0);
txptr = db->tx_insert_ptr;
suptr = (u32 *) txptr->tx_buf_ptr;
+ if (txptr->tdes0 & 0x80000000) {
+ printk(KERN_WARNING "%s: Too busy to send filter frame\n",
+ dev->name);
+ return;
+ }
+
/* Node address */
addrptr = (u16 *) dev->dev_addr;
*suptr++ = addrptr[0];
@@ -1231,26 +1278,20 @@
*suptr++ = addrptr[2];
}
- for (; i < 14; i++) {
+ for (i=0; i < 14; i++)
*suptr++ = 0xffff;
*suptr++ = 0xffff;
*suptr++ = 0xffff;
- }
+
/* prepare the setup frame */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+ db->tx_live_cnt++;
txptr->tdes1 = 0x890000c0;
- /* Resource Check and Send the setup packet */
- if (!db->tx_packet_cnt) {
- /* Resource Empty */
- db->tx_packet_cnt++;
- txptr->tdes0 = 0x80000000;
- update_cr6(db->cr6_data | 0x2000, dev->base_addr);
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- update_cr6(db->cr6_data, dev->base_addr);
- } else {
- /* Put into TX queue */
- db->tx_queue_cnt++;
- }
+ /* Send the setup packet */
+ txptr->tdes0 = 0x80000000;
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ update_cr6(db->cr6_data, dev->base_addr);
}
/*
@@ -1593,6 +1634,11 @@
{
int rc;
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
+
DMFE_DBUG(0, "init_module() ", debug);
if (debug)
@@ -1616,8 +1662,6 @@
if (rc < 0)
return rc;
- printk (KERN_INFO "Davicom DM91xx net driver loaded, version "
- DMFE_VERSION "\n");
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)