[PATCH 13/30] staging: comedi: amplc_dio200: internalize 8255 DIO implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Implement the '8255' DIO subdevice internally to this module instead of
using the external "8255" module.  I plan to add support for additional
cards to this driver that would require the I/O callback functionality
of the 8255 module, but the existing callback functions do not have much
context to handle this elegantly.  The additional cards also have extra
DIO features which cannot be handled by the existing "8255" module and
that I'd like to support some time in the future.

The bottom line is I _could_ continue using the "8255" module for a
while with a callback function, but it would turn out to be a very ugly
callback function and I'd have to ditch the use of the "8255" module as
soon as I added an extra feature to the DIO subdevice.

Signed-off-by: Ian Abbott <abbotti@xxxxxxxxx>
---
 drivers/staging/comedi/drivers/amplc_dio200.c | 147 +++++++++++++++++++++++++-
 1 file changed, 143 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
index d874bdf..0c824a9 100644
--- a/drivers/staging/comedi/drivers/amplc_dio200.c
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -207,7 +207,6 @@
 #include "../comedidev.h"
 
 #include "comedi_fc.h"
-#include "8255.h"
 #include "8253.h"
 
 #define DIO200_DRIVER_NAME	"amplc_dio200"
@@ -221,6 +220,15 @@
 #define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
 #define PCI_DEVICE_ID_INVALID 0xffff
 
+/* 8255 control register bits */
+#define CR_C_LO_IO	0x01
+#define CR_B_IO		0x02
+#define CR_B_MODE	0x04
+#define CR_C_HI_IO	0x08
+#define CR_A_IO		0x10
+#define CR_A_MODE(a)	((a)<<5)
+#define CR_CW		0x80
+
 /* 200 series registers */
 #define DIO200_IO_SIZE		0x20
 #define DIO200_XCLK_SCE		0x18	/* Group X clock selection register */
@@ -430,6 +438,10 @@ struct dio200_subdev_8254 {
 	spinlock_t spinlock;
 };
 
