This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / atm / ambassador.c
index 8a8521c..9be4013 100644 (file)
@@ -313,6 +313,9 @@ static u32 __initdata ucode_data[] = {
 static void do_housekeeping (unsigned long arg);
 /********** globals **********/
 
+static amb_dev * amb_devs = NULL;
+static struct timer_list housekeeping = TIMER_INITIALIZER(do_housekeeping, 0, 1);
+
 static unsigned short debug = 0;
 static unsigned int cmds = 8;
 static unsigned int txs = 32;
@@ -867,7 +870,7 @@ static inline void interrupts_off (amb_dev * dev) {
 
 static irqreturn_t interrupt_handler(int irq, void *dev_id,
                                        struct pt_regs *pt_regs) {
-  amb_dev * dev = (amb_dev *) dev_id;
+  amb_dev * dev = amb_devs;
   (void) pt_regs;
   
   PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
@@ -876,6 +879,24 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id,
     PRINTD (DBG_IRQ|DBG_ERR, "irq with NULL dev_id: %d", irq);
     return IRQ_NONE;
   }
+  // Did one of our cards generate the interrupt?
+  while (dev) {
+    if (dev == dev_id)
+      break;
+    dev = dev->prev;
+  }
+  // impossible - unless we add the device to our list after both
+  // registering the IRQ handler for it and enabling interrupts, AND
+  // the card generates an IRQ at startup - should not happen again
+  if (!dev) {
+    PRINTD (DBG_IRQ, "irq for unknown device: %d", irq);
+    return IRQ_NONE;
+  }
+  // impossible - unless we have memory corruption of dev or kernel
+  if (irq != dev->irq) {
+    PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq);
+    return IRQ_NONE;
+  }
   
   {
     u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt));
@@ -1533,13 +1554,22 @@ static const struct atmdev_ops amb_ops = {
 
 /********** housekeeping **********/
 static void do_housekeeping (unsigned long arg) {
-  amb_dev * dev = (amb_dev *) arg;
+  amb_dev * dev = amb_devs;
+  // data is set to zero at module unload
+  (void) arg;
   
-  // could collect device-specific (not driver/atm-linux) stats here
+  if (housekeeping.data) {
+    while (dev) {
       
-  // last resort refill once every ten seconds
-  fill_rx_pools (dev);
-  mod_timer(&dev->housekeeping, jiffies + 10*HZ);
+      // could collect device-specific (not driver/atm-linux) stats here
+      
+      // last resort refill once every ten seconds
+      fill_rx_pools (dev);
+      
+      dev = dev->prev;
+    }
+    mod_timer(&housekeeping, jiffies + 10*HZ);
+  }
   
   return;
 }
@@ -2195,7 +2225,6 @@ static void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
       
       // set up known dev items straight away
       dev->pci_dev = pci_dev; 
-      pci_set_drvdata(pci_dev, dev);
       
       dev->iobase = pci_resource_start (pci_dev, 1);
       dev->irq = pci_dev->irq; 
@@ -2255,22 +2284,16 @@ out:
        return ret;
 }
 
-static int __devinit amb_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
+static int __init do_pci_device(struct pci_dev *pci_dev)
 {
        amb_dev * dev;
        int err;
 
        // read resources from PCI configuration space
-       unsigned int irq = pci_dev->irq;
-
-       if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) {
-               PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
-               err = -EINVAL;
-               goto out;
-       }
+       u8 irq = pci_dev->irq;
 
        PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
-               " IO %lx, IRQ %u, MEM %p", pci_resource_start(pci_dev, 1),
+               " IO %x, IRQ %u, MEM %p", pci_resource_start(pci_dev, 1),
                irq, bus_to_virt(pci_resource_start(pci_dev, 0)));
 
        // check IO region
@@ -2324,10 +2347,9 @@ static int __devinit amb_probe(struct pci_dev *pci_dev, const struct pci_device_
        dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
        dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
 
-       init_timer(&dev->housekeeping);
-       dev->housekeeping.function = do_housekeeping;
-       dev->housekeeping.data = (unsigned long) dev;
-       mod_timer(&dev->housekeeping, jiffies);
+       // update linked list
+       dev->prev = amb_devs;
+       amb_devs = dev;
 
        // enable host interrupts
        interrupts_on (dev);
@@ -2348,25 +2370,29 @@ out_release:
        goto out;
 }
 
