Commit 6a6602c0 authored by Don Fry's avatar Don Fry Committed by Jeff Garzik

[PATCH] pcnet32.c handle failures in open

If there are errors in the open routine, the driver does not correctly clean
up and free resources.

This also removes an unnecessary netif_wake_queue, correctly supports the
debug parameter, and updates the version and date.
parent 30a43fcd
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
*************************************************************************/ *************************************************************************/
#define DRV_NAME "pcnet32" #define DRV_NAME "pcnet32"
#define DRV_VERSION "1.27b" #define DRV_VERSION "1.28"
#define DRV_RELDATE "01.10.2002" #define DRV_RELDATE "02.20.2004"
#define PFX DRV_NAME ": " #define PFX DRV_NAME ": "
static const char *version = static const char *version =
...@@ -219,6 +219,11 @@ static int full_duplex[MAX_UNITS]; ...@@ -219,6 +219,11 @@ static int full_duplex[MAX_UNITS];
* clean up and using new mii module * clean up and using new mii module
* v1.27b Sep 30 2002 Kent Yoder <yoder1@us.ibm.com> * v1.27b Sep 30 2002 Kent Yoder <yoder1@us.ibm.com>
* Added timer for cable connection state changes. * Added timer for cable connection state changes.
* v1.28 20 Feb 2004 Don Fry <brazilnut@us.ibm.com>
* Jon Lewis <jonmason@us.ibm.com>, Chinmay Albal <albal@in.ibm.com>
* Now uses ethtool_ops, netif_msg_* and generic_mii_ioctl.
* Fixes bogus 'Bus master arbitration failure', pci_[un]map_single
* length errors, and transmit hangs. Cleans up after errors in open.
*/ */
...@@ -650,8 +655,10 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, ...@@ -650,8 +655,10 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16); chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16);
if (pcnet32_debug & NETIF_MSG_PROBE) if (pcnet32_debug & NETIF_MSG_PROBE)
printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version); printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version);
if ((chip_version & 0xfff) != 0x003) if ((chip_version & 0xfff) != 0x003) {
printk(KERN_INFO PFX "Unsupported chip version.\n");
goto err_release_region; goto err_release_region;
}
/* initialize variables */ /* initialize variables */
fdx = mii = fset = dxsuflo = ltint = 0; fdx = mii = fset = dxsuflo = ltint = 0;
...@@ -733,6 +740,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, ...@@ -733,6 +740,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
dev = alloc_etherdev(0); dev = alloc_etherdev(0);
if(!dev) { if(!dev) {
printk(KERN_ERR PFX "Memory allocation failed.\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_release_region; goto err_release_region;
} }
...@@ -806,6 +814,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, ...@@ -806,6 +814,7 @@ pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared,
dev->base_addr = ioaddr; dev->base_addr = ioaddr;
/* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */
if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) { if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) {
printk(KERN_ERR PFX "Consistent memory allocation failed.\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_free_netdev; goto err_free_netdev;
} }
...@@ -939,6 +948,7 @@ pcnet32_open(struct net_device *dev) ...@@ -939,6 +948,7 @@ pcnet32_open(struct net_device *dev)
unsigned long ioaddr = dev->base_addr; unsigned long ioaddr = dev->base_addr;
u16 val; u16 val;
int i; int i;
int rc;
if (dev->irq == 0 || if (dev->irq == 0 ||
request_irq(dev->irq, &pcnet32_interrupt, request_irq(dev->irq, &pcnet32_interrupt,
...@@ -947,8 +957,10 @@ pcnet32_open(struct net_device *dev) ...@@ -947,8 +957,10 @@ pcnet32_open(struct net_device *dev)
} }
/* Check for a valid station address */ /* Check for a valid station address */
if( !is_valid_ether_addr(dev->dev_addr) ) if (!is_valid_ether_addr(dev->dev_addr)) {
return -EINVAL; rc = -EINVAL;
goto err_free_irq;
}
/* Reset the PCNET32 */ /* Reset the PCNET32 */
lp->a.reset (ioaddr); lp->a.reset (ioaddr);
...@@ -1022,8 +1034,10 @@ pcnet32_open(struct net_device *dev) ...@@ -1022,8 +1034,10 @@ pcnet32_open(struct net_device *dev)
lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
lp->init_block.filter[0] = 0x00000000; lp->init_block.filter[0] = 0x00000000;
lp->init_block.filter[1] = 0x00000000; lp->init_block.filter[1] = 0x00000000;
if (pcnet32_init_ring(dev)) if (pcnet32_init_ring(dev)) {
return -ENOMEM; rc = -ENOMEM;
goto err_free_ring;
}
/* Re-initialize the PCNET32, and start it when done. */ /* Re-initialize the PCNET32, and start it when done. */
lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff); lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff);
...@@ -1057,6 +1071,28 @@ pcnet32_open(struct net_device *dev) ...@@ -1057,6 +1071,28 @@ pcnet32_open(struct net_device *dev)
return 0; /* Always succeed */ return 0; /* Always succeed */
err_free_ring:
/* free any allocated skbuffs */
for (i = 0; i < RX_RING_SIZE; i++) {
lp->rx_ring[i].status = 0;
if (lp->rx_skbuff[i]) {
pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2,
PCI_DMA_FROMDEVICE);
dev_kfree_skb(lp->rx_skbuff[i]);
}
lp->rx_skbuff[i] = NULL;
lp->rx_dma_addr[i] = 0;
}
/*
* Switch back to 16bit mode to avoid problems with dumb
* DOS packet driver after a warm reboot
*/
lp->a.write_bcr (ioaddr, 20, 4);
err_free_irq:
free_irq(dev->irq, dev);
return rc;
} }
/* /*
...@@ -1125,6 +1161,7 @@ pcnet32_init_ring(struct net_device *dev) ...@@ -1125,6 +1161,7 @@ pcnet32_init_ring(struct net_device *dev)
lp->tx_ring[i].status = 0; lp->tx_ring[i].status = 0;
lp->tx_dma_addr[i] = 0; lp->tx_dma_addr[i] = 0;
} }
wmb(); /* Make sure all changes are visible */
lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
...@@ -1251,9 +1288,7 @@ pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1251,9 +1288,7 @@ pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies; dev->trans_start = jiffies;
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base != 0) {
netif_wake_queue(dev);
else {
lp->tx_full = 1; lp->tx_full = 1;
netif_stop_queue(dev); netif_stop_queue(dev);
} }
...@@ -1509,6 +1544,7 @@ pcnet32_rx(struct net_device *dev) ...@@ -1509,6 +1544,7 @@ pcnet32_rx(struct net_device *dev)
* of QNX reports that some revs of the 79C965 clear it. * of QNX reports that some revs of the 79C965 clear it.
*/ */
lp->rx_ring[entry].buf_length = le16_to_cpu(2-PKT_BUF_SZ); lp->rx_ring[entry].buf_length = le16_to_cpu(2-PKT_BUF_SZ);
wmb(); /* Make sure owner changes after all others are visible */
lp->rx_ring[entry].status |= le16_to_cpu(0x8000); lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
entry = (++lp->cur_rx) & RX_RING_MOD_MASK; entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
} }
...@@ -1725,7 +1761,7 @@ static struct pci_driver pcnet32_driver = { ...@@ -1725,7 +1761,7 @@ static struct pci_driver pcnet32_driver = {
}; };
MODULE_PARM(debug, "i"); MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, DRV_NAME " debug level (0-6)"); MODULE_PARM_DESC(debug, DRV_NAME " debug level");
MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt"); MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt");
MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(rx_copybreak, "i");
...@@ -1752,8 +1788,8 @@ static int __init pcnet32_init_module(void) ...@@ -1752,8 +1788,8 @@ static int __init pcnet32_init_module(void)
{ {
printk(KERN_INFO "%s", version); printk(KERN_INFO "%s", version);
if (debug > 0) if (debug >= 0 && debug < (sizeof(int) - 1))
pcnet32_debug = debug; pcnet32_debug = 1 << debug;
if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) if ((tx_start_pt >= 0) && (tx_start_pt <= 3))
tx_start = tx_start_pt; tx_start = tx_start_pt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment