#include <linux/uio.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/wait.h>
#include <asm/system.h>
#include <asm/io.h>
/********** globals **********/
-static hrz_dev * hrz_devs = NULL;
-static struct timer_list housekeeping;
+static void do_housekeeping (unsigned long arg);
static unsigned short debug = 0;
static unsigned short vpi_bits = 0;
// p ranges from 1 to a power of 2
#define CR_MAXPEXP 4
-
+
static int make_rate (const hrz_dev * dev, u32 c, rounding r,
- u16 * bits, unsigned int * actual) {
-
- // note: rounding the rate down means rounding 'p' up
+ u16 * bits, unsigned int * actual)
+{
+ // note: rounding the rate down means rounding 'p' up
+ const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ;
- const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ;
+ u32 div = CR_MIND;
+ u32 pre;
- u32 div = CR_MIND;
- u32 pre;
+ // br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in
+ // the tests below. We could think harder about exact possibilities
+ // of failure...
- // local fn to build the timer bits
- int set_cr (void) {
- // paranoia
- if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) {
- PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u",
- div, pre);
- return -EINVAL;
- } else {
- if (bits)
- *bits = (div<<CLOCK_SELECT_SHIFT) | (pre-1);
- if (actual) {
- *actual = (br + (pre<<div) - 1) / (pre<<div);
- PRINTD (DBG_QOS, "actual rate: %u", *actual);
- }
- return 0;
- }
- }
+ unsigned long br_man = br;
+ unsigned int br_exp = 0;
- // br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in
- // the tests below. We could think harder about exact possibilities
- // of failure...
+ PRINTD (DBG_QOS|DBG_FLOW, "make_rate b=%lu, c=%u, %s", br, c,
+ r == round_up ? "up" : r == round_down ? "down" : "nearest");
- unsigned long br_man = br;
- unsigned int br_exp = 0;
-
- PRINTD (DBG_QOS|DBG_FLOW, "make_rate b=%lu, c=%u, %s", br, c,
- (r == round_up) ? "up" : (r == round_down) ? "down" : "nearest");
-
- // avoid div by zero
- if (!c) {
- PRINTD (DBG_QOS|DBG_ERR, "zero rate is not allowed!");
- return -EINVAL;
- }
+ // avoid div by zero
+ if (!c) {
+ PRINTD (DBG_QOS|DBG_ERR, "zero rate is not allowed!");
+ return -EINVAL;
+ }
- while (br_exp < CR_MAXPEXP + CR_MIND && (br_man % 2 == 0)) {
- br_man = br_man >> 1;
- ++br_exp;
- }
- // (br >>br_exp) <<br_exp == br and
- // br_exp <= CR_MAXPEXP+CR_MIND
-
- if (br_man <= (c << (CR_MAXPEXP+CR_MIND-br_exp))) {
- // Equivalent to: B <= (c << (MAXPEXP+MIND))
- // take care of rounding
- switch (r) {
- case round_down:
- pre = (br+(c<<div)-1)/(c<<div);
- // but p must be non-zero
- if (!pre)
- pre = 1;
- break;
- case round_nearest:
- pre = (br+(c<<div)/2)/(c<<div);
- // but p must be non-zero
- if (!pre)
- pre = 1;
- break;
- case round_up:
- pre = br/(c<<div);
- // but p must be non-zero
- if (!pre)
- return -EINVAL;
- break;
- }
- PRINTD (DBG_QOS, "A: p=%u, d=%u", pre, div);
- return set_cr ();
- }
+ while (br_exp < CR_MAXPEXP + CR_MIND && (br_man % 2 == 0)) {
+ br_man = br_man >> 1;
+ ++br_exp;
+ }
+ // (br >>br_exp) <<br_exp == br and
+ // br_exp <= CR_MAXPEXP+CR_MIND
+
+ if (br_man <= (c << (CR_MAXPEXP+CR_MIND-br_exp))) {
+ // Equivalent to: B <= (c << (MAXPEXP+MIND))
+ // take care of rounding
+ switch (r) {
+ case round_down:
+ pre = (br+(c<<div)-1)/(c<<div);
+ // but p must be non-zero
+ if (!pre)
+ pre = 1;
+ break;
+ case round_nearest:
+ pre = (br+(c<<div)/2)/(c<<div);
+ // but p must be non-zero
+ if (!pre)
+ pre = 1;
+ break;
+ default: /* round_up */
+ pre = br/(c<<div);
+ // but p must be non-zero
+ if (!pre)
+ return -EINVAL;
+ }
+ PRINTD (DBG_QOS, "A: p=%u, d=%u", pre, div);
+ goto got_it;
+ }
- // at this point we have
- // d == MIND and (c << (MAXPEXP+MIND)) < B
- while (div < CR_MAXD) {
- div++;
- if (br_man <= (c << (CR_MAXPEXP+div-br_exp))) {
- // Equivalent to: B <= (c << (MAXPEXP+d))
- // c << (MAXPEXP+d-1) < B <= c << (MAXPEXP+d)
- // 1 << (MAXPEXP-1) < B/2^d/c <= 1 << MAXPEXP
- // MAXP/2 < B/c2^d <= MAXP
- // take care of rounding
- switch (r) {
- case round_down:
- pre = (br+(c<<div)-1)/(c<<div);
- break;
- case round_nearest:
- pre = (br+(c<<div)/2)/(c<<div);
- break;
- case round_up:
- pre = br/(c<<div);
- break;
- }
- PRINTD (DBG_QOS, "B: p=%u, d=%u", pre, div);
- return set_cr ();
- }
- }
- // at this point we have
- // d == MAXD and (c << (MAXPEXP+MAXD)) < B
- // but we cannot go any higher
- // take care of rounding
- switch (r) {
- case round_down:
- return -EINVAL;
- break;
- case round_nearest:
- break;
- case round_up:
- break;
- }
- pre = 1 << CR_MAXPEXP;
- PRINTD (DBG_QOS, "C: p=%u, d=%u", pre, div);
- return set_cr ();
+ // at this point we have
+ // d == MIND and (c << (MAXPEXP+MIND)) < B
+ while (div < CR_MAXD) {
+ div++;
+ if (br_man <= (c << (CR_MAXPEXP+div-br_exp))) {
+ // Equivalent to: B <= (c << (MAXPEXP+d))
+ // c << (MAXPEXP+d-1) < B <= c << (MAXPEXP+d)
+ // 1 << (MAXPEXP-1) < B/2^d/c <= 1 << MAXPEXP
+ // MAXP/2 < B/c2^d <= MAXP
+ // take care of rounding
+ switch (r) {
+ case round_down:
+ pre = (br+(c<<div)-1)/(c<<div);
+ break;
+ case round_nearest:
+ pre = (br+(c<<div)/2)/(c<<div);
+ break;
+ default: /* round_up */
+ pre = br/(c<<div);
+ }
+ PRINTD (DBG_QOS, "B: p=%u, d=%u", pre, div);
+ goto got_it;
+ }
+ }
+ // at this point we have
+ // d == MAXD and (c << (MAXPEXP+MAXD)) < B
+ // but we cannot go any higher
+ // take care of rounding
+ if (r == round_down)
+ return -EINVAL;
+ pre = 1 << CR_MAXPEXP;
+ PRINTD (DBG_QOS, "C: p=%u, d=%u", pre, div);
+got_it:
+ // paranoia
+ if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) {
+ PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u",
+ div, pre);
+ return -EINVAL;
+ } else {
+ if (bits)
+ *bits = (div<<CLOCK_SELECT_SHIFT) | (pre-1);
+ if (actual) {
+ *actual = (br + (pre<<div) - 1) / (pre<<div);
+ PRINTD (DBG_QOS, "actual rate: %u", *actual);
+ }
+ return 0;
+ }
}
static int make_rate_with_tolerance (const hrz_dev * dev, u32 c, rounding r, unsigned int tol,
struct atm_vcc * vcc = ATM_SKB(skb)->vcc;
// VC layer stats
atomic_inc(&vcc->stats->rx);
- do_gettimeofday(&skb->stamp);
+ __net_timestamp(skb);
// end of our responsability
vcc->push (vcc, skb);
}
/********** (queue to) become the next TX thread **********/
static inline int tx_hold (hrz_dev * dev) {
- while (test_and_set_bit (tx_busy, &dev->flags)) {
- PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags);
- interruptible_sleep_on (&dev->tx_queue);
- PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags);
- if (signal_pending (current))
- return -1;
- }
+ PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags);
+ wait_event_interruptible(dev->tx_queue, (!test_and_set_bit(tx_busy, &dev->flags)));
+ PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags);
+ if (signal_pending (current))
+ return -1;
PRINTD (DBG_TX, "set tx_busy for dev %p", dev);
return 0;
}
// tx_regions == 0
// that's all folks - end of frame
struct sk_buff * skb = dev->tx_skb;
- dev->tx_iovec = 0;
+ dev->tx_iovec = NULL;
// VC layer stats
atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
/********** interrupt handler **********/
-static irqreturn_t interrupt_handler(int irq, void *dev_id,
- struct pt_regs *pt_regs) {
- hrz_dev * dev = hrz_devs;
+static irqreturn_t interrupt_handler(int irq, void *dev_id) {
+ hrz_dev * dev = (hrz_dev *) dev_id;
u32 int_source;
unsigned int irq_ok;
- (void) pt_regs;
PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id);
- if (!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;
- }
- 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;
- }
-
// definitely for us
irq_ok = 0;
while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF)
/********** 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_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);
- }
+ 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);
+
return;
}
// a.k.a. prepare the channel and remember that we have done so.
tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel];
- u16 rd_ptr;
- u16 wr_ptr;
+ u32 rd_ptr;
+ u32 wr_ptr;
u16 channel = vcc->channel;
unsigned long flags;
if (tx_iovcnt) {
// scatter gather transfer
dev->tx_regions = tx_iovcnt;
- dev->tx_iovec = 0; /* @@@ needs rewritten */
+ dev->tx_iovec = NULL; /* @@@ needs rewritten */
dev->tx_bytes = 0;
PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)",
skb->data, tx_len);
} else {
// simple transfer
dev->tx_regions = 0;
- dev->tx_iovec = 0;
+ dev->tx_iovec = NULL;
dev->tx_bytes = tx_len;
dev->tx_addr = skb->data;
PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)",
/********** read the burnt in address **********/
-static u16 __init read_bia (const hrz_dev * dev, u16 addr) {
+static inline void WRITE_IT_WAIT (const hrz_dev *dev, u32 ctrl)
+{
+ wr_regl (dev, CONTROL_0_REG, ctrl);
+ udelay (5);
+}
+static inline void CLOCK_IT (const hrz_dev *dev, u32 ctrl)
+{
+ // DI must be valid around rising SK edge
+ WRITE_IT_WAIT(dev, ctrl & ~SEEPROM_SK);
+ WRITE_IT_WAIT(dev, ctrl | SEEPROM_SK);
+}
+
+static u16 __devinit read_bia (const hrz_dev * dev, u16 addr)
+{
u32 ctrl = rd_regl (dev, CONTROL_0_REG);
- void WRITE_IT_WAIT (void) {
- wr_regl (dev, CONTROL_0_REG, ctrl);
- udelay (5);
- }
-
- void CLOCK_IT (void) {
- // DI must be valid around rising SK edge
- ctrl &= ~SEEPROM_SK;
- WRITE_IT_WAIT();
- ctrl |= SEEPROM_SK;
- WRITE_IT_WAIT();
- }
-
const unsigned int addr_bits = 6;
const unsigned int data_bits = 16;
u16 res;
ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI);
- WRITE_IT_WAIT();
+ WRITE_IT_WAIT(dev, ctrl);
// wake Serial EEPROM and send 110 (READ) command
ctrl |= (SEEPROM_CS | SEEPROM_DI);
- CLOCK_IT();
+ CLOCK_IT(dev, ctrl);
ctrl |= SEEPROM_DI;
- CLOCK_IT();
+ CLOCK_IT(dev, ctrl);
ctrl &= ~SEEPROM_DI;
- CLOCK_IT();
+ CLOCK_IT(dev, ctrl);
for (i=0; i<addr_bits; i++) {
if (addr & (1 << (addr_bits-1)))
else
ctrl &= ~SEEPROM_DI;
- CLOCK_IT();
+ CLOCK_IT(dev, ctrl);
addr = addr << 1;
}
for (i=0;i<data_bits;i++) {
res = res >> 1;
- CLOCK_IT();
+ CLOCK_IT(dev, ctrl);
if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO)
res |= (1 << (data_bits-1));
}
ctrl &= ~(SEEPROM_SK | SEEPROM_CS);
- WRITE_IT_WAIT();
+ WRITE_IT_WAIT(dev, ctrl);
return res;
}
/********** initialise a card **********/
-static int __init hrz_init (hrz_dev * dev) {
+static int __devinit hrz_init (hrz_dev * dev) {
int onefivefive;
u16 chan;
// we take "the PCR" as a rate-cap
// not reserved
vcc.tx_rate = 0;
- make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0);
+ make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, NULL);
vcc.tx_xbr_bits = ABR_RATE_TYPE;
break;
}
PRINTK (KERN_ERR, "%s atm_vcc=%p rxer[channel]=%p",
"arghhh! we're going to die!",
atm_vcc, dev->rxer[channel]);
- dev->rxer[channel] = 0;
+ dev->rxer[channel] = NULL;
}
// atomically release our rate reservation
.owner = THIS_MODULE,
};
-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;
+static int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
+{
+ hrz_dev * dev;
+ int err = 0;
- 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 {
+ // 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));
+ unsigned int irq;
unsigned char lat;
-
- PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
- dev->atm_dev->number, dev, dev->atm_dev);
+
+ PRINTD (DBG_FLOW, "hrz_probe");
+
+ if (pci_enable_device(pci_dev))
+ return -EINVAL;
+
+ /* XXX DEV_LABEL is a guess */
+ if (!request_region(iobase, HRZ_IO_EXTENT, DEV_LABEL)) {
+ return -EINVAL;
+ goto out_disable;
+ }
+
+ dev = kzalloc(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_release;
+ }
+
+ pci_set_drvdata(pci_dev, dev);
+
+ // grab IRQ and install handler - move this someplace more sensible
+ irq = pci_dev->irq;
+ if (request_irq(irq,
+ interrupt_handler,
+ IRQF_SHARED, /* 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);
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 = 0;
- dev->tx_iovec = 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;
-
- // 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;
+
+ 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_release:
+ release_region(iobase, HRZ_IO_EXTENT);
+out_disable:
+ pci_disable_device(pci_dev);
+ 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);
}
static void __init hrz_check_args (void) {
MODULE_AUTHOR(maintainer_string);
MODULE_DESCRIPTION(description_string);
MODULE_LICENSE("GPL");
-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_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_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
- 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;
+ return pci_register_driver(&hrz_driver);
}
/********** module exit **********/
static void __exit hrz_module_exit (void) {
- hrz_dev * dev;
PRINTD (DBG_FLOW, "cleanup_module");
-
- // 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;
+
+ pci_unregister_driver(&hrz_driver);
}
module_init(hrz_module_init);