[PATCH 1/5] staging: comedi: addi_apci_16xx: rewrite low-level support code

H Hartley Sweeten hsweeten at visionengravers.com
Mon Jan 21 23:00:46 UTC 2013


The current low-level support code in hwdrv_apci16xx.c is seriously
broken. Besides that, it's overly complicated.

Rewrite, and simplify, the low-level code so it complies with the
comedi API.

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    | 128 ++++-
 2 files changed, 103 insertions(+), 567 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..6c41f0b 100644
--- a/drivers/staging/comedi/drivers/addi_apci_16xx.c
+++ b/drivers/staging/comedi/drivers/addi_apci_16xx.c
@@ -1,9 +1,12 @@
 #include "../comedidev.h"
 #include "comedi_fc.h"
 
-#include "addi-data/addi_common.h"
-
-#include "addi-data/hwdrv_apci16xx.c"
+/*
+ * Register I/O map
+ */
+#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 +20,78 @@ 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_INPUT : COMEDI_OUTPUT;
+		return insn->n;
+	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 +112,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 +124,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 nubmer of subdevices needed 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