staging: comedi: refactor pcmmio driver to remove forward declarations
Refactor the switch_page and pcmmio_stop_intr functions to avoid needing the forward declarations. Move the module_init/module_exit routines and the associated struct comedi_driver and other variables to the end of the source. This is more typical of how other drivers are written and removes the need for the forward declarations. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Ian Abbott <abbotti@mev.co.uk> Cc: Mori Hess <fmhess@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
924f4685f4
commit
b2bb98e179
@ -145,13 +145,6 @@ Configuration Options:
|
||||
#define PAGE_ENAB 2
|
||||
#define PAGE_INT_ID 3
|
||||
|
||||
static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
|
||||
struct comedi_insn *, unsigned int *);
|
||||
static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
|
||||
struct comedi_insn *, unsigned int *);
|
||||
static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
|
||||
struct comedi_insn *, unsigned int *);
|
||||
|
||||
/*
|
||||
* Board descriptions for two imaginary boards. Describing the
|
||||
* boards in this way is optional, and completely driver-dependent.
|
||||
@ -190,23 +183,6 @@ static const struct comedi_lrange ranges_ao = {
|
||||
RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
|
||||
};
|
||||
|
||||
static const struct pcmmio_board pcmmio_boards[] = {
|
||||
{
|
||||
.name = "pcmmio",
|
||||
.dio_num_asics = 1,
|
||||
.dio_num_ports = 6,
|
||||
.total_iosize = 32,
|
||||
.ai_bits = 16,
|
||||
.ao_bits = 16,
|
||||
.n_ai_chans = 16,
|
||||
.n_ao_chans = 8,
|
||||
.ai_range_table = &ranges_ai,
|
||||
.ao_range_table = &ranges_ao,
|
||||
.ai_rinsn = ai_rinsn,
|
||||
.ao_rinsn = ao_rinsn,
|
||||
.ao_winsn = ao_winsn},
|
||||
};
|
||||
|
||||
/*
|
||||
* Useful for shorthand access to the particular board structure
|
||||
*/
|
||||
@ -293,312 +269,6 @@ struct pcmmio_private {
|
||||
*/
|
||||
#define devpriv ((struct pcmmio_private *)dev->private)
|
||||
#define subpriv ((struct pcmmio_subdev_private *)s->private)
|
||||
/*
|
||||
* The struct comedi_driver structure tells the Comedi core module
|
||||
* which functions to call to configure/deconfigure (attach/detach)
|
||||
* the board, and also about the kernel module that contains
|
||||
* the device code.
|
||||
*/
|
||||
static int pcmmio_attach(struct comedi_device *dev,
|
||||
struct comedi_devconfig *it);
|
||||
static int pcmmio_detach(struct comedi_device *dev);
|
||||
|
||||
static struct comedi_driver driver = {
|
||||
.driver_name = "pcmmio",
|
||||
.module = THIS_MODULE,
|
||||
.attach = pcmmio_attach,
|
||||
.detach = pcmmio_detach,
|
||||
/* It is not necessary to implement the following members if you are
|
||||
* writing a driver for a ISA PnP or PCI card */
|
||||
/* Most drivers will support multiple types of boards by
|
||||
* having an array of board structures. These were defined
|
||||
* in pcmmio_boards[] above. Note that the element 'name'
|
||||
* was first in the structure -- Comedi uses this fact to
|
||||
* extract the name of the board without knowing any details
|
||||
* about the structure except for its length.
|
||||
* When a device is attached (by comedi_config), the name
|
||||
* of the device is given to Comedi, and Comedi tries to
|
||||
* match it by going through the list of board names. If
|
||||
* there is a match, the address of the pointer is put
|
||||
* into dev->board_ptr and driver->attach() is called.
|
||||
*
|
||||
* Note that these are not necessary if you can determine
|
||||
* the type of board in software. ISA PnP, PCI, and PCMCIA
|
||||
* devices are such boards.
|
||||
*/
|
||||
.board_name = &pcmmio_boards[0].name,
|
||||
.offset = sizeof(struct pcmmio_board),
|
||||
.num_names = ARRAY_SIZE(pcmmio_boards),
|
||||
};
|
||||
|
||||
static int pcmmio_dio_insn_bits(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
static int pcmmio_dio_insn_config(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn, unsigned int *data);
|
||||
|
||||
static irqreturn_t interrupt_pcmmio(int irq, void *d);
|
||||
static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
|
||||
static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
|
||||
static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
|
||||
static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
struct comedi_cmd *cmd);
|
||||
|
||||
/* some helper functions to deal with specifics of this device's registers */
|
||||
/* sets up/clears ASIC chips to defaults */
|
||||
static void init_asics(struct comedi_device *dev);
|
||||
static void switch_page(struct comedi_device *dev, int asic, int page);
|
||||
#ifdef notused
|
||||
static void lock_port(struct comedi_device *dev, int asic, int port);
|
||||
static void unlock_port(struct comedi_device *dev, int asic, int port);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Attach is called by the Comedi core to configure the driver
|
||||
* for a particular board. If you specified a board_name array
|
||||
* in the driver structure, dev->board_ptr contains that
|
||||
* address.
|
||||
*/
|
||||
static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||
{
|
||||
struct comedi_subdevice *s;
|
||||
int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
|
||||
thisasic_chanct = 0;
|
||||
unsigned long iobase;
|
||||
unsigned int irq[MAX_ASICS];
|
||||
|
||||
iobase = it->options[0];
|
||||
irq[0] = it->options[1];
|
||||
|
||||
printk(KERN_INFO "comedi%d: %s: io: %lx attaching...\n", dev->minor,
|
||||
driver.driver_name, iobase);
|
||||
|
||||
dev->iobase = iobase;
|
||||
|
||||
if (!iobase || !request_region(iobase,
|
||||
thisboard->total_iosize,
|
||||
driver.driver_name)) {
|
||||
printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize dev->board_name. Note that we can use the "thisboard"
|
||||
* macro now, since we just initialized it in the last line.
|
||||
*/
|
||||
dev->board_name = thisboard->name;
|
||||
|
||||
/*
|
||||
* Allocate the private structure area. alloc_private() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate private data structure\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (asic = 0; asic < MAX_ASICS; ++asic) {
|
||||
devpriv->asics[asic].num = asic;
|
||||
devpriv->asics[asic].iobase =
|
||||
dev->iobase + 16 + asic * ASIC_IOSIZE;
|
||||
/*
|
||||
* this gets actually set at the end of this function when we
|
||||
* request_irqs
|
||||
*/
|
||||
devpriv->asics[asic].irq = 0;
|
||||
spin_lock_init(&devpriv->asics[asic].spinlock);
|
||||
}
|
||||
|
||||
chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
|
||||
n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
|
||||
n_subdevs = n_dio_subdevs + 2;
|
||||
devpriv->sprivs =
|
||||
kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
|
||||
GFP_KERNEL);
|
||||
if (!devpriv->sprivs) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Allocate the subdevice structures. alloc_subdevice() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*
|
||||
* Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
|
||||
*/
|
||||
if (alloc_subdevices(dev, n_subdevs) < 0) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate subdevice data structures\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* First, AI */
|
||||
sdev_no = 0;
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = (1 << thisboard->ai_bits) - 1;
|
||||
s->range_table = thisboard->ai_range_table;
|
||||
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->n_chan = thisboard->n_ai_chans;
|
||||
s->len_chanlist = s->n_chan;
|
||||
s->insn_read = thisboard->ai_rinsn;
|
||||
subpriv->iobase = dev->iobase + 0;
|
||||
/* initialize the resource enable register by clearing it */
|
||||
outb(0, subpriv->iobase + 3);
|
||||
outb(0, subpriv->iobase + 4 + 3);
|
||||
|
||||
/* Next, AO */
|
||||
++sdev_no;
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = (1 << thisboard->ao_bits) - 1;
|
||||
s->range_table = thisboard->ao_range_table;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->n_chan = thisboard->n_ao_chans;
|
||||
s->len_chanlist = s->n_chan;
|
||||
s->insn_read = thisboard->ao_rinsn;
|
||||
s->insn_write = thisboard->ao_winsn;
|
||||
subpriv->iobase = dev->iobase + 8;
|
||||
/* initialize the resource enable register by clearing it */
|
||||
outb(0, subpriv->iobase + 3);
|
||||
outb(0, subpriv->iobase + 4 + 3);
|
||||
|
||||
++sdev_no;
|
||||
port = 0;
|
||||
asic = 0;
|
||||
for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
|
||||
int byte_no;
|
||||
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->insn_bits = pcmmio_dio_insn_bits;
|
||||
s->insn_config = pcmmio_dio_insn_config;
|
||||
s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
|
||||
subpriv->dio.intr.asic = -1;
|
||||
subpriv->dio.intr.first_chan = -1;
|
||||
subpriv->dio.intr.asic_chan = -1;
|
||||
subpriv->dio.intr.num_asic_chans = -1;
|
||||
subpriv->dio.intr.active = 0;
|
||||
s->len_chanlist = 1;
|
||||
|
||||
/* save the ioport address for each 'port' of 8 channels in the
|
||||
subdevice */
|
||||
for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
|
||||
if (port >= PORTS_PER_ASIC) {
|
||||
port = 0;
|
||||
++asic;
|
||||
thisasic_chanct = 0;
|
||||
}
|
||||
subpriv->iobases[byte_no] =
|
||||
devpriv->asics[asic].iobase + port;
|
||||
|
||||
if (thisasic_chanct <
|
||||
CHANS_PER_PORT * INTR_PORTS_PER_ASIC
|
||||
&& subpriv->dio.intr.asic < 0) {
|
||||
/*
|
||||
* this is an interrupt subdevice,
|
||||
* so setup the struct
|
||||
*/
|
||||
subpriv->dio.intr.asic = asic;
|
||||
subpriv->dio.intr.active = 0;
|
||||
subpriv->dio.intr.stop_count = 0;
|
||||
subpriv->dio.intr.first_chan = byte_no * 8;
|
||||
subpriv->dio.intr.asic_chan = thisasic_chanct;
|
||||
subpriv->dio.intr.num_asic_chans =
|
||||
s->n_chan - subpriv->dio.intr.first_chan;
|
||||
s->cancel = pcmmio_cancel;
|
||||
s->do_cmd = pcmmio_cmd;
|
||||
s->do_cmdtest = pcmmio_cmdtest;
|
||||
s->len_chanlist =
|
||||
subpriv->dio.intr.num_asic_chans;
|
||||
}
|
||||
thisasic_chanct += CHANS_PER_PORT;
|
||||
}
|
||||
spin_lock_init(&subpriv->dio.intr.spinlock);
|
||||
|
||||
chans_left -= s->n_chan;
|
||||
|
||||
if (!chans_left) {
|
||||
/*
|
||||
* reset the asic to our first asic,
|
||||
* to do intr subdevs
|
||||
*/
|
||||
asic = 0;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init_asics(dev); /* clear out all the registers, basically */
|
||||
|
||||
for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
|
||||
if (irq[asic]
|
||||
&& request_irq(irq[asic], interrupt_pcmmio,
|
||||
IRQF_SHARED, thisboard->name, dev)) {
|
||||
int i;
|
||||
/* unroll the allocated irqs.. */
|
||||
for (i = asic - 1; i >= 0; --i) {
|
||||
free_irq(irq[i], dev);
|
||||
devpriv->asics[i].irq = irq[i] = 0;
|
||||
}
|
||||
irq[asic] = 0;
|
||||
}
|
||||
devpriv->asics[asic].irq = irq[asic];
|
||||
}
|
||||
|
||||
dev->irq = irq[0]; /*
|
||||
* grr.. wish comedi dev struct supported
|
||||
* multiple irqs..
|
||||
*/
|
||||
|
||||
if (irq[0]) {
|
||||
printk(KERN_DEBUG "comedi%d: irq: %u\n", dev->minor, irq[0]);
|
||||
if (thisboard->dio_num_asics == 2 && irq[1])
|
||||
printk(KERN_DEBUG "comedi%d: second ASIC irq: %u\n",
|
||||
dev->minor, irq[1]);
|
||||
} else {
|
||||
printk(KERN_INFO "comedi%d: (IRQ mode disabled)\n", dev->minor);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "comedi%d: attached\n", dev->minor);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* _detach is called to deconfigure a device. It should deallocate
|
||||
* resources.
|
||||
* This function is also called when _attach() fails, so it should be
|
||||
* careful not to release resources that were not necessarily
|
||||
* allocated by _attach(). dev->private and dev->subdevices are
|
||||
* deallocated automatically by the core.
|
||||
*/
|
||||
static int pcmmio_detach(struct comedi_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "comedi%d: %s: remove\n", dev->minor, driver.driver_name);
|
||||
if (dev->iobase)
|
||||
release_region(dev->iobase, thisboard->total_iosize);
|
||||
|
||||
for (i = 0; i < MAX_ASICS; ++i) {
|
||||
if (devpriv && devpriv->asics[i].irq)
|
||||
free_irq(devpriv->asics[i].irq, dev);
|
||||
}
|
||||
|
||||
if (devpriv && devpriv->sprivs)
|
||||
kfree(devpriv->sprivs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DIO devices are slightly special. Although it is possible to
|
||||
* implement the insn_read/insn_write interface, it is much more
|
||||
@ -751,6 +421,21 @@ static int pcmmio_dio_insn_config(struct comedi_device *dev,
|
||||
return insn->n;
|
||||
}
|
||||
|
||||
static void switch_page(struct comedi_device *dev, int asic, int page)
|
||||
{
|
||||
if (asic < 0 || asic >= thisboard->dio_num_asics)
|
||||
return; /* paranoia */
|
||||
if (page < 0 || page >= NUM_PAGES)
|
||||
return; /* more paranoia */
|
||||
|
||||
devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
|
||||
devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
|
||||
|
||||
/* now write out the shadow register */
|
||||
outb(devpriv->asics[asic].pagelock,
|
||||
devpriv->asics[asic].iobase + REG_PAGELOCK);
|
||||
}
|
||||
|
||||
static void init_asics(struct comedi_device *dev)
|
||||
{ /* sets up an
|
||||
ASIC chip to defaults */
|
||||
@ -788,21 +473,6 @@ static void init_asics(struct comedi_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void switch_page(struct comedi_device *dev, int asic, int page)
|
||||
{
|
||||
if (asic < 0 || asic >= thisboard->dio_num_asics)
|
||||
return; /* paranoia */
|
||||
if (page < 0 || page >= NUM_PAGES)
|
||||
return; /* more paranoia */
|
||||
|
||||
devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
|
||||
devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
|
||||
|
||||
/* now write out the shadow register */
|
||||
outb(devpriv->asics[asic].pagelock,
|
||||
devpriv->asics[asic].iobase + REG_PAGELOCK);
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
static void lock_port(struct comedi_device *dev, int asic, int port)
|
||||
{
|
||||
@ -831,6 +501,27 @@ static void unlock_port(struct comedi_device *dev, int asic, int port)
|
||||
}
|
||||
#endif /* notused */
|
||||
|
||||
static void pcmmio_stop_intr(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
int nports, firstport, asic, port;
|
||||
|
||||
asic = subpriv->dio.intr.asic;
|
||||
if (asic < 0)
|
||||
return; /* not an interrupt subdev */
|
||||
|
||||
subpriv->dio.intr.enabled_mask = 0;
|
||||
subpriv->dio.intr.active = 0;
|
||||
s->async->inttrig = 0;
|
||||
nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
|
||||
firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
|
||||
switch_page(dev, asic, PAGE_ENAB);
|
||||
for (port = firstport; port < firstport + nports; ++port) {
|
||||
/* disable all intrs for this subdev.. */
|
||||
outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t interrupt_pcmmio(int irq, void *d)
|
||||
{
|
||||
int asic, got1 = 0;
|
||||
@ -991,27 +682,6 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void pcmmio_stop_intr(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
int nports, firstport, asic, port;
|
||||
|
||||
asic = subpriv->dio.intr.asic;
|
||||
if (asic < 0)
|
||||
return; /* not an interrupt subdev */
|
||||
|
||||
subpriv->dio.intr.enabled_mask = 0;
|
||||
subpriv->dio.intr.active = 0;
|
||||
s->async->inttrig = 0;
|
||||
nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
|
||||
firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
|
||||
switch_page(dev, asic, PAGE_ENAB);
|
||||
for (port = firstport; port < firstport + nports; ++port) {
|
||||
/* disable all intrs for this subdev.. */
|
||||
outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
|
||||
}
|
||||
}
|
||||
|
||||
static int pcmmio_start_intr(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s)
|
||||
{
|
||||
@ -1340,21 +1010,276 @@ static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
|
||||
return n;
|
||||
}
|
||||
|
||||
static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||
{
|
||||
struct comedi_subdevice *s;
|
||||
int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
|
||||
thisasic_chanct = 0;
|
||||
unsigned long iobase;
|
||||
unsigned int irq[MAX_ASICS];
|
||||
|
||||
iobase = it->options[0];
|
||||
irq[0] = it->options[1];
|
||||
|
||||
printk(KERN_INFO "comedi%d: %s: io: %lx attaching...\n", dev->minor,
|
||||
driver.driver_name, iobase);
|
||||
|
||||
dev->iobase = iobase;
|
||||
|
||||
if (!iobase || !request_region(iobase,
|
||||
thisboard->total_iosize,
|
||||
driver.driver_name)) {
|
||||
printk(KERN_ERR "comedi%d: I/O port conflict\n", dev->minor);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* A convenient macro that defines init_module() and cleanup_module(),
|
||||
* as necessary.
|
||||
* Initialize dev->board_name. Note that we can use the "thisboard"
|
||||
* macro now, since we just initialized it in the last line.
|
||||
*/
|
||||
dev->board_name = thisboard->name;
|
||||
|
||||
/*
|
||||
* Allocate the private structure area. alloc_private() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*/
|
||||
if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate private data structure\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (asic = 0; asic < MAX_ASICS; ++asic) {
|
||||
devpriv->asics[asic].num = asic;
|
||||
devpriv->asics[asic].iobase =
|
||||
dev->iobase + 16 + asic * ASIC_IOSIZE;
|
||||
/*
|
||||
* this gets actually set at the end of this function when we
|
||||
* request_irqs
|
||||
*/
|
||||
devpriv->asics[asic].irq = 0;
|
||||
spin_lock_init(&devpriv->asics[asic].spinlock);
|
||||
}
|
||||
|
||||
chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
|
||||
n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
|
||||
n_subdevs = n_dio_subdevs + 2;
|
||||
devpriv->sprivs =
|
||||
kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
|
||||
GFP_KERNEL);
|
||||
if (!devpriv->sprivs) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Allocate the subdevice structures. alloc_subdevice() is a
|
||||
* convenient macro defined in comedidev.h.
|
||||
*
|
||||
* Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
|
||||
*/
|
||||
if (alloc_subdevices(dev, n_subdevs) < 0) {
|
||||
printk(KERN_ERR "comedi%d: cannot allocate subdevice data structures\n",
|
||||
dev->minor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* First, AI */
|
||||
sdev_no = 0;
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = (1 << thisboard->ai_bits) - 1;
|
||||
s->range_table = thisboard->ai_range_table;
|
||||
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->n_chan = thisboard->n_ai_chans;
|
||||
s->len_chanlist = s->n_chan;
|
||||
s->insn_read = thisboard->ai_rinsn;
|
||||
subpriv->iobase = dev->iobase + 0;
|
||||
/* initialize the resource enable register by clearing it */
|
||||
outb(0, subpriv->iobase + 3);
|
||||
outb(0, subpriv->iobase + 4 + 3);
|
||||
|
||||
/* Next, AO */
|
||||
++sdev_no;
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = (1 << thisboard->ao_bits) - 1;
|
||||
s->range_table = thisboard->ao_range_table;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->n_chan = thisboard->n_ao_chans;
|
||||
s->len_chanlist = s->n_chan;
|
||||
s->insn_read = thisboard->ao_rinsn;
|
||||
s->insn_write = thisboard->ao_winsn;
|
||||
subpriv->iobase = dev->iobase + 8;
|
||||
/* initialize the resource enable register by clearing it */
|
||||
outb(0, subpriv->iobase + 3);
|
||||
outb(0, subpriv->iobase + 4 + 3);
|
||||
|
||||
++sdev_no;
|
||||
port = 0;
|
||||
asic = 0;
|
||||
for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
|
||||
int byte_no;
|
||||
|
||||
s = dev->subdevices + sdev_no;
|
||||
s->private = devpriv->sprivs + sdev_no;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->insn_bits = pcmmio_dio_insn_bits;
|
||||
s->insn_config = pcmmio_dio_insn_config;
|
||||
s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
|
||||
subpriv->dio.intr.asic = -1;
|
||||
subpriv->dio.intr.first_chan = -1;
|
||||
subpriv->dio.intr.asic_chan = -1;
|
||||
subpriv->dio.intr.num_asic_chans = -1;
|
||||
subpriv->dio.intr.active = 0;
|
||||
s->len_chanlist = 1;
|
||||
|
||||
/* save the ioport address for each 'port' of 8 channels in the
|
||||
subdevice */
|
||||
for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
|
||||
if (port >= PORTS_PER_ASIC) {
|
||||
port = 0;
|
||||
++asic;
|
||||
thisasic_chanct = 0;
|
||||
}
|
||||
subpriv->iobases[byte_no] =
|
||||
devpriv->asics[asic].iobase + port;
|
||||
|
||||
if (thisasic_chanct <
|
||||
CHANS_PER_PORT * INTR_PORTS_PER_ASIC
|
||||
&& subpriv->dio.intr.asic < 0) {
|
||||
/*
|
||||
* this is an interrupt subdevice,
|
||||
* so setup the struct
|
||||
*/
|
||||
subpriv->dio.intr.asic = asic;
|
||||
subpriv->dio.intr.active = 0;
|
||||
subpriv->dio.intr.stop_count = 0;
|
||||
subpriv->dio.intr.first_chan = byte_no * 8;
|
||||
subpriv->dio.intr.asic_chan = thisasic_chanct;
|
||||
subpriv->dio.intr.num_asic_chans =
|
||||
s->n_chan - subpriv->dio.intr.first_chan;
|
||||
s->cancel = pcmmio_cancel;
|
||||
s->do_cmd = pcmmio_cmd;
|
||||
s->do_cmdtest = pcmmio_cmdtest;
|
||||
s->len_chanlist =
|
||||
subpriv->dio.intr.num_asic_chans;
|
||||
}
|
||||
thisasic_chanct += CHANS_PER_PORT;
|
||||
}
|
||||
spin_lock_init(&subpriv->dio.intr.spinlock);
|
||||
|
||||
chans_left -= s->n_chan;
|
||||
|
||||
if (!chans_left) {
|
||||
/*
|
||||
* reset the asic to our first asic,
|
||||
* to do intr subdevs
|
||||
*/
|
||||
asic = 0;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init_asics(dev); /* clear out all the registers, basically */
|
||||
|
||||
for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
|
||||
if (irq[asic]
|
||||
&& request_irq(irq[asic], interrupt_pcmmio,
|
||||
IRQF_SHARED, thisboard->name, dev)) {
|
||||
int i;
|
||||
/* unroll the allocated irqs.. */
|
||||
for (i = asic - 1; i >= 0; --i) {
|
||||
free_irq(irq[i], dev);
|
||||
devpriv->asics[i].irq = irq[i] = 0;
|
||||
}
|
||||
irq[asic] = 0;
|
||||
}
|
||||
devpriv->asics[asic].irq = irq[asic];
|
||||
}
|
||||
|
||||
dev->irq = irq[0]; /*
|
||||
* grr.. wish comedi dev struct supported
|
||||
* multiple irqs..
|
||||
*/
|
||||
|
||||
if (irq[0]) {
|
||||
printk(KERN_DEBUG "comedi%d: irq: %u\n", dev->minor, irq[0]);
|
||||
if (thisboard->dio_num_asics == 2 && irq[1])
|
||||
printk(KERN_DEBUG "comedi%d: second ASIC irq: %u\n",
|
||||
dev->minor, irq[1]);
|
||||
} else {
|
||||
printk(KERN_INFO "comedi%d: (IRQ mode disabled)\n", dev->minor);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "comedi%d: attached\n", dev->minor);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pcmmio_detach(struct comedi_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "comedi%d: %s: remove\n", dev->minor, driver.driver_name);
|
||||
if (dev->iobase)
|
||||
release_region(dev->iobase, thisboard->total_iosize);
|
||||
|
||||
for (i = 0; i < MAX_ASICS; ++i) {
|
||||
if (devpriv && devpriv->asics[i].irq)
|
||||
free_irq(devpriv->asics[i].irq, dev);
|
||||
}
|
||||
|
||||
if (devpriv && devpriv->sprivs)
|
||||
kfree(devpriv->sprivs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pcmmio_board pcmmio_boards[] = {
|
||||
{
|
||||
.name = "pcmmio",
|
||||
.dio_num_asics = 1,
|
||||
.dio_num_ports = 6,
|
||||
.total_iosize = 32,
|
||||
.ai_bits = 16,
|
||||
.ao_bits = 16,
|
||||
.n_ai_chans = 16,
|
||||
.n_ao_chans = 8,
|
||||
.ai_range_table = &ranges_ai,
|
||||
.ao_range_table = &ranges_ao,
|
||||
.ai_rinsn = ai_rinsn,
|
||||
.ao_rinsn = ao_rinsn,
|
||||
.ao_winsn = ao_winsn
|
||||
},
|
||||
};
|
||||
|
||||
static struct comedi_driver driver = {
|
||||
.driver_name = "pcmmio",
|
||||
.module = THIS_MODULE,
|
||||
.attach = pcmmio_attach,
|
||||
.detach = pcmmio_detach,
|
||||
.board_name = &pcmmio_boards[0].name,
|
||||
.offset = sizeof(struct pcmmio_board),
|
||||
.num_names = ARRAY_SIZE(pcmmio_boards),
|
||||
};
|
||||
|
||||
static int __init driver_init_module(void)
|
||||
{
|
||||
return comedi_driver_register(&driver);
|
||||
}
|
||||
module_init(driver_init_module);
|
||||
|
||||
static void __exit driver_cleanup_module(void)
|
||||
{
|
||||
comedi_driver_unregister(&driver);
|
||||
}
|
||||
|
||||
module_init(driver_init_module);
|
||||
module_exit(driver_cleanup_module);
|
||||
|
||||
MODULE_AUTHOR("Comedi http://www.comedi.org");
|
||||
|
Loading…
Reference in New Issue
Block a user