[PATCH 19/22] staging: comedi: addi_apci_16xx: rewrite low-level support code

H Hartley Sweeten hsweeten at visionengravers.com
Fri Jan 18 17:49:20 UTC 2013


The current low-level support code in hwdrv_apci16xx.c is seriously
broken. This file defines four comedi insn_* subdevice functions:

  (*insn_config)  i_APCI16XX_InsnConfigInitTTLIO()
  (*insn_bits)    i_APCI16XX_InsnBitsReadTTLIO()
  (*insn_read)    i_APCI16XX_InsnReadTTLIOAllPortValue()
  (*insn_write)   i_APCI16XX_InsnBitsWriteTTLIO()

Comedi (*insn_config) functions use the first passed data parameter
as the configuration 'instruction' and the remaining data values as
the 'parameters' for that instruction. The comedi core validates
the number of parameters being passed, based on the 'instruction',
before actually calling the subdevice function.

This driver currently redefines three of the comedi core 'instructions'
for it's own internal use:

  INSN_CONFIG_DIO_INPUT      APCI16XX_TTL_INIT
  INSN_CONFIG_DIO_OUTPUT     APCI16XX_TTL_INITDIRECTION
  INSN_CONFIG_DIO_OPENDRAIN  APCI16XX_TTL_OUTPUTMEMORY

The i_APCI16XX_InsnConfigInitTTLIO() function expects the redefined
APCI16XX_TTL_INITDIRECTION to have (s->n_chan / 8) paramenters
(the number of 8-bit i/o ports on the board). This can never happen
due to checks in the comedi core so the function will never be
called if the user tries to pass a COMEDI_INSN ioctl that has the
insn data defined like the function expects.

The redefined APCI16XX_TTL_OUTPUTMEMORY instruction would work but
each attempt to use it results in a noisy pr_warn() message because
the INSN_CONFIG_DIO_OPENDRAIN instruction is not currently defined
in the comed core.

The redefined APCI16XX_TTL_INIT instruction would also work but
does not work like the comedi core expects. The INSN_CONFIG_DIO_INPUT
instruction expects to configure the channel passed in the insn->chanspec
as an input. In this driver, APCI16XX_TTL_INIT configures all the
channels to the 'default' direction, inputs.

Replace this broken implementation with a new function, apci16xx_insn_config(),
which properly handles the comedi core instructions to set the channels
as inputs of outputs and allows the user to query the current configuration.

The (*insn_bits) function also abuses the comedi core API. For these
functions, the comedi core passes a 'mask' value of the bits to update
and a 'bits' value indicating the new state for these bits. After updating
the outputs, the comedi core expects the function to return the state
of the subdevice inputs.

In this driver, the i_APCI16XX_InsnBitsReadTTLIO() function redefines
the use of the 'mask' value and uses it as a 'command' indicating if
the user wants to read a specific channel of a given 8-bit port or a
full 8-bit port. Writing to the outputs is not even supported.

Replace this broken implementation with a new function, apci16xx_dio_insn_bits(),
which works as the comedi core expects it to.

The (*insn_read) and (*insn_write) functions in this driver are even
more abusive.

The read function expects the passed buffer size (insn->n) to always be
large enough to return all the channels. In addition, the 'aref' value
packed in the insn->chanspec is used as a 'command' to indicate if the
input channel registers or output channel registers are to be read.

The (*insn_read) function is not required by the comedi core if a properly
working (*insn_bits) function is defined. The comedi core can use the
(*insn_bits) function to emulate the (*insn_read). Just remove the goofy
read function.

The write function is similarly broken. Instead of just writng the passed
data values to the hardware, this function uses the first data value as
a 'command' to indicate if a single channel in an 8-bit port or if the full
8-bit port is being set/cleared.

Again, the (*insn_write) function is not required if there is a working
(*insn_bits) function. Remove this goofy write function.

The new (*insn_config) and (*insn_bits) functions are coded to work with
32-bit ports, this is also how the registers on the board are setup. Modify
the apci16xx_auto_attach() so that the proper number subdevices are allocated
and each subdevice handles up to 32 channels.

