/********** globals **********/
-static void do_housekeeping (unsigned long arg);
+static hrz_dev * hrz_devs = NULL;
+static struct timer_list housekeeping;
static unsigned short debug = 0;
static unsigned short vpi_bits = 0;
static irqreturn_t interrupt_handler(int irq, void *dev_id,
struct pt_regs *pt_regs) {
- hrz_dev * dev = (hrz_dev *) dev_id;
+ hrz_dev * dev = hrz_devs;
u32 int_source;
unsigned int irq_ok;
(void) pt_regs;
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;
+ }
+ if (!dev) {
+ PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+ return IRQ_NONE;
+ }
if (irq != dev->irq) {
PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq);
return IRQ_NONE;
/********** housekeeping **********/
+static void set_timer (struct timer_list * timer, unsigned int delay) {
+ timer->expires = jiffies + delay;
+ add_timer (timer);
+ return;
+}
+
static void do_housekeeping (unsigned long arg) {
// just stats at the moment
- hrz_dev * dev = (hrz_dev *) arg;
-
- // collect device-specific (not driver/atm-linux) stats here
- dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF);
- dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF);
- dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF);
- dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF);
-
- mod_timer (&dev->housekeeping, jiffies + HZ/10);
-
+ hrz_dev * dev = hrz_devs;
+ (void) arg;
+ // data is set to zero at module unload
+ if (housekeeping.data) {
+ while (dev) {
+ // collect device-specific (not driver/atm-linux) stats here
+ dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF);
+ dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF);
+ dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF);
+ dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF);
+ dev = dev->prev;
+ }
+ set_timer (&housekeeping, HZ/10);
+ }
return;
}
.owner = THIS_MODULE,
};
-static int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
-{
- hrz_dev * dev;
- int err = 0;
+static int __init hrz_probe (void) {
+ struct pci_dev * pci_dev;
+ int devs;
+
+ PRINTD (DBG_FLOW, "hrz_probe");
+
+ devs = 0;
+ pci_dev = NULL;
+ while ((pci_dev = pci_find_device
+ (PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, pci_dev)
+ )) {
+ hrz_dev * dev;
+
+ // adapter slot free, read resources from PCI configuration space
+ u32 iobase = pci_resource_start (pci_dev, 0);
+ u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1));
+ u8 irq = pci_dev->irq;
+
+ /* XXX DEV_LABEL is a guess */
+ if (!request_region (iobase, HRZ_IO_EXTENT, DEV_LABEL))
+ continue;
- // adapter slot free, read resources from PCI configuration space
- u32 iobase = pci_resource_start (pci_dev, 0);
- u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1));
- u8 irq = pci_dev->irq;
+ if (pci_enable_device (pci_dev))
+ continue;
+
+ dev = kmalloc (sizeof(hrz_dev), GFP_KERNEL);
+ if (!dev) {
+ // perhaps we should be nice: deregister all adapters and abort?
+ PRINTD (DBG_ERR, "out of memory");
+ continue;
+ }
+
+ memset (dev, 0, sizeof(hrz_dev));
+
+ // grab IRQ and install handler - move this someplace more sensible
+ if (request_irq (irq,
+ interrupt_handler,
+ SA_SHIRQ, /* irqflags guess */
+ DEV_LABEL, /* name guess */
+ dev)) {
+ PRINTD (DBG_WARN, "request IRQ failed!");
+ // free_irq is at "endif"
+ } else {
+
+ PRINTD (DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p",
+ iobase, irq, membase);
+
+ dev->atm_dev = atm_dev_register (DEV_LABEL, &hrz_ops, -1, NULL);
+ if (!(dev->atm_dev)) {
+ PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+ } else {
unsigned char lat;
-
- PRINTD (DBG_FLOW, "hrz_probe");
-
- /* XXX DEV_LABEL is a guess */
- if (!request_region(iobase, HRZ_IO_EXTENT, DEV_LABEL))
- return -EINVAL;
-
- if (pci_enable_device(pci_dev)) {
- err = -EINVAL;
- goto out_release;
- }
-
- dev = kmalloc(sizeof(hrz_dev), GFP_KERNEL);
- if (!dev) {
- // perhaps we should be nice: deregister all adapters and abort?
- PRINTD(DBG_ERR, "out of memory");
- err = -ENOMEM;
- goto out_disable;
- }
-
- memset(dev, 0, sizeof(hrz_dev));
-
- pci_set_drvdata(pci_dev, dev);
-
- // grab IRQ and install handler - move this someplace more sensible
- if (request_irq(irq,
- interrupt_handler,
- SA_SHIRQ, /* irqflags guess */
- DEV_LABEL, /* name guess */
- dev)) {
- PRINTD(DBG_WARN, "request IRQ failed!");
- err = -EINVAL;
- goto out_free;
- }
-
- PRINTD(DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p",
- iobase, irq, membase);
-
- dev->atm_dev = atm_dev_register(DEV_LABEL, &hrz_ops, -1, NULL);
- if (!(dev->atm_dev)) {
- PRINTD(DBG_ERR, "failed to register Madge ATM adapter");
- err = -EINVAL;
- goto out_free_irq;
- }
-
- PRINTD(DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
- dev->atm_dev->number, dev, dev->atm_dev);
+
+ PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
+ dev->atm_dev->number, dev, dev->atm_dev);
dev->atm_dev->dev_data = (void *) dev;
dev->pci_dev = pci_dev;
-
+
// enable bus master accesses
- pci_set_master(pci_dev);
-
+ pci_set_master (pci_dev);
+
// frobnicate latency (upwards, usually)
- pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat);
+ pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
if (pci_lat) {
- PRINTD(DBG_INFO, "%s PCI latency timer from %hu to %hu",
- "changing", lat, pci_lat);
- pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
+ PRINTD (DBG_INFO, "%s PCI latency timer from %hu to %hu",
+ "changing", lat, pci_lat);
+ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, pci_lat);
} else if (lat < MIN_PCI_LATENCY) {
- PRINTK(KERN_INFO, "%s PCI latency timer from %hu to %hu",
- "increasing", lat, MIN_PCI_LATENCY);
- pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
+ PRINTK (KERN_INFO, "%s PCI latency timer from %hu to %hu",
+ "increasing", lat, MIN_PCI_LATENCY);
+ pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
}
-
+
dev->iobase = iobase;
dev->irq = irq;
dev->membase = membase;
-
+
dev->rx_q_entry = dev->rx_q_reset = &memmap->rx_q_entries[0];
dev->rx_q_wrap = &memmap->rx_q_entries[RX_CHANS-1];
-
+
// these next three are performance hacks
dev->last_vc = -1;
dev->tx_last = -1;
dev->tx_idle = 0;
-
+
dev->tx_regions = 0;
dev->tx_bytes = 0;
dev->tx_skb = NULL;
dev->tx_iovec = NULL;
-
+
dev->tx_cell_count = 0;
dev->rx_cell_count = 0;
dev->hec_error_count = 0;
dev->unassigned_cell_count = 0;
-
+
dev->noof_spare_buffers = 0;
-
+
{
- unsigned int i;
- for (i = 0; i < TX_CHANS; ++i)
- dev->tx_channel_record[i] = -1;
+ unsigned int i;
+ for (i = 0; i < TX_CHANS; ++i)
+ dev->tx_channel_record[i] = -1;
}
-
+
dev->flags = 0;
-
+
// Allocate cell rates and remember ASIC version
// Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
// Copper: (WRONG) we want 6 into the above, close to 25Mb/s
// Copper: (plagarise!) 25600000/8/270*260/53 - n/53
-
- if (hrz_init(dev)) {
- // to be really pedantic, this should be ATM_OC3c_PCR
- dev->tx_avail = ATM_OC3_PCR;
- dev->rx_avail = ATM_OC3_PCR;
- set_bit(ultra, &dev->flags); // NOT "|= ultra" !
+
+ if (hrz_init (dev)) {
+ // to be really pedantic, this should be ATM_OC3c_PCR
+ dev->tx_avail = ATM_OC3_PCR;
+ dev->rx_avail = ATM_OC3_PCR;
+ set_bit (ultra, &dev->flags); // NOT "|= ultra" !
} else {
- dev->tx_avail = ((25600000/8)*26)/(27*53);
- dev->rx_avail = ((25600000/8)*26)/(27*53);
- PRINTD(DBG_WARN, "Buggy ASIC: no TX bus-mastering.");
+ dev->tx_avail = ((25600000/8)*26)/(27*53);
+ dev->rx_avail = ((25600000/8)*26)/(27*53);
+ PRINTD (DBG_WARN, "Buggy ASIC: no TX bus-mastering.");
}
-
+
// rate changes spinlock
- spin_lock_init(&dev->rate_lock);
-
+ spin_lock_init (&dev->rate_lock);
+
// on-board memory access spinlock; we want atomic reads and
// writes to adapter memory (handles IRQ and SMP)
- spin_lock_init(&dev->mem_lock);
-
- init_waitqueue_head(&dev->tx_queue);
-
+ spin_lock_init (&dev->mem_lock);
+
+ init_waitqueue_head (&dev->tx_queue);
+
// vpi in 0..4, vci in 6..10
dev->atm_dev->ci_range.vpi_bits = vpi_bits;
dev->atm_dev->ci_range.vci_bits = 10-vpi_bits;
-
- init_timer(&dev->housekeeping);
- dev->housekeeping.function = do_housekeeping;
- dev->housekeeping.data = (unsigned long) dev;
- mod_timer(&dev->housekeeping, jiffies);
-
-out:
- return err;
-
-out_free_irq:
- free_irq(dev->irq, dev);
-out_free:
- kfree(dev);
-out_disable:
- pci_disable_device(pci_dev);
-out_release:
- release_region(iobase, HRZ_IO_EXTENT);
- goto out;
-}
-
-static void __devexit hrz_remove_one(struct pci_dev *pci_dev)
-{
- hrz_dev *dev;
-
- dev = pci_get_drvdata(pci_dev);
-
- PRINTD(DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
- del_timer_sync(&dev->housekeeping);
- hrz_reset(dev);
- atm_dev_deregister(dev->atm_dev);
- free_irq(dev->irq, dev);
- release_region(dev->iobase, HRZ_IO_EXTENT);
- kfree(dev);
-
- pci_disable_device(pci_dev);
+
+ // update count and linked list
+ ++devs;
+ dev->prev = hrz_devs;
+ hrz_devs = dev;
+ // success
+ continue;
+
+ /* not currently reached */
+ atm_dev_deregister (dev->atm_dev);
+ } /* atm_dev_register */
+ free_irq (irq, dev);
+
+ } /* request_irq */
+ kfree (dev);
+ release_region(iobase, HRZ_IO_EXTENT);
+ } /* kmalloc and while */
+ return devs;
}
static void __init hrz_check_args (void) {
MODULE_AUTHOR(maintainer_string);
MODULE_DESCRIPTION(description_string);
MODULE_LICENSE("GPL");
-module_param(debug, ushort, 0644);
-module_param(vpi_bits, ushort, 0);
-module_param(max_tx_size, int, 0);
-module_param(max_rx_size, int, 0);
-module_param(pci_lat, byte, 0);
+MODULE_PARM(debug, "h");
+MODULE_PARM(vpi_bits, "h");
+MODULE_PARM(max_tx_size, "i");
+MODULE_PARM(max_rx_size, "i");
+MODULE_PARM(pci_lat, "b");
MODULE_PARM_DESC(debug, "debug bitmap, see .h file");
MODULE_PARM_DESC(vpi_bits, "number of bits (0..4) to allocate to VPIs");
MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames");
MODULE_PARM_DESC(max_rx_size, "maximum size of RX AAL5 frames");
MODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
-static struct pci_device_id hrz_pci_tbl[] = {
- { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, PCI_ANY_ID, PCI_ANY_ID,
- 0, 0, 0 },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, hrz_pci_tbl);
-
-static struct pci_driver hrz_driver = {
- .name = "horizon",
- .probe = hrz_probe,
- .remove = __devexit_p(hrz_remove_one),
- .id_table = hrz_pci_tbl,
-};
-
/********** module entry **********/
static int __init hrz_module_init (void) {
+ int devs;
+
// sanity check - cast is needed since printk does not support %Zu
if (sizeof(struct MEMMAP) != 128*1024/4) {
PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).",
hrz_check_args();
// get the juice
- return pci_module_init(&hrz_driver);
+ devs = hrz_probe();
+
+ if (devs) {
+ init_timer (&housekeeping);
+ housekeeping.function = do_housekeeping;
+ // paranoia
+ housekeeping.data = 1;
+ set_timer (&housekeeping, 0);
+ } else {
+ PRINTK (KERN_ERR, "no (usable) adapters found");
+ }
+
+ return devs ? 0 : -ENODEV;
}
/********** module exit **********/
static void __exit hrz_module_exit (void) {
+ hrz_dev * dev;
PRINTD (DBG_FLOW, "cleanup_module");
- return pci_unregister_driver(&hrz_driver);
+ // paranoia
+ housekeeping.data = 0;
+ del_timer (&housekeeping);
+
+ while (hrz_devs) {
+ dev = hrz_devs;
+ hrz_devs = dev->prev;
+
+ PRINTD (DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
+ hrz_reset (dev);
+ atm_dev_deregister (dev->atm_dev);
+ free_irq (dev->irq, dev);
+ release_region (dev->iobase, HRZ_IO_EXTENT);
+ kfree (dev);
+ }
+
+ return;
}
module_init(hrz_module_init);