+static int __init amb_probe (void) {
+  struct pci_dev * pci_dev;
+  int devs;
+  
+  PRINTD (DBG_FLOW, "amb_probe");
+  
+  devs = 0;
+  pci_dev = NULL;
+  while ((pci_dev = pci_find_device
+          (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR, pci_dev)
+          )) {
+       if (do_pci_device(pci_dev) == 0)
+               devs++;
+  }
 
-static void __devexit amb_remove_one(struct pci_dev *pci_dev)
-{
-       struct amb_dev *dev;
-
-       dev = pci_get_drvdata(pci_dev);
-
-       PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
-       del_timer_sync(&dev->housekeeping);
-       // the drain should not be necessary
-       drain_rx_pools(dev);
-       interrupts_off(dev);
-       amb_reset(dev, 0);
-       free_irq(dev->irq, dev);
-       pci_disable_device(pci_dev);
-       destroy_queues(dev);
-       atm_dev_deregister(dev->atm_dev);
-       kfree(dev);
-       pci_release_region(pci_dev, 1);
+  
+  pci_dev = NULL;
+  while ((pci_dev = pci_find_device
+          (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD, pci_dev)
+          ))
+    PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
+  
+  return devs;
 }
 
 static void __init amb_check_args (void) {
@@ -2414,13 +2440,13 @@ static void __init amb_check_args (void) {
 MODULE_AUTHOR(maintainer_string);
 MODULE_DESCRIPTION(description_string);
 MODULE_LICENSE("GPL");
-module_param(debug,   ushort, 0644);
-module_param(cmds,    uint, 0);
-module_param(txs,     uint, 0);
-module_param_array(rxs,     uint, NULL, 0);
-module_param_array(rxs_bs,  uint, NULL, 0);
-module_param(rx_lats, uint, 0);
-module_param(pci_lat, byte, 0);
+MODULE_PARM(debug,   "h");
+MODULE_PARM(cmds,    "i");
+MODULE_PARM(txs,     "i");
+MODULE_PARM(rxs,     __MODULE_STRING(NUM_RX_POOLS) "i");
+MODULE_PARM(rxs_bs,  __MODULE_STRING(NUM_RX_POOLS) "i");
+MODULE_PARM(rx_lats, "i");
+MODULE_PARM(pci_lat, "b");
 MODULE_PARM_DESC(debug,   "debug bitmap, see .h file");
 MODULE_PARM_DESC(cmds,    "number of command queue entries");
 MODULE_PARM_DESC(txs,     "number of TX queue entries");
@@ -2431,25 +2457,9 @@ MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
 
 /********** module entry **********/
 
-static struct pci_device_id amb_pci_tbl[] = {
-       { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR, PCI_ANY_ID, PCI_ANY_ID,
-         0, 0, 0 },
-       { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD, PCI_ANY_ID, PCI_ANY_ID,
-         0, 0, 0 },
-       { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, amb_pci_tbl);
-
-static struct pci_driver amb_driver = {
-       .name =         "amb",
-       .probe =        amb_probe,
-       .remove =       __devexit_p(amb_remove_one),
-       .id_table =     amb_pci_tbl,
-};
-
-static int __init amb_module_init (void)
-{
+static int __init amb_module_init (void) {
+  int devs;
+  
   PRINTD (DBG_FLOW|DBG_INIT, "init_module");
   
   // sanity check - cast needed as printk does not support %Zu
@@ -2464,16 +2474,49 @@ static int __init amb_module_init (void)
   amb_check_args();
   
   // get the juice
-  return pci_module_init(&amb_driver);
+  devs = amb_probe();
+  
+  if (devs) {
+    mod_timer (&housekeeping, jiffies);
+  } else {
+    PRINTK (KERN_INFO, "no (usable) adapters found");
+  }
+  
+  return devs ? 0 : -ENODEV;
 }
 
 /********** module exit **********/
 
-static void __exit amb_module_exit (void)
-{
+static void __exit amb_module_exit (void) {
+  amb_dev * dev;
+  
   PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module");
   
-  return pci_unregister_driver(&amb_driver);
+  // paranoia
+  housekeeping.data = 0;
+  del_timer_sync(&housekeeping);
+  
+  while (amb_devs) {
+    struct pci_dev *pdev;
+
+    dev = amb_devs;
+    pdev = dev->pci_dev;
+    amb_devs = dev->prev;
+    
+    PRINTD (DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
+    // the drain should not be necessary
+    drain_rx_pools (dev);
+    interrupts_off (dev);
+    amb_reset (dev, 0);
+    free_irq (dev->irq, dev);
+    pci_disable_device (pdev);
+    destroy_queues (dev);
+    atm_dev_deregister (dev->atm_dev);
+    kfree (dev);
+    pci_release_region (pdev, 1);
+  }
+  
+  return;
 }
 
 module_init(amb_module_init);