Signed-off-by: H Hartley Sweeten <hsweeten at visionengravers.com>
Cc: Ian Abbott <abbotti at mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
---
 .../comedi/drivers/addi-data/hwdrv_apci16xx.c      | 542 ---------------------
 drivers/staging/comedi/drivers/addi_apci_16xx.c    | 133 ++++-
 2 files changed, 107 insertions(+), 568 deletions(-)
 delete mode 100644 drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c

diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c
deleted file mode 100644
index 098ea59..0000000
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/**
- at verbatim
-
-Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
-
-	ADDI-DATA GmbH
-	Dieselstrasse 3
-	D-77833 Ottersweier
-	Tel: +19(0)7223/9493-0
-	Fax: +49(0)7223/9493-92
-	http://www.addi-data.com
-	info at addi-data.com
-
-This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-You should also find the complete GPL in the COPYING file accompanying this source code.
-
- at endverbatim
-*/
-/*
-
-  +-----------------------------------------------------------------------+
-  | (C) ADDI-DATA GmbH          Dieselstraße 3       D-77833 Ottersweier  |
-  +-----------------------------------------------------------------------+
-  | Tel : +49 (0) 7223/9493-0     | email    : info at addi-data.com         |
-  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
-  +-----------------------------------------------------------------------+
-  | Project     : API APCI1648    | Compiler : gcc                        |
-  | Module name : TTL.C           | Version  : 2.96                       |
-  +-------------------------------+---------------------------------------+
-  | Project manager: S. Weber     | Date     :  25/05/2005                |
-  +-----------------------------------------------------------------------+
-  | Description :   APCI-16XX TTL I/O module                              |
-  |                                                                       |
-  |                                                                       |
-  +-----------------------------------------------------------------------+
-  |                             UPDATES                                   |
-  +-----------------------------------------------------------------------+
-  |   Date   |   Author  |          Description of updates                |
-  +----------+-----------+------------------------------------------------+
-  |25.05.2005| S.Weber   | Creation                                       |
-  |          |           |                                                |
-  +-----------------------------------------------------------------------+
-*/
-
-#define APCI16XX_TTL_INIT		0
-#define APCI16XX_TTL_INITDIRECTION	1
-#define APCI16XX_TTL_OUTPUTMEMORY	2
-
-#define APCI16XX_TTL_READCHANNEL	0
-#define APCI16XX_TTL_READPORT		1
-
-#define APCI16XX_TTL_WRITECHANNEL_ON	0
-#define APCI16XX_TTL_WRITECHANNEL_OFF	1
-#define APCI16XX_TTL_WRITEPORT_ON	2
-#define APCI16XX_TTL_WRITEPORT_OFF	3
-
-#define APCI16XX_TTL_READ_ALL_INPUTS	0
-#define APCI16XX_TTL_READ_ALL_OUTPUTS	1
-
-/*
-+----------------------------------------------------------------------------+
-| Function Name     : int   i_APCI16XX_InsnConfigInitTTLIO                   |
-|                          (struct comedi_device    *dev,                           |
-|                           struct comedi_subdevice *s,                             |
-|                           struct comedi_insn      *insn,                          |
-|                           unsigned int         *data)                          |
-+----------------------------------------------------------------------------+
-| Task           APCI16XX_TTL_INIT (using defaults)   :                      |
-|                Configure the TTL I/O operating mode from all ports         |
-|                You must calling this function be                           |
-|                for you call any other function witch access of TTL.        |
-|                APCI16XX_TTL_INITDIRECTION(user inputs for direction)       |
-+----------------------------------------------------------------------------+
-| Input Parameters  : b_InitType    = (unsigned char) data[0];                        |
-|                     b_Port0Mode   = (unsigned char) data[1];                        |
-|                     b_Port1Mode   = (unsigned char) data[2];                        |
-|                     b_Port2Mode   = (unsigned char) data[3];                        |
-|                     b_Port3Mode   = (unsigned char) data[4];                        |
-|                     ........                                               |
-+----------------------------------------------------------------------------+
-| Output Parameters : -                                                      |
-+----------------------------------------------------------------------------+
-| Return Value      :>0: No error                                            |
-|                    -1: Port 0 mode selection is wrong                      |
-|                    -2: Port 1 mode selection is wrong                      |
-|                    -3: Port 2 mode selection is wrong                      |
-|                    -4: Port 3 mode selection is wrong                      |
-|                    -X: Port X-1 mode selection is wrong                    |
-|                    ....                                                    |
-|                    -100 : Config command error                             |
-|                    -101 : Data size error                                  |
-+----------------------------------------------------------------------------+
-*/
-static int i_APCI16XX_InsnConfigInitTTLIO(struct comedi_device *dev,
-					  struct comedi_subdevice *s,
-					  struct comedi_insn *insn,
-					  unsigned int *data)
-{
-	struct addi_private *devpriv = dev->private;
-	int i_ReturnValue = insn->n;
-	unsigned char b_Command = 0;
-	unsigned char b_Cpt = 0;
-	unsigned char b_NumberOfPort = s->n_chan / 8;
-
-	/* Test the buffer size */
-	if (insn->n >= 1) {
-		/* Get the command */
-		b_Command = (unsigned char) data[0];
-
-		/* Test the command */
-		if ((b_Command == APCI16XX_TTL_INIT) ||
-			(b_Command == APCI16XX_TTL_INITDIRECTION) ||
-			(b_Command == APCI16XX_TTL_OUTPUTMEMORY)) {
-			/* Test the initialisation buffer size */
-			if ((b_Command == APCI16XX_TTL_INITDIRECTION)
-				&& ((unsigned char) (insn->n - 1) != b_NumberOfPort)) {
-				printk("\nBuffer size error");
-				i_ReturnValue = -101;
-			}
-
-			if ((b_Command == APCI16XX_TTL_OUTPUTMEMORY)
-				&& ((unsigned char) (insn->n) != 2)) {
-				printk("\nBuffer size error");
-				i_ReturnValue = -101;
-			}
-		} else {
-			printk("\nCommand selection error");
-			i_ReturnValue = -100;
-		}
-	} else {
-		printk("\nBuffer size error");
-		i_ReturnValue = -101;
-	}
-
-	/* Test if no error occur and APCI16XX_TTL_INITDIRECTION command selected */
-	if ((i_ReturnValue >= 0) && (b_Command == APCI16XX_TTL_INITDIRECTION)) {
-		memset(devpriv->ul_TTLPortConfiguration, 0,
-			sizeof(devpriv->ul_TTLPortConfiguration));
-
-		/* Test the port direction selection */
-		for (b_Cpt = 1;
-			(b_Cpt <= b_NumberOfPort) && (i_ReturnValue >= 0);
-			b_Cpt++) {
-			/* Test the direction */
-			if ((data[b_Cpt] != 0) && (data[b_Cpt] != 0xFF)) {
-				printk("\nPort %d direction selection error",
-					(int) b_Cpt);
-				i_ReturnValue = -(int) b_Cpt;
-			}
-
-			/* Save the configuration */
-			devpriv->ul_TTLPortConfiguration[(b_Cpt - 1) / 4] =
-				devpriv->ul_TTLPortConfiguration[(b_Cpt -
-					1) / 4] | (data[b_Cpt] << (8 * ((b_Cpt -
-							1) % 4)));
-		}
-	}
-
-	/* Test if no error occur */
-	if (i_ReturnValue >= 0) {
-		/* Test if TTL port initilaisation */
-		if ((b_Command == APCI16XX_TTL_INIT)
-			|| (b_Command == APCI16XX_TTL_INITDIRECTION)) {
-			/* Set all port configuration */
-			for (b_Cpt = 0; b_Cpt <= b_NumberOfPort; b_Cpt++) {
-				if ((b_Cpt % 4) == 0) {
-					/* Set the configuration */
-					outl(devpriv->
-						ul_TTLPortConfiguration[b_Cpt /
-							4],
-						dev->iobase + 32 + b_Cpt);
-				}
-			}
-		}
-	}
-
-	/* Test if output memory initialisation command */
-	if (b_Command == APCI16XX_TTL_OUTPUTMEMORY) {
-		if (data[1]) {
-			devpriv->b_OutputMemoryStatus = ADDIDATA_ENABLE;
-		} else {
-			devpriv->b_OutputMemoryStatus = ADDIDATA_DISABLE;
-		}
-	}
-
-	return i_ReturnValue;
-}
-
-/*
-+----------------------------------------------------------------------------+
-| Function Name     : int     i_APCI16XX_InsnBitsReadTTLIO                   |
-|                          (struct comedi_device    *dev,                           |
-|                           struct comedi_subdevice *s,                             |
-|                           struct comedi_insn      *insn,                          |
-|                           unsigned int         *data)                          |
-+----------------------------------------------------------------------------+
-| Task              : Read the status from selected TTL digital input        |
-|                     (b_InputChannel)                                       |
-+----------------------------------------------------------------------------+
-| Task              : Read the status from digital input port                |
-|                     (b_SelectedPort)                                       |
-+----------------------------------------------------------------------------+
-| Input Parameters  :                                                        |
-|              APCI16XX_TTL_READCHANNEL                                      |
-|                    b_SelectedPort= CR_RANGE(insn->chanspec);               |
-|                    b_InputChannel= CR_CHAN(insn->chanspec);                |
-|                    b_ReadType	  = (unsigned char) data[0];                          |
-|                                                                            |
-|              APCI16XX_TTL_READPORT                                         |
-|                    b_SelectedPort= CR_RANGE(insn->chanspec);               |
-|                    b_ReadType	  = (unsigned char) data[0];                          |
-+----------------------------------------------------------------------------+
-| Output Parameters : data[0]    0 : Channle is not active                   |
-|                                1 : Channle is active                       |
-+----------------------------------------------------------------------------+
-| Return Value      : >0  : No error                                         |
-|                    -100 : Config command error                             |
-|                    -101 : Data size error                                  |
-|                    -102 : The selected TTL input port is wrong             |
-|                    -103 : The selected TTL digital input is wrong          |
-+----------------------------------------------------------------------------+
-*/
-static int i_APCI16XX_InsnBitsReadTTLIO(struct comedi_device *dev,
-					struct comedi_subdevice *s,
-					struct comedi_insn *insn,
-					unsigned int *data)
-{
-	struct addi_private *devpriv = dev->private;
-	int i_ReturnValue = insn->n;
-	unsigned char b_Command = 0;
-	unsigned char b_NumberOfPort = s->n_chan / 8;
-	unsigned char b_SelectedPort = CR_RANGE(insn->chanspec);
-	unsigned char b_InputChannel = CR_CHAN(insn->chanspec);
-	unsigned char *pb_Status;
-	unsigned int dw_Status;
-
-	/* Test the buffer size */
-	if (insn->n >= 1) {
-		/* Get the command */
-		b_Command = (unsigned char) data[0];
-
-		/* Test the command */
-		if ((b_Command == APCI16XX_TTL_READCHANNEL)
-			|| (b_Command == APCI16XX_TTL_READPORT)) {
-			/* Test the selected port */
-			if (b_SelectedPort < b_NumberOfPort) {
-				/* Test if input port */
-				if (((devpriv->ul_TTLPortConfiguration
-							[b_SelectedPort /
-								4] >> (8 *
-								(b_SelectedPort
-									%
-									4))) &
-						0xFF) == 0) {
-					/* Test the channel number */
-					if ((b_Command ==
-							APCI16XX_TTL_READCHANNEL)
-						&& (b_InputChannel > 7)) {
-						printk("\nChannel selection error");
-						i_ReturnValue = -103;
-					}
-				} else {
-					printk("\nPort selection error");
-					i_ReturnValue = -102;
-				}
-			} else {
-				printk("\nPort selection error");
-				i_ReturnValue = -102;
-			}
-		} else {
-			printk("\nCommand selection error");
-			i_ReturnValue = -100;
-		}
-	} else {
-		printk("\nBuffer size error");
-		i_ReturnValue = -101;
-	}
-
-	/* Test if no error occur */
-	if (i_ReturnValue >= 0) {
-		pb_Status = (unsigned char *) &data[0];
-
-		/* Get the digital inpu status */
-		dw_Status =
-			inl(dev->iobase + 8 + ((b_SelectedPort / 4) * 4));
-		dw_Status = (dw_Status >> (8 * (b_SelectedPort % 4))) & 0xFF;
-
-		/* Save the port value */
-		*pb_Status = (unsigned char) dw_Status;
-
-		/* Test if read channel status command */
-		if (b_Command == APCI16XX_TTL_READCHANNEL) {
-			*pb_Status = (*pb_Status >> b_InputChannel) & 1;
-		}
-	}
-
-	return i_ReturnValue;
-}
-
-/*
-+----------------------------------------------------------------------------+
-| Function Name     : int i_APCI16XX_InsnReadTTLIOAllPortValue               |
-|                          (struct comedi_device    *dev,                           |
-|                           struct comedi_subdevice *s,                             |
-|                           struct comedi_insn      *insn,                          |
-|                           unsigned int         *data)                          |
-+----------------------------------------------------------------------------+
-| Task              : Read the status from all digital input ports           |
-+----------------------------------------------------------------------------+
-| Input Parameters  : -                                                      |
-+----------------------------------------------------------------------------+
-| Output Parameters : data[0] : Port 0 to 3 data                             |
-|                     data[1] : Port 4 to 7 data                             |
-|                     ....                                                   |
-+----------------------------------------------------------------------------+
-| Return Value      : 0: No error                                            |
-|                    -100 : Read command error                               |
-|                    -101 : Data size error                                  |
-+----------------------------------------------------------------------------+
-*/
-static int i_APCI16XX_InsnReadTTLIOAllPortValue(struct comedi_device *dev,
-						struct comedi_subdevice *s,
-						struct comedi_insn *insn,
-						unsigned int *data)
-{
-	struct addi_private *devpriv = dev->private;
-	unsigned char b_Command = (unsigned char) CR_AREF(insn->chanspec);
-	int i_ReturnValue = insn->n;
-	unsigned char b_Cpt = 0;
-	unsigned char b_NumberOfPort = 0;
-	unsigned int *pls_ReadData = data;
-
-	/* Test the command */
-	if ((b_Command == APCI16XX_TTL_READ_ALL_INPUTS)
-		|| (b_Command == APCI16XX_TTL_READ_ALL_OUTPUTS)) {
-		/* Get the number of 32-Bit ports */
-		b_NumberOfPort = s->n_chan / 32;
-		if ((b_NumberOfPort * 32) < s->n_chan)
-			b_NumberOfPort = b_NumberOfPort + 1;
-
-		/* Test the buffer size */
-		if (insn->n >= b_NumberOfPort) {
-			if (b_Command == APCI16XX_TTL_READ_ALL_INPUTS) {
-				/* Read all digital input */
-				for (b_Cpt = 0; b_Cpt < b_NumberOfPort; b_Cpt++) {
-					/* Read the 32-Bit port */
-					pls_ReadData[b_Cpt] =
-						inl(dev->iobase + 8 +
-						(b_Cpt * 4));
-
-					/* Mask all channels used als outputs */
-					pls_ReadData[b_Cpt] =
-						pls_ReadData[b_Cpt] &
-						(~devpriv->
-						ul_TTLPortConfiguration[b_Cpt]);
-				}
-			} else {
-				/* Read all digital outputs */
-				for (b_Cpt = 0; b_Cpt < b_NumberOfPort; b_Cpt++) {
-					/* Read the 32-Bit port */
-					pls_ReadData[b_Cpt] =
-						inl(dev->iobase + 20 +
-						(b_Cpt * 4));
-
-					/* Mask all channels used als outputs */
-					pls_ReadData[b_Cpt] =
-						pls_ReadData[b_Cpt] & devpriv->
-						ul_TTLPortConfiguration[b_Cpt];
-				}
-			}
-		} else {
-			printk("\nBuffer size error");
-			i_ReturnValue = -101;
-		}
-	} else {
-		printk("\nCommand selection error");
-		i_ReturnValue = -100;
-	}
-
-	return i_ReturnValue;
-}
-
-/*
-+----------------------------------------------------------------------------+
-| Function Name     : int     i_APCI16XX_InsnBitsWriteTTLIO                  |
-|                          (struct comedi_device    *dev,                           |
-|                           struct comedi_subdevice *s,                             |
-|                           struct comedi_insn      *insn,                          |
-|                           unsigned int         *data)                          |
-+----------------------------------------------------------------------------+
-| Task              : Set the state from selected TTL digital output         |
-|                     (b_OutputChannel)                                      |
-+----------------------------------------------------------------------------+
-| Task              : Set the state from digital output port                 |
-|                     (b_SelectedPort)                                       |
-+----------------------------------------------------------------------------+
-| Input Parameters  :                                                        |
-|              APCI16XX_TTL_WRITECHANNEL_ON | APCI16XX_TTL_WRITECHANNEL_OFF  |
-|                    b_SelectedPort = CR_RANGE(insn->chanspec);              |
-|                    b_OutputChannel= CR_CHAN(insn->chanspec);               |
-|                    b_Command      = (unsigned char) data[0];                        |
-|                                                                            |
-|              APCI16XX_TTL_WRITEPORT_ON | APCI16XX_TTL_WRITEPORT_OFF        |
-|                    b_SelectedPort = CR_RANGE(insn->chanspec);              |
-|                    b_Command      = (unsigned char) data[0];                        |
-+----------------------------------------------------------------------------+
-| Output Parameters : data[0] : TTL output port 0 to 3 data                  |
-|                     data[1] : TTL output port 4 to 7 data                  |
-|                     ....                                                   |
-+----------------------------------------------------------------------------+
-| Return Value      : >0  : No error                                         |
-|                    -100 : Command error                                    |
-|                    -101 : Data size error                                  |
-|                    -102 : The selected TTL output port is wrong            |
-|                    -103 : The selected TTL digital output is wrong         |
-|                    -104 : Output memory disabled                           |
-+----------------------------------------------------------------------------+
-*/
-static int i_APCI16XX_InsnBitsWriteTTLIO(struct comedi_device *dev,
-					 struct comedi_subdevice *s,
-					 struct comedi_insn *insn,
-					 unsigned int *data)
-{
-	struct addi_private *devpriv = dev->private;
-	int i_ReturnValue = insn->n;
-	unsigned char b_Command = 0;
-	unsigned char b_NumberOfPort = s->n_chan / 8;
-	unsigned char b_SelectedPort = CR_RANGE(insn->chanspec);
-	unsigned char b_OutputChannel = CR_CHAN(insn->chanspec);
-	unsigned int dw_Status = 0;
-
-	/* Test the buffer size */
-	if (insn->n >= 1) {
-		/* Get the command */
-		b_Command = (unsigned char) data[0];
-
-		/* Test the command */
-		if ((b_Command == APCI16XX_TTL_WRITECHANNEL_ON) ||
-			(b_Command == APCI16XX_TTL_WRITEPORT_ON) ||
-			(b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) ||
-			(b_Command == APCI16XX_TTL_WRITEPORT_OFF)) {
-			/* Test the selected port */
-			if (b_SelectedPort < b_NumberOfPort) {
-				/* Test if output port */
-				if (((devpriv->ul_TTLPortConfiguration
-							[b_SelectedPort /
-								4] >> (8 *
-								(b_SelectedPort
-									%
-									4))) &
-						0xFF) == 0xFF) {
-					/* Test the channel number */
-					if (((b_Command == APCI16XX_TTL_WRITECHANNEL_ON) || (b_Command == APCI16XX_TTL_WRITECHANNEL_OFF)) && (b_OutputChannel > 7)) {
-						printk("\nChannel selection error");
-						i_ReturnValue = -103;
-					}
-
-					if (((b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) || (b_Command == APCI16XX_TTL_WRITEPORT_OFF)) && (devpriv->b_OutputMemoryStatus == ADDIDATA_DISABLE)) {
-						printk("\nOutput memory disabled");
-						i_ReturnValue = -104;
-					}
-
-					/* Test the buffer size */
-					if (((b_Command == APCI16XX_TTL_WRITEPORT_ON) || (b_Command == APCI16XX_TTL_WRITEPORT_OFF)) && (insn->n < 2)) {
-						printk("\nBuffer size error");
-						i_ReturnValue = -101;
-					}
-				} else {
-					printk("\nPort selection error %lX",
-						(unsigned long)devpriv->
-						ul_TTLPortConfiguration[0]);
-					i_ReturnValue = -102;
-				}
-			} else {
-				printk("\nPort selection error %d %d",
-					b_SelectedPort, b_NumberOfPort);
-				i_ReturnValue = -102;
-			}
-		} else {
-			printk("\nCommand selection error");
-			i_ReturnValue = -100;
-		}
-	} else {
-		printk("\nBuffer size error");
-		i_ReturnValue = -101;
-	}
-
-	/* Test if no error occur */
-	if (i_ReturnValue >= 0) {
-		/* Get the digital output state */
-		dw_Status =
-			inl(dev->iobase + 20 + ((b_SelectedPort / 4) * 4));
-
-		/* Test if output memory not used */
-		if (devpriv->b_OutputMemoryStatus == ADDIDATA_DISABLE) {
-			/* Clear the selected port value */
-			dw_Status =
-				dw_Status & (0xFFFFFFFFUL -
-				(0xFFUL << (8 * (b_SelectedPort % 4))));
-		}
-
-		/* Test if setting channel ON */
-		if (b_Command == APCI16XX_TTL_WRITECHANNEL_ON) {
-			dw_Status =
-				dw_Status | (1UL << ((8 * (b_SelectedPort %
-							4)) + b_OutputChannel));
-		}
-
-		/* Test if setting port ON */
-		if (b_Command == APCI16XX_TTL_WRITEPORT_ON) {
-			dw_Status =
-				dw_Status | ((data[1] & 0xFF) << (8 *
-					(b_SelectedPort % 4)));
-		}
-
-		/* Test if setting channel OFF */
-		if (b_Command == APCI16XX_TTL_WRITECHANNEL_OFF) {
-			dw_Status =
-				dw_Status & (0xFFFFFFFFUL -
-				(1UL << ((8 * (b_SelectedPort % 4)) +
-						b_OutputChannel)));
-		}
-
-		/* Test if setting port OFF */
-		if (b_Command == APCI16XX_TTL_WRITEPORT_OFF) {
-			dw_Status =
-				dw_Status & (0xFFFFFFFFUL -
-				((data[1] & 0xFF) << (8 * (b_SelectedPort %
-							4))));
-		}
-
-		outl(dw_Status,
-			dev->iobase + 20 + ((b_SelectedPort / 4) * 4));
-	}
-
-	return i_ReturnValue;
-}
diff --git a/drivers/staging/comedi/drivers/addi_apci_16xx.c b/drivers/staging/comedi/drivers/addi_apci_16xx.c
index 899dae1..f5953cb 100644
--- a/drivers/staging/comedi/drivers/addi_apci_16xx.c
+++ b/drivers/staging/comedi/drivers/addi_apci_16xx.c
@@ -1,9 +1,14 @@
 #include "../comedidev.h"