+struct dio200_subdev_8255 {
+	unsigned int ofs;		/* DIO base offset */
+};
+
 struct dio200_subdev_intr {
 	unsigned int ofs;
 	spinlock_t spinlock;
@@ -1245,6 +1257,133 @@ dio200_subdev_8254_cleanup(struct comedi_device *dev,
 	kfree(subpriv);
 }
 
+/*
+ * This function sets I/O directions for an '8255' DIO subdevice.
+ */
+static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
+				       struct comedi_subdevice *s)
+{
+	struct dio200_subdev_8255 *subpriv = s->private;
+	int config;
+
+	config = CR_CW;
+	/* 1 in io_bits indicates output, 1 in config indicates input */
+	if (!(s->io_bits & 0x0000ff))
+		config |= CR_A_IO;
+	if (!(s->io_bits & 0x00ff00))
+		config |= CR_B_IO;
+	if (!(s->io_bits & 0x0f0000))
+		config |= CR_C_LO_IO;
+	if (!(s->io_bits & 0xf00000))
+		config |= CR_C_HI_IO;
+	outb(config, dev->iobase + subpriv->ofs + 3);
+}
+
+/*
+ * Handle 'insn_bits' for an '8255' DIO subdevice.
+ */
+static int dio200_subdev_8255_bits(struct comedi_device *dev,
+				   struct comedi_subdevice *s,
+				   struct comedi_insn *insn, unsigned int *data)
+{
+	struct dio200_subdev_8255 *subpriv = s->private;
+
+	if (data[0]) {
+		s->state &= ~data[0];
+		s->state |= (data[0] & data[1]);
+		if (data[0] & 0xff)
+			outb(s->state & 0xff, dev->iobase + subpriv->ofs);
+		if (data[0] & 0xff00)
+			outb((s->state >> 8) & 0xff,
+			     dev->iobase + subpriv->ofs + 1);
+		if (data[0] & 0xff0000)
+			outb((s->state >> 16) & 0xff,
+			     dev->iobase + subpriv->ofs + 2);
+	}
+	data[1] = inb(dev->iobase + subpriv->ofs);
+	data[1] |= inb(dev->iobase + subpriv->ofs + 1) << 8;
+	data[1] |= inb(dev->iobase + subpriv->ofs + 2) << 16;
+	return 2;
+}
+
+/*
+ * Handle 'insn_config' for an '8255' DIO subdevice.
+ */
+static int dio200_subdev_8255_config(struct comedi_device *dev,
+				     struct comedi_subdevice *s,
+				     struct comedi_insn *insn,
+				     unsigned int *data)
+{
+	unsigned int mask;
+	unsigned int bits;
+
+	mask = 1 << CR_CHAN(insn->chanspec);
+	if (mask & 0x0000ff)
+		bits = 0x0000ff;
+	else if (mask & 0x00ff00)
+		bits = 0x00ff00;
+	else if (mask & 0x0f0000)
+		bits = 0x0f0000;
+	else
+		bits = 0xf00000;
+	switch (data[0]) {
+	case INSN_CONFIG_DIO_INPUT:
+		s->io_bits &= ~bits;
+		break;
+	case INSN_CONFIG_DIO_OUTPUT:
+		s->io_bits |= bits;
+		break;
+	case INSN_CONFIG_DIO_QUERY:
+		data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
+		return insn->n;
+		break;
+	default:
+		return -EINVAL;
+	}
+	dio200_subdev_8255_set_dir(dev, s);
+	return 1;
+}
+
+/*
+ * This function initializes an '8255' DIO subdevice.
+ *
+ * offset is the offset to the 8255 chip.
+ */
+static int dio200_subdev_8255_init(struct comedi_device *dev,
+				   struct comedi_subdevice *s,
+				   unsigned int offset)
+{
+	struct dio200_subdev_8255 *subpriv;
+
+	subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+	if (!subpriv)
+		return -ENOMEM;
+	subpriv->ofs = offset;
+	s->private = subpriv;
+	s->type = COMEDI_SUBD_DIO;
+	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+	s->n_chan = 24;
+	s->range_table = &range_digital;
+	s->maxdata = 1;
+	s->insn_bits = dio200_subdev_8255_bits;
+	s->insn_config = dio200_subdev_8255_config;
+	s->state = 0;
+	s->io_bits = 0;
+	dio200_subdev_8255_set_dir(dev, s);
+	return 0;
+}
+
+/*
+ * This function cleans up an '8255' DIO subdevice.
+ */
+static void dio200_subdev_8255_cleanup(struct comedi_device *dev,
+				       struct comedi_subdevice *s)
+{
+	struct dio200_subdev_8255 *subpriv = s->private;
+
+	kfree(subpriv);
+}
+
 static void dio200_report_attach(struct comedi_device *dev, unsigned int irq)
 {
 	const struct dio200_board *thisboard = comedi_board(dev);
@@ -1301,8 +1440,8 @@ static int dio200_common_attach(struct comedi_device *dev, unsigned long iobase,
 			break;
 		case sd_8255:
 			/* digital i/o subdevice (8255) */
-			ret = subdev_8255_init(dev, s, NULL,
-					       iobase + layout->sdinfo[n]);
+			ret = dio200_subdev_8255_init(dev, s,
+						      layout->sdinfo[n]);
 			if (ret < 0)
 				return ret;
 			break;
@@ -1438,7 +1577,7 @@ static void dio200_detach(struct comedi_device *dev)
 				dio200_subdev_8254_cleanup(dev, s);
 				break;
 			case sd_8255:
-				subdev_8255_cleanup(dev, s);
+				dio200_subdev_8255_cleanup(dev, s);
 				break;
 			case sd_intr:
 				dio200_subdev_intr_cleanup(dev, s);
-- 
1.7.12.4

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel


[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux