Commit da6578ab authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman
Browse files

staging: comedi: addi_apci_3xxx: refactor the ttl digital i/o support



Currently, the subdevice functions used to configure and read/write the
ttl digital i/o ports is way over to complicated. The (*insn_config)
function also abuses the comedi API by overriding the instruction
command passed in data[0].

Fix the ttl digital i/o support to work like the comedi core expects.

The (*insn_config) function supports the three instructions common for
COMEDI_SUBD_DIO subdevices:

    INSN_CONFIG_DIO_INPUT - configure the specified channel as input
    INSN_CONFIG_DIO_OUTPUT - configure the specified channel as output
    INSN_CONFIG_DIO_QUERY - returns the status of the specified channel

    Port 0 (channels 0-7) is always input
    Port 1 (channels 8-15) is always output
    Port 2 (channels 9-23) are programmable i/o (all channels are input or output)

The (*insn_bits) function allows writing to the output channels and returns
the state of all channels.

The (*insn_read) and (*insn_write) functions are not required. The comedi
core will emulate them using the (*insn_bits) function.

Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0e771e49
Loading
Loading
Loading
Loading
+0 −520
Original line number Diff line number Diff line
@@ -514,523 +514,3 @@ static int i_APCI3XXX_InsnReadAnalogInput(struct comedi_device *dev,
	}
	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
