staging: comedi: pcmmio: simplify pcmmio_dio_insn_bits()

Currently this function uses the subdevice private data to get the
iobase address needed to update the dio output channel state and
read the current state of the input channels. This subdevice private
data is in the process of being removed.

Use the subdevice 'index' to determine the base 'port' needed to
access the correct digital i/o registers. The pcmmio_dio_write()
function can then be used to update the outputs.

Introduce a new helper function, pcmmio_dio_read(), to read the
current state of the input channels.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
H Hartley Sweeten 2013-12-09 15:31:01 -07:00 committed by Greg Kroah-Hartman
parent 72c7692a9d
commit 0398606c18
1 changed files with 60 additions and 51 deletions

View File

@ -282,63 +282,72 @@ static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
spin_unlock_irqrestore(&devpriv->pagelock, flags); spin_unlock_irqrestore(&devpriv->pagelock, flags);
} }
/* DIO devices are slightly special. Although it is possible to static unsigned int pcmmio_dio_read(struct comedi_device *dev,
* implement the insn_read/insn_write interface, it is much more int page, int port)
* useful to applications if you implement the insn_bits interface. {
* This allows packed reading/writing of the DIO channels. The struct pcmmio_private *devpriv = dev->private;
* comedi core can convert between insn_bits and insn_read/write */ unsigned long iobase = dev->iobase;
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&devpriv->pagelock, flags);
if (page == 0) {
/* Port registers are valid for any page */
val = inb(iobase + PCMMIO_PORT_REG(port + 0));
val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
} else {
outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
val = inb(iobase + PCMMIO_PAGE_REG(0));
val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
}
spin_unlock_irqrestore(&devpriv->pagelock, flags);
return val;
}
/*
* Each channel can be individually programmed for input or output.
* Writing a '0' to a channel causes the corresponding output pin
* to go to a high-z state (pulled high by an external 10K resistor).
* This allows it to be used as an input. When used in the input mode,
* a read reflects the inverted state of the I/O pin, such that a
* high on the pin will read as a '0' in the register. Writing a '1'
* to a bit position causes the pin to sink current (up to 12mA),
* effectively pulling it low.
*/
static int pcmmio_dio_insn_bits(struct comedi_device *dev, static int pcmmio_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data) struct comedi_insn *insn,
unsigned int *data)
{ {
struct pcmmio_subdev_private *subpriv = s->private; /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
int byte_no; int port = s->index == 2 ? 0 : 3;
unsigned int chanmask = (1 << s->n_chan) - 1;
unsigned int mask;
unsigned int val;
/* NOTE: mask = comedi_dio_update_state(s, data);
reading a 0 means this channel was high if (mask) {
writine a 0 sets the channel high /*
reading a 1 means this channel was low * Outputs are inverted, invert the state and
writing a 1 means set this channel low * update the channels.
*
Therefore everything is always inverted. */ * The s->io_bits mask makes sure the input channels
* are '0' so that the outputs pins stay in a high
/* The insn data is a mask in data[0] and the new data * z-state.
* in data[1], each channel cooresponding to a bit. */ */
val = ~s->state & chanmask;
s->state = 0; val &= s->io_bits;
pcmmio_dio_write(dev, val, 0, port);
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 */
if (write_mask_byte) {
/*
* this byte has some write_bits
* -- so set the output lines
*/
/* clear bits for write mask */
byte &= ~write_mask_byte;
/* set to inverted data_byte */
byte |= ~data_byte & write_mask_byte;
/* Write out the new digital output state */
outb(byte, ioaddr);
}
/* 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! */ /* get inverted state of the channels from the port */
data[1] = ~s->state; val = pcmmio_dio_read(dev, 0, port);
/* return the true state of the channels */
data[1] = ~val & chanmask;
return insn->n; return insn->n;
} }