From 92eb37959f0e98376969e97bb6518c0f4b738241 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 21 Jan 2013 16:00:46 -0700 Subject: [PATCH] staging: comedi: addi_apci_16xx: rewrite low-level support code 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 Cc: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- .../comedi/drivers/addi-data/hwdrv_apci16xx.c | 542 ------------------ .../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 098ea59a41e3..000000000000 --- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci16xx.c +++ /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; -} diff --git a/drivers/staging/comedi/drivers/addi_apci_16xx.c b/drivers/staging/comedi/drivers/addi_apci_16xx.c index 899dae14a75c..6c41f0bfea34 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; } -- 2.30.2