|                              TTL FUNCTIONS                                 |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : int   i_APCI3XXX_InsnConfigInitTTLIO                   |
|                          (struct comedi_device    *dev,                           |
|                           struct comedi_subdevice *s,                             |
|                           struct comedi_insn      *insn,                          |
|                           unsigned int         *data)                          |
+----------------------------------------------------------------------------+
| Task           You must calling this function be                           |
|                for you call any other function witch access of TTL.        |
|                APCI3XXX_TTL_INIT_DIRECTION_PORT2(user inputs for direction)|
+----------------------------------------------------------------------------+
| Input Parameters  : b_InitType    = (unsigned char) data[0];                        |
|                     b_Port2Mode   = (unsigned char) data[1];                        |
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      :>0: No error                                            |
|                    -1: Port 2 mode selection is wrong                      |
|                    ....                                                    |
|                    -100 : Config command error                             |
|                    -101 : Data size error                                  |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnConfigInitTTLIO(struct comedi_device *dev,
					  struct comedi_subdevice *s,
					  struct comedi_insn *insn,
					  unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	int i_ReturnValue = insn->n;
	unsigned char b_Command = 0;

	/************************/
	/* Test the buffer size */
	/************************/

	if (insn->n >= 1) {
	   /*******************/
		/* Get the command */
		/* **************** */

		b_Command = (unsigned char) data[0];

	   /********************/
		/* Test the command */
	   /********************/

		if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
	      /***************************************/
			/* Test the initialisation buffer size */
	      /***************************************/

			if ((b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)
				&& (insn->n != 2)) {
		 /*******************/
				/* Data size error */
		 /*******************/

				printk("Buffer size error\n");
				i_ReturnValue = -101;
			}
		} else {
	      /************************/
			/* Config command error */
	      /************************/

			printk("Command selection error\n");
			i_ReturnValue = -100;
		}
	} else {
	   /*******************/
		/* Data size error */
	   /*******************/

		printk("Buffer size error\n");
		i_ReturnValue = -101;
	}

	/*********************************************************************************/
	/* Test if no error occur and APCI3XXX_TTL_INIT_DIRECTION_PORT2 command selected */
	/*********************************************************************************/

	if ((i_ReturnValue >= 0)
		&& (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)) {
	   /**********************/
		/* Test the direction */
	   /**********************/

		if ((data[1] == 0) || (data[1] == 0xFF)) {
	      /**************************/
			/* Save the configuration */
	      /**************************/

			devpriv->ul_TTLPortConfiguration[0] =
				devpriv->ul_TTLPortConfiguration[0] | data[1];
		} else {
	      /************************/
			/* Port direction error */
	      /************************/

			printk("Port 2 direction selection error\n");
			i_ReturnValue = -1;
		}
	}

	/**************************/
	/* Test if no error occur */
	/**************************/

	if (i_ReturnValue >= 0) {
	   /***********************************/
		/* Test if TTL port initilaisation */
	   /***********************************/

		if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
	      /*************************/
			/* Set the configuration */
	      /*************************/

			outl(data[1], devpriv->iobase + 224);
		}
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
|                        TTL INPUT FUNCTIONS                                 |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : int     i_APCI3XXX_InsnBitsTTLIO                       |
|                          (struct comedi_device    *dev,                           |
|                           struct comedi_subdevice *s,                             |
|                           struct comedi_insn      *insn,                          |
|                           unsigned int         *data)                          |
+----------------------------------------------------------------------------+
| Task              : Write the selected output mask and read the status from|
|                     all TTL channles                                       |
+----------------------------------------------------------------------------+
| Input Parameters  : dw_ChannelMask = data [0];                             |
|                     dw_BitMask     = data [1];                             |
+----------------------------------------------------------------------------+
| Output Parameters : data[1] : All TTL channles states                      |
+----------------------------------------------------------------------------+
| Return Value      : >0  : No error                                         |
|                    -4   : Channel mask error                               |
|                    -101 : Data size error                                  |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnBitsTTLIO(struct comedi_device *dev,
				    struct comedi_subdevice *s,
				    struct comedi_insn *insn,
				    unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	int i_ReturnValue = insn->n;
	unsigned char b_ChannelCpt = 0;
	unsigned int dw_ChannelMask = 0;
	unsigned int dw_BitMask = 0;
	unsigned int dw_Status = 0;

	/************************/
	/* Test the buffer size */
	/************************/

	if (insn->n >= 2) {
	   /*******************************/
		/* Get the channe and bit mask */
	   /*******************************/

		dw_ChannelMask = data[0];
		dw_BitMask = data[1];

	   /*************************/
		/* Test the channel mask */
	   /*************************/

		if (((dw_ChannelMask & 0XFF00FF00) == 0) &&
			(((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0xFF)
				|| (((devpriv->ul_TTLPortConfiguration[0] &
							0xFF) == 0)
					&& ((dw_ChannelMask & 0XFF0000) ==
						0)))) {
	      /*********************************/
			/* Test if set/reset any channel */
	      /*********************************/

			if (dw_ChannelMask) {
		 /****************************************/
				/* Test if set/rest any port 0 channels */
		 /****************************************/

				if (dw_ChannelMask & 0xFF) {
		    /*******************************************/
					/* Read port 0 (first digital output port) */
		    /*******************************************/

					dw_Status = inl(devpriv->iobase + 80);

					for (b_ChannelCpt = 0; b_ChannelCpt < 8;
						b_ChannelCpt++) {
						if ((dw_ChannelMask >>
								b_ChannelCpt) &
							1) {
							dw_Status =
								(dw_Status &
								(0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
						}
					}

					outl(dw_Status, devpriv->iobase + 80);
				}

		 /****************************************/
				/* Test if set/rest any port 2 channels */
		 /****************************************/

				if (dw_ChannelMask & 0xFF0000) {
					dw_BitMask = dw_BitMask >> 16;
					dw_ChannelMask = dw_ChannelMask >> 16;

		    /********************************************/
					/* Read port 2 (second digital output port) */
		    /********************************************/

					dw_Status = inl(devpriv->iobase + 112);

					for (b_ChannelCpt = 0; b_ChannelCpt < 8;
						b_ChannelCpt++) {
						if ((dw_ChannelMask >>
								b_ChannelCpt) &
							1) {
							dw_Status =
								(dw_Status &
								(0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
						}
					}

					outl(dw_Status, devpriv->iobase + 112);
				}
			}

	      /*******************************************/
			/* Read port 0 (first digital output port) */
	      /*******************************************/

			data[1] = inl(devpriv->iobase + 80);

	      /******************************************/
			/* Read port 1 (first digital input port) */
	      /******************************************/

			data[1] = data[1] | (inl(devpriv->iobase + 64) << 8);

	      /************************/
			/* Test if port 2 input */
	      /************************/

			if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0) {
				data[1] =
					data[1] | (inl(devpriv->iobase +
						96) << 16);
			} else {
				data[1] =
					data[1] | (inl(devpriv->iobase +
						112) << 16);
			}
		} else {
	      /************************/
			/* Config command error */
	      /************************/

			printk("Channel mask error\n");
			i_ReturnValue = -4;
		}
	} else {
	   /*******************/
		/* Data size error */
	   /*******************/

		printk("Buffer size error\n");
		i_ReturnValue = -101;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     : int i_APCI3XXX_InsnReadTTLIO                           |
|                          (struct comedi_device    *dev,                           |
|                           struct comedi_subdevice *s,                             |
|                           struct comedi_insn      *insn,                          |
|                           unsigned int         *data)                          |
+----------------------------------------------------------------------------+
| Task              : Read the status from selected channel                  |
+----------------------------------------------------------------------------+
| Input Parameters  : b_Channel = CR_CHAN(insn->chanspec)                    |
+----------------------------------------------------------------------------+
| Output Parameters : data[0] : Selected TTL channel state                   |
+----------------------------------------------------------------------------+
| Return Value      : 0   : No error                                         |
|                    -3   : Channel selection error                          |
|                    -101 : Data size error                                  |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnReadTTLIO(struct comedi_device *dev,
				    struct comedi_subdevice *s,
				    struct comedi_insn *insn,
				    unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	unsigned char b_Channel = (unsigned char) CR_CHAN(insn->chanspec);
	int i_ReturnValue = insn->n;
	unsigned int *pls_ReadData = data;

	/************************/
	/* Test the buffer size */
	/************************/

	if (insn->n >= 1) {
	   /***********************/
		/* Test if read port 0 */
	   /***********************/

		if (b_Channel < 8) {
	      /*******************************************/
			/* Read port 0 (first digital output port) */
	      /*******************************************/

			pls_ReadData[0] = inl(devpriv->iobase + 80);
			pls_ReadData[0] = (pls_ReadData[0] >> b_Channel) & 1;
		} else {
	      /***********************/
			/* Test if read port 1 */
	      /***********************/

			if ((b_Channel > 7) && (b_Channel < 16)) {
		 /******************************************/
				/* Read port 1 (first digital input port) */
		 /******************************************/

				pls_ReadData[0] = inl(devpriv->iobase + 64);
				pls_ReadData[0] =
					(pls_ReadData[0] >> (b_Channel -
						8)) & 1;
			} else {
		 /***********************/
				/* Test if read port 2 */
		 /***********************/

				if ((b_Channel > 15) && (b_Channel < 24)) {
		    /************************/
					/* Test if port 2 input */
		    /************************/

					if ((devpriv->ul_TTLPortConfiguration[0]
							& 0xFF) == 0) {
						pls_ReadData[0] =
							inl(devpriv->iobase +
							96);
						pls_ReadData[0] =
							(pls_ReadData[0] >>
							(b_Channel - 16)) & 1;
					} else {
						pls_ReadData[0] =
							inl(devpriv->iobase +
							112);
						pls_ReadData[0] =
							(pls_ReadData[0] >>
							(b_Channel - 16)) & 1;
					}
				} else {
		    /***************************/
					/* Channel selection error */
		    /***************************/

					i_ReturnValue = -3;
					printk("Channel %d selection error\n",
						b_Channel);
				}
			}
		}
	} else {
	   /*******************/
		/* Data size error */
	   /*******************/

		printk("Buffer size error\n");
		i_ReturnValue = -101;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
|                        TTL OUTPUT FUNCTIONS                                |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : int     i_APCI3XXX_InsnWriteTTLIO                      |
|                          (struct comedi_device    *dev,                           |
|                           struct comedi_subdevice *s,                             |
|                           struct comedi_insn      *insn,                          |
|                           unsigned int         *data)                          |
+----------------------------------------------------------------------------+
| Task              : Set the state from TTL output channel                  |
+----------------------------------------------------------------------------+
| Input Parameters  : b_Channel = CR_CHAN(insn->chanspec)                    |
|                     b_State   = data [0]                                   |
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      : 0   : No error                                         |
|                    -3   : Channel selection error                          |
|                    -101 : Data size error                                  |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnWriteTTLIO(struct comedi_device *dev,
				     struct comedi_subdevice *s,
				     struct comedi_insn *insn,
				     unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	int i_ReturnValue = insn->n;
	unsigned char b_Channel = (unsigned char) CR_CHAN(insn->chanspec);
	unsigned char b_State = 0;
	unsigned int dw_Status = 0;

	/************************/
	/* Test the buffer size */
	/************************/

	if (insn->n >= 1) {
		b_State = (unsigned char) data[0];

	   /***********************/
		/* Test if read port 0 */
	   /***********************/

		if (b_Channel < 8) {
	      /*****************************************************************************/
			/* Read port 0 (first digital output port) and set/reset the selected channel */
	      /*****************************************************************************/

			dw_Status = inl(devpriv->iobase + 80);
			dw_Status =
				(dw_Status & (0xFF -
					(1 << b_Channel))) | ((b_State & 1) <<
				b_Channel);
			outl(dw_Status, devpriv->iobase + 80);
		} else {
	      /***********************/
			/* Test if read port 2 */
	      /***********************/

			if ((b_Channel > 15) && (b_Channel < 24)) {
		 /*************************/
				/* Test if port 2 output */
		 /*************************/

				if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF)
					== 0xFF) {
		    /*****************************************************************************/
					/* Read port 2 (first digital output port) and set/reset the selected channel */
		    /*****************************************************************************/

					dw_Status = inl(devpriv->iobase + 112);
					dw_Status =
						(dw_Status & (0xFF -
							(1 << (b_Channel -
									16)))) |
						((b_State & 1) << (b_Channel -
							16));
					outl(dw_Status, devpriv->iobase + 112);
				} else {
		    /***************************/
					/* Channel selection error */
		    /***************************/

					i_ReturnValue = -3;
					printk("Channel %d selection error\n",
						b_Channel);
				}
			} else {
		 /***************************/
				/* Channel selection error */
		 /***************************/

				i_ReturnValue = -3;
				printk("Channel %d selection error\n",
					b_Channel);
			}
		}
	} else {
	   /*******************/
		/* Data size error */
	   /*******************/

		printk("Buffer size error\n");
		i_ReturnValue = -101;
	}

	return i_ReturnValue;
}
+79 −5
Original line number Diff line number Diff line
@@ -378,7 +378,6 @@ struct apci3xxx_private {
	unsigned char b_EocEosConversionTimeBase;
	unsigned char b_SingelDiff;
	struct task_struct *tsk_Current;
	unsigned int ul_TTLPortConfiguration[10];
};

#include "addi-data/hwdrv_apci3xxx.c"
@@ -477,6 +476,83 @@ static int apci3xxx_do_insn_bits(struct comedi_device *dev,
	return insn->n;
}

static int apci3xxx_dio_insn_config(struct comedi_device *dev,
				    struct comedi_subdevice *s,
				    struct comedi_insn *insn,
				    unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	unsigned int chan = CR_CHAN(insn->chanspec);
	unsigned int mask = 1 << chan;
	unsigned int bits;

	/*
	 * Port 0 (channels 0-7) are always inputs
	 * Port 1 (channels 8-15) are always outputs
	 * Port 2 (channels 16-23) are programmable i/o
	 *
	 * Changing any channel in port 2 changes the entire port.
	 */
	if (mask & 0xff0000)
		bits = 0xff0000;
	else
		bits = 0;

	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;
	default:
		return -EINVAL;
	}

	/* update port 2 configuration */
	if (bits)
		outl((s->io_bits >> 24) & 0xff, devpriv->iobase + 224);

	return insn->n;
}

static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
				  struct comedi_subdevice *s,
				  struct comedi_insn *insn,
				  unsigned int *data)
{
	struct apci3xxx_private *devpriv = dev->private;
	unsigned int mask = data[0];
	unsigned int bits = data[1];
	unsigned int val;

	/* only update output channels */
	mask &= s->io_bits;
	if (mask) {
		s->state &= ~mask;
		s->state |= (bits & mask);

		if (mask & 0xff)
			outl(s->state & 0xff, devpriv->iobase + 80);
		if (mask & 0xff0000)
			outl((s->state >> 16) & 0xff, devpriv->iobase + 112);
	}

	val = inl(devpriv->iobase + 80);
	val |= (inl(devpriv->iobase + 64) << 8);
	if (s->io_bits & 0xff0000)
		val |= (inl(devpriv->iobase + 112) << 16);
	else
		val |= (inl(devpriv->iobase + 96) << 16);

	data[1] = val;

	return insn->n;
}

static int apci3xxx_reset(struct comedi_device *dev)
{
	struct apci3xxx_private *devpriv = dev->private;
@@ -632,10 +708,8 @@ static int apci3xxx_auto_attach(struct comedi_device *dev,
		s->maxdata	= 1;
		s->io_bits	= 0xff;	/* channels 0-7 are always outputs */
		s->range_table	= &range_digital;
		s->insn_config	= i_APCI3XXX_InsnConfigInitTTLIO;
		s->insn_bits	= i_APCI3XXX_InsnBitsTTLIO;
		s->insn_read	= i_APCI3XXX_InsnReadTTLIO;
		s->insn_write	= i_APCI3XXX_InsnWriteTTLIO;
		s->insn_config	= apci3xxx_dio_insn_config;
		s->insn_bits	= apci3xxx_dio_insn_bits;
	} else {
		s->type = COMEDI_SUBD_UNUSED;
	}