staging: comedi: addi_apci_16xx: rewrite low-level support code
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Mon, 21 Jan 2013 23:00:46 +0000 (16:00 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Jan 2013 00:08:05 +0000 (16:08 -0800)
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@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c [deleted file]
drivers/staging/comedi/drivers/addi_apci_16xx.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 (file)
index 098ea59..0000000
+++ /dev/null
@@ -1,542 +0,0 @@
-/**
-@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@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.
-
-@endverbatim
-*/
-/*
-
-  +-----------------------------------------------------------------------+
-  | (C) ADDI-DATA GmbH          Dieselstraße 3       D-77833 Ottersweier  |
-  +-----------------------------------------------------------------------+
-  | Tel : +49 (0) 7223/9493-0     | email    : info@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;
-}
index 899dae14a75ce4fa25e27c7d73fb8edab394fded..6c41f0bfea3446d1acbc12608c7053f7d7a47b4a 100644 (file)
@@ -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;
 }