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;
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);
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));
/********** 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;
}
// 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;
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
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);
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) {
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");
/********** 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
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);