From 6b19f9c6bee7afcd1e5dee9528333b2aa52de404 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 27 Apr 2012 15:28:57 -0700 Subject: [PATCH] staging: comedi: refactor pcmuio driver to remove forward declarations Refactor the switch_page and pcmuio_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 Cc: Ian Abbott Cc: Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/pcmuio.c | 1175 +++++++++++------------ 1 file changed, 553 insertions(+), 622 deletions(-) diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c index 661ba2e03892..de4f4865bef1 100644 --- a/drivers/staging/comedi/drivers/pcmuio.c +++ b/drivers/staging/comedi/drivers/pcmuio.c @@ -155,19 +155,6 @@ struct pcmuio_board { const int num_ports; }; -static const struct pcmuio_board pcmuio_boards[] = { - { - .name = "pcmuio48", - .num_asics = 1, - .num_ports = 6, - }, - { - .name = "pcmuio96", - .num_asics = 2, - .num_ports = 12, - }, -}; - /* * Useful for shorthand access to the particular board structure */ @@ -218,72 +205,546 @@ struct pcmuio_private { */ #define devpriv ((struct pcmuio_private *)dev->private) #define subpriv ((struct pcmuio_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 pcmuio_attach(struct comedi_device *dev, - struct comedi_devconfig *it); -static int pcmuio_detach(struct comedi_device *dev); - -static struct comedi_driver driver = { - .driver_name = "pcmuio", - .module = THIS_MODULE, - .attach = pcmuio_attach, - .detach = pcmuio_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 pcmuio_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 = &pcmuio_boards[0].name, - .offset = sizeof(struct pcmuio_board), - .num_names = ARRAY_SIZE(pcmuio_boards), -}; +/* DIO devices are slightly special. Although it is possible to + * implement the insn_read/insn_write interface, it is much more + * useful to applications if you implement the insn_bits interface. + * This allows packed reading/writing of the DIO channels. The + * comedi core can convert between insn_bits and insn_read/write */ static int pcmuio_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); -static int pcmuio_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); + struct comedi_insn *insn, unsigned int *data) +{ + int byte_no; + if (insn->n != 2) + return -EINVAL; -static irqreturn_t interrupt_pcmuio(int irq, void *d); -static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *); -static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s); -static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s); -static int pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_cmd *cmd); + /* NOTE: + reading a 0 means this channel was high + writine a 0 sets the channel high + reading a 1 means this channel was low + writing a 1 means set this channel low -/* some helper functions to deal with specifics of this device's registers */ -static void init_asics(struct comedi_device *dev); /* sets up/clears ASIC chips to defaults */ -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); + Therefore everything is always inverted. */ + + /* The insn data is a mask in data[0] and the new data + * in data[1], each channel cooresponding to a bit. */ + +#ifdef DAMMIT_ITS_BROKEN + /* DEBUG */ + dev_dbg(dev->hw_dev, "write mask: %08x data: %08x\n", data[0], + data[1]); #endif + s->state = 0; + + for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) { + /* address of 8-bit port */ + unsigned long ioaddr = subpriv->iobases[byte_no], + /* bit offset of port in 32-bit doubleword */ + offset = byte_no * 8; + /* this 8-bit port's data */ + unsigned char byte = 0, + /* The write mask for this port (if any) */ + write_mask_byte = (data[0] >> offset) & 0xff, + /* The data byte for this port */ + data_byte = (data[1] >> offset) & 0xff; + + byte = inb(ioaddr); /* read all 8-bits for this port */ + +#ifdef DAMMIT_ITS_BROKEN + /* DEBUG */ + printk + ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", + byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, + offset, ioaddr, (unsigned)byte); +#endif + + if (write_mask_byte) { + /* this byte has some write_bits -- so set the output lines */ + byte &= ~write_mask_byte; /* clear bits for write mask */ + byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */ + /* Write out the new digital output state */ + outb(byte, ioaddr); + } +#ifdef DAMMIT_ITS_BROKEN + /* DEBUG */ + dev_dbg(dev->hw_dev, "data_out_byte %02x\n", (unsigned)byte); +#endif + /* save the digital input lines for this byte.. */ + s->state |= ((unsigned int)byte) << offset; + } + + /* now return the DIO lines to data[1] - note they came inverted! */ + data[1] = ~s->state; + +#ifdef DAMMIT_ITS_BROKEN + /* DEBUG */ + dev_dbg(dev->hw_dev, "s->state %08x data_out %08x\n", s->state, + data[1]); +#endif + + return 2; +} + +/* The input or output configuration of each digital line is + * configured by a special insn_config instruction. chanspec + * contains the channel to be changed, and data[0] contains the + * value COMEDI_INPUT or COMEDI_OUTPUT. */ +static int pcmuio_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data) +{ + int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no = + chan % 8; + unsigned long ioaddr; + unsigned char byte; + + /* Compute ioaddr for this channel */ + ioaddr = subpriv->iobases[byte_no]; + + /* NOTE: + writing a 0 an IO channel's bit sets the channel to INPUT + and pulls the line high as well + + writing a 1 to an IO channel's bit pulls the line low + + All channels are implicitly always in OUTPUT mode -- but when + they are high they can be considered to be in INPUT mode.. + + Thus, we only force channels low if the config request was INPUT, + otherwise we do nothing to the hardware. */ + + switch (data[0]) { + case INSN_CONFIG_DIO_OUTPUT: + /* save to io_bits -- don't actually do anything since + all input channels are also output channels... */ + s->io_bits |= 1 << chan; + break; + case INSN_CONFIG_DIO_INPUT: + /* write a 0 to the actual register representing the channel + to set it to 'input'. 0 means "float high". */ + byte = inb(ioaddr); + byte &= ~(1 << bit_no); + /**< set input channel to '0' */ + + /* write out byte -- this is the only time we actually affect the + hardware as all channels are implicitly output -- but input + channels are set to float-high */ + outb(byte, ioaddr); + + /* save to io_bits */ + s->io_bits &= ~(1 << chan); + break; + + case INSN_CONFIG_DIO_QUERY: + /* retrieve from shadow register */ + data[1] = + (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; + return insn->n; + break; + + default: + return -EINVAL; + break; + } + + return insn->n; +} + +static void switch_page(struct comedi_device *dev, int asic, int page) +{ + if (asic < 0 || asic >= thisboard->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, + dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); +} + +static void init_asics(struct comedi_device *dev) +{ /* sets up an + ASIC chip to defaults */ + int asic; + + for (asic = 0; asic < thisboard->num_asics; ++asic) { + int port, page; + unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE; + + switch_page(dev, asic, 0); /* switch back to page 0 */ + + /* first, clear all the DIO port bits */ + for (port = 0; port < PORTS_PER_ASIC; ++port) + outb(0, baseaddr + REG_PORT0 + port); + + /* Next, clear all the paged registers for each page */ + for (page = 1; page < NUM_PAGES; ++page) { + int reg; + /* now clear all the paged registers */ + switch_page(dev, asic, page); + for (reg = FIRST_PAGED_REG; + reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg) + outb(0, baseaddr + reg); + } + + /* DEBUG set rising edge interrupts on port0 of both asics */ + /*switch_page(dev, asic, PAGE_POL); + outb(0xff, baseaddr + REG_POL0); + switch_page(dev, asic, PAGE_ENAB); + outb(0xff, baseaddr + REG_ENAB0); */ + /* END DEBUG */ + + switch_page(dev, asic, 0); /* switch back to default page 0 */ + + } +} + +#ifdef notused +static void lock_port(struct comedi_device *dev, int asic, int port) +{ + if (asic < 0 || asic >= thisboard->num_asics) + return; /* paranoia */ + if (port < 0 || port >= PORTS_PER_ASIC) + return; /* more paranoia */ + + devpriv->asics[asic].pagelock |= 0x1 << port; + /* now write out the shadow register */ + outb(devpriv->asics[asic].pagelock, + dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); +} + +static void unlock_port(struct comedi_device *dev, int asic, int port) +{ + if (asic < 0 || asic >= thisboard->num_asics) + return; /* paranoia */ + if (port < 0 || port >= PORTS_PER_ASIC) + return; /* more paranoia */ + devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK; + /* now write out the shadow register */ + outb(devpriv->asics[asic].pagelock, + dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); +} +#endif /* notused */ + +static void pcmuio_stop_intr(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + int nports, firstport, asic, port; + + asic = subpriv->intr.asic; + if (asic < 0) + return; /* not an interrupt subdev */ + + subpriv->intr.enabled_mask = 0; + subpriv->intr.active = 0; + s->async->inttrig = 0; + nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; + firstport = subpriv->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_pcmuio(int irq, void *d) +{ + int asic, got1 = 0; + struct comedi_device *dev = (struct comedi_device *)d; + + for (asic = 0; asic < MAX_ASICS; ++asic) { + if (irq == devpriv->asics[asic].irq) { + unsigned long flags; + unsigned triggered = 0; + unsigned long iobase = devpriv->asics[asic].iobase; + /* it is an interrupt for ASIC #asic */ + unsigned char int_pend; + + spin_lock_irqsave(&devpriv->asics[asic].spinlock, + flags); + + int_pend = inb(iobase + REG_INT_PENDING) & 0x07; + + if (int_pend) { + int port; + for (port = 0; port < INTR_PORTS_PER_ASIC; + ++port) { + if (int_pend & (0x1 << port)) { + unsigned char + io_lines_with_edges = 0; + switch_page(dev, asic, + PAGE_INT_ID); + io_lines_with_edges = + inb(iobase + + REG_INT_ID0 + port); + + if (io_lines_with_edges) + /* clear pending interrupt */ + outb(0, iobase + + REG_INT_ID0 + + port); + + triggered |= + io_lines_with_edges << + port * 8; + } + } + + ++got1; + } + + spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, + flags); + + if (triggered) { + struct comedi_subdevice *s; + /* TODO here: dispatch io lines to subdevs with commands.. */ + printk + ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", + irq, asic, triggered); + for (s = dev->subdevices; + s < dev->subdevices + dev->n_subdevices; + ++s) { + if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */ + unsigned long flags; + unsigned oldevents; + + spin_lock_irqsave(&subpriv-> + intr.spinlock, + flags); + + oldevents = s->async->events; + + if (subpriv->intr.active) { + unsigned mytrig = + ((triggered >> + subpriv->intr.asic_chan) + & + ((0x1 << subpriv-> + intr. + num_asic_chans) - + 1)) << subpriv-> + intr.first_chan; + if (mytrig & + subpriv->intr.enabled_mask) + { + unsigned int val + = 0; + unsigned int n, + ch, len; + + len = + s-> + async->cmd.chanlist_len; + for (n = 0; + n < len; + n++) { + ch = CR_CHAN(s->async->cmd.chanlist[n]); + if (mytrig & (1U << ch)) { + val |= (1U << n); + } + } + /* Write the scan to the buffer. */ + if (comedi_buf_put(s->async, ((short *)&val)[0]) + && + comedi_buf_put + (s->async, + ((short *) + &val)[1])) + { + s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); + } else { + /* Overflow! Stop acquisition!! */ + /* TODO: STOP_ACQUISITION_CALL_HERE!! */ + pcmuio_stop_intr + (dev, + s); + } + + /* Check for end of acquisition. */ + if (!subpriv->intr.continuous) { + /* stop_src == TRIG_COUNT */ + if (subpriv->intr.stop_count > 0) { + subpriv->intr.stop_count--; + if (subpriv->intr.stop_count == 0) { + s->async->events |= COMEDI_CB_EOA; + /* TODO: STOP_ACQUISITION_CALL_HERE!! */ + pcmuio_stop_intr + (dev, + s); + } + } + } + } + } + + spin_unlock_irqrestore + (&subpriv->intr.spinlock, + flags); + + if (oldevents != + s->async->events) { + comedi_event(dev, s); + } + + } + + } + } + + } + } + if (!got1) + return IRQ_NONE; /* interrupt from other source */ + return IRQ_HANDLED; +} + +static int pcmuio_start_intr(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) { + /* An empty acquisition! */ + s->async->events |= COMEDI_CB_EOA; + subpriv->intr.active = 0; + return 1; + } else { + unsigned bits = 0, pol_bits = 0, n; + int nports, firstport, asic, port; + struct comedi_cmd *cmd = &s->async->cmd; + + asic = subpriv->intr.asic; + if (asic < 0) + return 1; /* not an interrupt + subdev */ + subpriv->intr.enabled_mask = 0; + subpriv->intr.active = 1; + nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; + firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; + if (cmd->chanlist) { + for (n = 0; n < cmd->chanlist_len; n++) { + bits |= (1U << CR_CHAN(cmd->chanlist[n])); + pol_bits |= (CR_AREF(cmd->chanlist[n]) + || CR_RANGE(cmd-> + chanlist[n]) ? 1U : 0U) + << CR_CHAN(cmd->chanlist[n]); + } + } + bits &= ((0x1 << subpriv->intr.num_asic_chans) - + 1) << subpriv->intr.first_chan; + subpriv->intr.enabled_mask = bits; + + switch_page(dev, asic, PAGE_ENAB); + for (port = firstport; port < firstport + nports; ++port) { + unsigned enab = + bits >> (subpriv->intr.first_chan + (port - + firstport) * + 8) & 0xff, pol = + pol_bits >> (subpriv->intr.first_chan + + (port - firstport) * 8) & 0xff; + /* set enab intrs for this subdev.. */ + outb(enab, + devpriv->asics[asic].iobase + REG_ENAB0 + port); + switch_page(dev, asic, PAGE_POL); + outb(pol, + devpriv->asics[asic].iobase + REG_ENAB0 + port); + } + } + return 0; +} + +static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) +{ + unsigned long flags; + + spin_lock_irqsave(&subpriv->intr.spinlock, flags); + if (subpriv->intr.active) + pcmuio_stop_intr(dev, s); + spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); + + return 0; +} + /* - * 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. + * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice. */ +static int +pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s, + unsigned int trignum) +{ + unsigned long flags; + int event = 0; + + if (trignum != 0) + return -EINVAL; + + spin_lock_irqsave(&subpriv->intr.spinlock, flags); + s->async->inttrig = 0; + if (subpriv->intr.active) + event = pcmuio_start_intr(dev, s); + + spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); + + if (event) + comedi_event(dev, s); + + return 1; +} + +/* + * 'do_cmd' function for an 'INTERRUPT' subdevice. + */ +static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) +{ + struct comedi_cmd *cmd = &s->async->cmd; + unsigned long flags; + int event = 0; + + spin_lock_irqsave(&subpriv->intr.spinlock, flags); + subpriv->intr.active = 1; + + /* Set up end of acquisition. */ + switch (cmd->stop_src) { + case TRIG_COUNT: + subpriv->intr.continuous = 0; + subpriv->intr.stop_count = cmd->stop_arg; + break; + default: + /* TRIG_NONE */ + subpriv->intr.continuous = 1; + subpriv->intr.stop_count = 0; + break; + } + + /* Set up start of acquisition. */ + switch (cmd->start_src) { + case TRIG_INT: + s->async->inttrig = pcmuio_inttrig_start_intr; + break; + default: + /* TRIG_NOW */ + event = pcmuio_start_intr(dev, s); + break; + } + spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); + + if (event) + comedi_event(dev, s); + + return 0; +} + +static int +pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_cmd *cmd) +{ + return comedi_pcm_cmdtest(dev, s, cmd); +} + static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) { struct comedi_subdevice *s; @@ -447,14 +908,6 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) 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 pcmuio_detach(struct comedi_device *dev) { int i; @@ -475,560 +928,38 @@ static int pcmuio_detach(struct comedi_device *dev) return 0; } -/* DIO devices are slightly special. Although it is possible to - * implement the insn_read/insn_write interface, it is much more - * useful to applications if you implement the insn_bits interface. - * This allows packed reading/writing of the DIO channels. The - * comedi core can convert between insn_bits and insn_read/write */ -static int pcmuio_dio_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - int byte_no; - if (insn->n != 2) - return -EINVAL; +static const struct pcmuio_board pcmuio_boards[] = { + { + .name = "pcmuio48", + .num_asics = 1, + .num_ports = 6, + }, { + .name = "pcmuio96", + .num_asics = 2, + .num_ports = 12, + }, +}; + +static struct comedi_driver driver = { + .driver_name = "pcmuio", + .module = THIS_MODULE, + .attach = pcmuio_attach, + .detach = pcmuio_detach, + .board_name = &pcmuio_boards[0].name, + .offset = sizeof(struct pcmuio_board), + .num_names = ARRAY_SIZE(pcmuio_boards), +}; - /* NOTE: - reading a 0 means this channel was high - writine a 0 sets the channel high - reading a 1 means this channel was low - writing a 1 means set this channel low - - Therefore everything is always inverted. */ - - /* The insn data is a mask in data[0] and the new data - * in data[1], each channel cooresponding to a bit. */ - -#ifdef DAMMIT_ITS_BROKEN - /* DEBUG */ - dev_dbg(dev->hw_dev, "write mask: %08x data: %08x\n", data[0], - data[1]); -#endif - - s->state = 0; - - for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) { - /* address of 8-bit port */ - unsigned long ioaddr = subpriv->iobases[byte_no], - /* bit offset of port in 32-bit doubleword */ - offset = byte_no * 8; - /* this 8-bit port's data */ - unsigned char byte = 0, - /* The write mask for this port (if any) */ - write_mask_byte = (data[0] >> offset) & 0xff, - /* The data byte for this port */ - data_byte = (data[1] >> offset) & 0xff; - - byte = inb(ioaddr); /* read all 8-bits for this port */ - -#ifdef DAMMIT_ITS_BROKEN - /* DEBUG */ - printk - ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", - byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, - offset, ioaddr, (unsigned)byte); -#endif - - if (write_mask_byte) { - /* this byte has some write_bits -- so set the output lines */ - byte &= ~write_mask_byte; /* clear bits for write mask */ - byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */ - /* Write out the new digital output state */ - outb(byte, ioaddr); - } -#ifdef DAMMIT_ITS_BROKEN - /* DEBUG */ - dev_dbg(dev->hw_dev, "data_out_byte %02x\n", (unsigned)byte); -#endif - /* save the digital input lines for this byte.. */ - s->state |= ((unsigned int)byte) << offset; - } - - /* now return the DIO lines to data[1] - note they came inverted! */ - data[1] = ~s->state; - -#ifdef DAMMIT_ITS_BROKEN - /* DEBUG */ - dev_dbg(dev->hw_dev, "s->state %08x data_out %08x\n", s->state, - data[1]); -#endif - - return 2; -} - -/* The input or output configuration of each digital line is - * configured by a special insn_config instruction. chanspec - * contains the channel to be changed, and data[0] contains the - * value COMEDI_INPUT or COMEDI_OUTPUT. */ -static int pcmuio_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no = - chan % 8; - unsigned long ioaddr; - unsigned char byte; - - /* Compute ioaddr for this channel */ - ioaddr = subpriv->iobases[byte_no]; - - /* NOTE: - writing a 0 an IO channel's bit sets the channel to INPUT - and pulls the line high as well - - writing a 1 to an IO channel's bit pulls the line low - - All channels are implicitly always in OUTPUT mode -- but when - they are high they can be considered to be in INPUT mode.. - - Thus, we only force channels low if the config request was INPUT, - otherwise we do nothing to the hardware. */ - - switch (data[0]) { - case INSN_CONFIG_DIO_OUTPUT: - /* save to io_bits -- don't actually do anything since - all input channels are also output channels... */ - s->io_bits |= 1 << chan; - break; - case INSN_CONFIG_DIO_INPUT: - /* write a 0 to the actual register representing the channel - to set it to 'input'. 0 means "float high". */ - byte = inb(ioaddr); - byte &= ~(1 << bit_no); - /**< set input channel to '0' */ - - /* write out byte -- this is the only time we actually affect the - hardware as all channels are implicitly output -- but input - channels are set to float-high */ - outb(byte, ioaddr); - - /* save to io_bits */ - s->io_bits &= ~(1 << chan); - break; - - case INSN_CONFIG_DIO_QUERY: - /* retrieve from shadow register */ - data[1] = - (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; - return insn->n; - break; - - default: - return -EINVAL; - break; - } - - return insn->n; -} - -static void init_asics(struct comedi_device *dev) -{ /* sets up an - ASIC chip to defaults */ - int asic; - - for (asic = 0; asic < thisboard->num_asics; ++asic) { - int port, page; - unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE; - - switch_page(dev, asic, 0); /* switch back to page 0 */ - - /* first, clear all the DIO port bits */ - for (port = 0; port < PORTS_PER_ASIC; ++port) - outb(0, baseaddr + REG_PORT0 + port); - - /* Next, clear all the paged registers for each page */ - for (page = 1; page < NUM_PAGES; ++page) { - int reg; - /* now clear all the paged registers */ - switch_page(dev, asic, page); - for (reg = FIRST_PAGED_REG; - reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg) - outb(0, baseaddr + reg); - } - - /* DEBUG set rising edge interrupts on port0 of both asics */ - /*switch_page(dev, asic, PAGE_POL); - outb(0xff, baseaddr + REG_POL0); - switch_page(dev, asic, PAGE_ENAB); - outb(0xff, baseaddr + REG_ENAB0); */ - /* END DEBUG */ - - switch_page(dev, asic, 0); /* switch back to default page 0 */ - - } -} - -static void switch_page(struct comedi_device *dev, int asic, int page) -{ - if (asic < 0 || asic >= thisboard->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, - dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); -} - -#ifdef notused -static void lock_port(struct comedi_device *dev, int asic, int port) -{ - if (asic < 0 || asic >= thisboard->num_asics) - return; /* paranoia */ - if (port < 0 || port >= PORTS_PER_ASIC) - return; /* more paranoia */ - - devpriv->asics[asic].pagelock |= 0x1 << port; - /* now write out the shadow register */ - outb(devpriv->asics[asic].pagelock, - dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); -} - -static void unlock_port(struct comedi_device *dev, int asic, int port) -{ - if (asic < 0 || asic >= thisboard->num_asics) - return; /* paranoia */ - if (port < 0 || port >= PORTS_PER_ASIC) - return; /* more paranoia */ - devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK; - /* now write out the shadow register */ - outb(devpriv->asics[asic].pagelock, - dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK); -} -#endif /* notused */ - -static irqreturn_t interrupt_pcmuio(int irq, void *d) -{ - int asic, got1 = 0; - struct comedi_device *dev = (struct comedi_device *)d; - - for (asic = 0; asic < MAX_ASICS; ++asic) { - if (irq == devpriv->asics[asic].irq) { - unsigned long flags; - unsigned triggered = 0; - unsigned long iobase = devpriv->asics[asic].iobase; - /* it is an interrupt for ASIC #asic */ - unsigned char int_pend; - - spin_lock_irqsave(&devpriv->asics[asic].spinlock, - flags); - - int_pend = inb(iobase + REG_INT_PENDING) & 0x07; - - if (int_pend) { - int port; - for (port = 0; port < INTR_PORTS_PER_ASIC; - ++port) { - if (int_pend & (0x1 << port)) { - unsigned char - io_lines_with_edges = 0; - switch_page(dev, asic, - PAGE_INT_ID); - io_lines_with_edges = - inb(iobase + - REG_INT_ID0 + port); - - if (io_lines_with_edges) - /* clear pending interrupt */ - outb(0, iobase + - REG_INT_ID0 + - port); - - triggered |= - io_lines_with_edges << - port * 8; - } - } - - ++got1; - } - - spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, - flags); - - if (triggered) { - struct comedi_subdevice *s; - /* TODO here: dispatch io lines to subdevs with commands.. */ - printk - ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", - irq, asic, triggered); - for (s = dev->subdevices; - s < dev->subdevices + dev->n_subdevices; - ++s) { - if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */ - unsigned long flags; - unsigned oldevents; - - spin_lock_irqsave(&subpriv-> - intr.spinlock, - flags); - - oldevents = s->async->events; - - if (subpriv->intr.active) { - unsigned mytrig = - ((triggered >> - subpriv->intr.asic_chan) - & - ((0x1 << subpriv-> - intr. - num_asic_chans) - - 1)) << subpriv-> - intr.first_chan; - if (mytrig & - subpriv->intr.enabled_mask) - { - unsigned int val - = 0; - unsigned int n, - ch, len; - - len = - s-> - async->cmd.chanlist_len; - for (n = 0; - n < len; - n++) { - ch = CR_CHAN(s->async->cmd.chanlist[n]); - if (mytrig & (1U << ch)) { - val |= (1U << n); - } - } - /* Write the scan to the buffer. */ - if (comedi_buf_put(s->async, ((short *)&val)[0]) - && - comedi_buf_put - (s->async, - ((short *) - &val)[1])) - { - s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); - } else { - /* Overflow! Stop acquisition!! */ - /* TODO: STOP_ACQUISITION_CALL_HERE!! */ - pcmuio_stop_intr - (dev, - s); - } - - /* Check for end of acquisition. */ - if (!subpriv->intr.continuous) { - /* stop_src == TRIG_COUNT */ - if (subpriv->intr.stop_count > 0) { - subpriv->intr.stop_count--; - if (subpriv->intr.stop_count == 0) { - s->async->events |= COMEDI_CB_EOA; - /* TODO: STOP_ACQUISITION_CALL_HERE!! */ - pcmuio_stop_intr - (dev, - s); - } - } - } - } - } - - spin_unlock_irqrestore - (&subpriv->intr.spinlock, - flags); - - if (oldevents != - s->async->events) { - comedi_event(dev, s); - } - - } - - } - } - - } - } - if (!got1) - return IRQ_NONE; /* interrupt from other source */ - return IRQ_HANDLED; -} - -static void pcmuio_stop_intr(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - int nports, firstport, asic, port; - - asic = subpriv->intr.asic; - if (asic < 0) - return; /* not an interrupt subdev */ - - subpriv->intr.enabled_mask = 0; - subpriv->intr.active = 0; - s->async->inttrig = 0; - nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; - firstport = subpriv->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 pcmuio_start_intr(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) { - /* An empty acquisition! */ - s->async->events |= COMEDI_CB_EOA; - subpriv->intr.active = 0; - return 1; - } else { - unsigned bits = 0, pol_bits = 0, n; - int nports, firstport, asic, port; - struct comedi_cmd *cmd = &s->async->cmd; - - asic = subpriv->intr.asic; - if (asic < 0) - return 1; /* not an interrupt - subdev */ - subpriv->intr.enabled_mask = 0; - subpriv->intr.active = 1; - nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT; - firstport = subpriv->intr.asic_chan / CHANS_PER_PORT; - if (cmd->chanlist) { - for (n = 0; n < cmd->chanlist_len; n++) { - bits |= (1U << CR_CHAN(cmd->chanlist[n])); - pol_bits |= (CR_AREF(cmd->chanlist[n]) - || CR_RANGE(cmd-> - chanlist[n]) ? 1U : 0U) - << CR_CHAN(cmd->chanlist[n]); - } - } - bits &= ((0x1 << subpriv->intr.num_asic_chans) - - 1) << subpriv->intr.first_chan; - subpriv->intr.enabled_mask = bits; - - switch_page(dev, asic, PAGE_ENAB); - for (port = firstport; port < firstport + nports; ++port) { - unsigned enab = - bits >> (subpriv->intr.first_chan + (port - - firstport) * - 8) & 0xff, pol = - pol_bits >> (subpriv->intr.first_chan + - (port - firstport) * 8) & 0xff; - /* set enab intrs for this subdev.. */ - outb(enab, - devpriv->asics[asic].iobase + REG_ENAB0 + port); - switch_page(dev, asic, PAGE_POL); - outb(pol, - devpriv->asics[asic].iobase + REG_ENAB0 + port); - } - } - return 0; -} - -static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) -{ - unsigned long flags; - - spin_lock_irqsave(&subpriv->intr.spinlock, flags); - if (subpriv->intr.active) - pcmuio_stop_intr(dev, s); - spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); - - return 0; -} - -/* - * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice. - */ -static int -pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s, - unsigned int trignum) -{ - unsigned long flags; - int event = 0; - - if (trignum != 0) - return -EINVAL; - - spin_lock_irqsave(&subpriv->intr.spinlock, flags); - s->async->inttrig = 0; - if (subpriv->intr.active) - event = pcmuio_start_intr(dev, s); - - spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); - - if (event) - comedi_event(dev, s); - - return 1; -} - -/* - * 'do_cmd' function for an 'INTERRUPT' subdevice. - */ -static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct comedi_cmd *cmd = &s->async->cmd; - unsigned long flags; - int event = 0; - - spin_lock_irqsave(&subpriv->intr.spinlock, flags); - subpriv->intr.active = 1; - - /* Set up end of acquisition. */ - switch (cmd->stop_src) { - case TRIG_COUNT: - subpriv->intr.continuous = 0; - subpriv->intr.stop_count = cmd->stop_arg; - break; - default: - /* TRIG_NONE */ - subpriv->intr.continuous = 1; - subpriv->intr.stop_count = 0; - break; - } - - /* Set up start of acquisition. */ - switch (cmd->start_src) { - case TRIG_INT: - s->async->inttrig = pcmuio_inttrig_start_intr; - break; - default: - /* TRIG_NOW */ - event = pcmuio_start_intr(dev, s); - break; - } - spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); - - if (event) - comedi_event(dev, s); - - return 0; -} - -static int -pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - return comedi_pcm_cmdtest(dev, s, cmd); -} - -/* - * A convenient macro that defines init_module() and cleanup_module(), - * as necessary. - */ 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");