-#include "comedi_fc.h"
 
-#include "addi-data/addi_common.h"
-
-#include "addi-data/hwdrv_apci16xx.c"
+/*
+ * Register I/O map
+ *
+ * The subdevice 'index' is used to work out the actual
+ * registers for each group of 32 channels.
+ */
+#define APCI16XX_IN_REG(x)		(((x) * 4) + 0x08)
+#define APCI16XX_OUT_REG(x)		(((x) * 4) + 0x14)
+#define APCI16XX_DIR_REG(x)		(((x) * 4) + 0x20)
 
 struct apci16xx_boardinfo {
 	const char *name;
@@ -17,15 +22,79 @@ static const struct apci16xx_boardinfo apci16xx_boardtypes[] = {
 		.name		= "apci1648",
 		.vendor		= PCI_VENDOR_ID_ADDIDATA,
 		.device		= 0x1009,
-		.n_chan		= 48,
+		.n_chan		= 48,		/* 2 subdevices */
 	}, {
 		.name		= "apci1696",
 		.vendor		= PCI_VENDOR_ID_ADDIDATA,
 		.device		= 0x100A,
-		.n_chan		= 96,
+		.n_chan		= 96,		/* 3 subdevices */
 	},
 };
 
+static int apci16xx_insn_config(struct comedi_device *dev,
+				struct comedi_subdevice *s,
+				struct comedi_insn *insn,
+				unsigned int *data)
+{
+	unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec);
+	unsigned int bits;
+
+	/*
+	 * Each 8-bit "port" is configurable as either input or
+	 * output. Changing the configuration of any channel in
+	 * a port changes the entire port.
+	 */
+	if (chan_mask & 0x000000ff)
+		bits = 0x000000ff;
+	else if (chan_mask & 0x0000ff00)
+		bits = 0x0000ff00;
+	else if (chan_mask & 0x00ff0000)
+		bits = 0x00ff0000;
+	else
+		bits = 0xff000000;
+
+	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;
+	}
+
+	outl(s->io_bits, dev->iobase + APCI16XX_DIR_REG(s->index));
+
+	return insn->n;
+}
+
+static int apci16xx_dio_insn_bits(struct comedi_device *dev,
+				  struct comedi_subdevice *s,
+				  struct comedi_insn *insn,
+				  unsigned int *data)
+{
+	unsigned int mask = data[0];
+	unsigned int bits = data[1];
+
+	/* Only update the channels configured as outputs */
+	mask &= s->io_bits;
+	if (mask) {
+		s->state &= ~mask;
+		s->state |= (bits & mask);
+
+		outl(s->state, dev->iobase + APCI16XX_OUT_REG(s->index));
+	}
+
+	data[1] = inl(dev->iobase + APCI16XX_IN_REG(s->index));
+
+	return insn->n;
+}
+
 static const void *apci16xx_find_boardinfo(struct comedi_device *dev,
 					   struct pci_dev *pcidev)
 {
@@ -46,8 +115,10 @@ static int apci16xx_auto_attach(struct comedi_device *dev,
 {
 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 	const struct apci16xx_boardinfo *board;
-	struct addi_private *devpriv;
 	struct comedi_subdevice *s;
+	unsigned int n_subdevs;
+	unsigned int last;
+	int i;
 	int ret;
 
 	board = apci16xx_find_boardinfo(dev, pcidev);
@@ -56,34 +127,44 @@ static int apci16xx_auto_attach(struct comedi_device *dev,
 	dev->board_ptr = board;
 	dev->board_name = board->name;
 
-	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
-	if (!devpriv)
-		return -ENOMEM;
-	dev->private = devpriv;
-
 	ret = comedi_pci_enable(pcidev, dev->board_name);
 	if (ret)
 		return ret;
 
 	dev->iobase = pci_resource_start(pcidev, 0);
 
-	ret = comedi_alloc_subdevices(dev, 1);
+	/*
+	 * Work out the number of subdevices needes to support all the
+	 * digital i/o channels on the board. Each subdevice supports
+	 * up to 32 channels.
+	 */
+	n_subdevs = board->n_chan / 32;
+	if ((n_subdevs * 32) < board->n_chan) {
+		last = board->n_chan - (n_subdevs * 32);
+		n_subdevs++;
+	} else {
+		last = 0;
+	}
+
+	ret = comedi_alloc_subdevices(dev, n_subdevs);
 	if (ret)
 		return ret;
 
-	/* Initialize the TTL digital i/o */
-	s = &dev->subdevices[0];
-	s->type		= COMEDI_SUBD_DIO;
-	s->subdev_flags	= SDF_WRITEABLE | SDF_READABLE;
-	s->n_chan	= board->n_chan;
-	s->maxdata	= 1;
-	s->io_bits	= 0;	/* all bits input */
-	s->len_chanlist	= board->n_chan;
-	s->range_table	= &range_digital;
-	s->insn_config	= i_APCI16XX_InsnConfigInitTTLIO;
-	s->insn_bits	= i_APCI16XX_InsnBitsReadTTLIO;
-	s->insn_read	= i_APCI16XX_InsnReadTTLIOAllPortValue;
-	s->insn_write	= i_APCI16XX_InsnBitsWriteTTLIO;
+	/* Initialize the TTL digital i/o subdevices */
+	for (i = 0; i < n_subdevs; i++) {
+		s = &dev->subdevices[i];
+		s->type		= COMEDI_SUBD_DIO;
+		s->subdev_flags	= SDF_WRITEABLE | SDF_READABLE;
+		s->n_chan	= ((i * 32) < board->n_chan) ? 32 : last;
+		s->maxdata	= 1;
+		s->range_table	= &range_digital;
+		s->insn_config	= apci16xx_insn_config;
+		s->insn_bits	= apci16xx_dio_insn_bits;
+
+		/* Default all channels to inputs */
+		s->io_bits	= 0;
+		outl(s->io_bits, dev->iobase + APCI16XX_DIR_REG(i));
+	}
 
 	return 0;
 }
-- 
1.8.0




More information about the devel mailing list