PATCH: fix up nmclan locking and hang on eject at wrong moment

From: Alan Cox (alan@lxorguk.ukuu.org.uk)
Date: Fri Jul 11 2003 - 13:06:46 EST


[Not tested as I no longer have the card]
diff -u --new-file --recursive --exclude-from /usr/src/exclude linux-2.5.75/drivers/net/pcmcia/nmclan_cs.c linux-2.5.75-ac1/drivers/net/pcmcia/nmclan_cs.c
--- linux-2.5.75/drivers/net/pcmcia/nmclan_cs.c 2003-07-10 21:04:57.000000000 +0100
+++ linux-2.5.75-ac1/drivers/net/pcmcia/nmclan_cs.c 2003-07-11 15:58:34.000000000 +0100
@@ -8,6 +8,7 @@
 
 Written by Roger C. Pao <rpao@paonet.org>
   Copyright 1995 Roger C. Pao
+ Linux 2.5 cleanups Copyright Red Hat 2003
 
   This software may be used and distributed according to the terms of
   the GNU General Public License.
@@ -68,6 +69,10 @@
 History
 -------------------------------------------------------------------------------
 Log: nmclan_cs.c,v
+ * 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com>
+ * Fixed hang on card eject as we probe it
+ * Cleaned up to use new style locking.
+ *
  * Revision 0.16 1995/07/01 06:42:17 rpao
  * Bug fix: nmclan_reset() called CardServices incorrectly.
  *
@@ -369,6 +374,8 @@
 
     char tx_free_frames; /* Number of free transmit frame buffers */
     char tx_irq_disabled; /* MACE TX interrupt disabled */
+
+ spinlock_t bank_lock; /* Must be held if you step off bank 0 */
 } mace_private;
 
 /* ----------------------------------------------------------------------------
@@ -482,6 +489,7 @@
     link = &lp->link;
     link->priv = dev;
     
+ spin_lock_init(&lp->bank_lock);
     init_timer(&link->release);
     link->release.function = &nmclan_release;
     link->release.data = (u_long)link;
@@ -561,7 +569,7 @@
     if (*linkp == NULL)
         return;
 
- del_timer(&link->release);
+ del_timer_sync(&link->release);
     if (link->state & DEV_CONFIG) {
         nmclan_release((u_long)link);
         if (link->state & DEV_STALE_CONFIG) {
@@ -588,7 +596,7 @@
         assuming that during normal operation, the MACE is always in
         bank 0.
 ---------------------------------------------------------------------------- */
-static int mace_read(ioaddr_t ioaddr, int reg)
+static int mace_read(mace_private *lp, ioaddr_t ioaddr, int reg)
 {
   int data = 0xFF;
   unsigned long flags;
@@ -598,12 +606,11 @@
       data = inb(ioaddr + AM2150_MACE_BASE + reg);
       break;
     case 1: /* register 16-31 */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&lp->bank_lock, flags);
       MACEBANK(1);
       data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
       MACEBANK(0);
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->bank_lock, flags);
       break;
   }
   return (data & 0xFF);
@@ -616,7 +623,7 @@
         are assuming that during normal operation, the MACE is always in
         bank 0.
 ---------------------------------------------------------------------------- */
-static void mace_write(ioaddr_t ioaddr, int reg, int data)
+static void mace_write(mace_private *lp, ioaddr_t ioaddr, int reg, int data)
 {
   unsigned long flags;
 
@@ -625,12 +632,11 @@
       outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
       break;
     case 1: /* register 16-31 */
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&lp->bank_lock, flags);
       MACEBANK(1);
       outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
       MACEBANK(0);
- restore_flags(flags);
+ spin_unlock_irqrestore(&lp->bank_lock, flags);
       break;
   }
 } /* mace_write */
@@ -639,22 +645,29 @@
 mace_init
         Resets the MACE chip.
 ---------------------------------------------------------------------------- */
-static void mace_init(ioaddr_t ioaddr, char *enet_addr)
+static int mace_init(mace_private *lp, ioaddr_t ioaddr, char *enet_addr)
 {
   int i;
+ int ct = 0;
 
   /* MACE Software reset */
- mace_write(ioaddr, MACE_BIUCC, 1);
- while (mace_read(ioaddr, MACE_BIUCC) & 0x01) {
+ mace_write(lp, ioaddr, MACE_BIUCC, 1);
+ while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
     /* Wait for reset bit to be cleared automatically after <= 200ns */;
+ if(++ct > 500)
+ {
+ printk(KERN_ERR "mace: reset failed, card removed ?\n");
+ return -1;
+ }
+ udelay(1);
   }
- mace_write(ioaddr, MACE_BIUCC, 0);
+ mace_write(lp, ioaddr, MACE_BIUCC, 0);
 
   /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
- mace_write(ioaddr, MACE_FIFOCC, 0x0F);
+ mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
 
- mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
- mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
+ mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
+ mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
 
   /*
    * Bit 2-1 PORTSEL[1-0] Port Select.
@@ -670,31 +683,39 @@
    */
   switch (if_port) {
     case 1:
- mace_write(ioaddr, MACE_PLSCC, 0x02);
+ mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
       break;
     case 2:
- mace_write(ioaddr, MACE_PLSCC, 0x00);
+ mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
       break;
     default:
- mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4);
+ mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
       /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
          and the MACE device will automatically select the operating media
          interface port. */
       break;
   }
 
- mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
+ mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
   /* Poll ADDRCHG bit */
- while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
- ;
+ ct = 0;
+ while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+ {
+ if(++ ct > 500)
+ {
+ printk(KERN_ERR "mace: ADDRCHG timeout, card removed ?\n");
+ return -1;
+ }
+ }
   /* Set PADR register */
   for (i = 0; i < ETHER_ADDR_LEN; i++)
- mace_write(ioaddr, MACE_PADR, enet_addr[i]);
+ mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
 
   /* MAC Configuration Control Register should be written last */
   /* Let set_multicast_list set this. */
- /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
- mace_write(ioaddr, MACE_MACCC, 0x00);
+ /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
+ mace_write(lp, ioaddr, MACE_MACCC, 0x00);
+ return 0;
 } /* mace_init */
 
 /* ----------------------------------------------------------------------------
@@ -759,8 +780,8 @@
   {
     char sig[2];
 
- sig[0] = mace_read(ioaddr, MACE_CHIPIDL);
- sig[1] = mace_read(ioaddr, MACE_CHIPIDH);
+ sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
+ sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
     if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
       DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
             sig[0], sig[1]);
@@ -772,7 +793,8 @@
     }
   }
 
- mace_init(ioaddr, dev->dev_addr);
+ if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
+ goto failed;
 
   /* The if_port symbol can be set when the module is loaded */
   if (if_port <= 2)
@@ -923,8 +945,8 @@
   lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
 
   /* Reinitialize the MACE chip for operation. */
- mace_init(dev->base_addr, dev->dev_addr);
- mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
+ mace_init(lp, dev->base_addr, dev->dev_addr);
+ mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
 
   /* Restore the multicast list and enable TX and RX. */
   restore_multicast_list(dev);
@@ -1456,9 +1478,9 @@
 {
   mace_private *lp = (mace_private *)dev->priv;
 
- lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC);
- lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC);
- lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC);
+ lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
+ lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
+ lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
   /* At this point, mace_stats is fully updated for this call.
      We may now update the linux_stats. */
 
@@ -1608,30 +1630,30 @@
 
     DEBUG(1, "Attempt to restore multicast list detected.\n");
 
- mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
+ mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
     /* Poll ADDRCHG bit */
- while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+ while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
       ;
     /* Set LADRF register */
     for (i = 0; i < MACE_LADRF_LEN; i++)
- mace_write(ioaddr, MACE_LADRF, ladrf[i]);
+ mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
 
- mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
 
   } else if (num_addrs < 0) {
 
     /* Promiscuous mode: receive all packets */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC,
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC,
       MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
     );
 
   } else {
 
     /* Normal mode */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
 
   }
 } /* restore_multicast_list */
@@ -1691,20 +1713,21 @@
 static void restore_multicast_list(struct net_device *dev)
 {
   ioaddr_t ioaddr = dev->base_addr;
+ mace_private *lp = (mace_private *)dev->priv;
 
   DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
         ((mace_private *)(dev->priv))->multicast_num_addrs);
 
   if (dev->flags & IFF_PROMISC) {
     /* Promiscuous mode: receive all packets */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC,
+ mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC,
       MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
     );
   } else {
     /* Normal mode */
- mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+ mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
   }
 } /* restore_multicast_list */
 
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue Jul 15 2003 - 22:00:40 EST