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@xxxxxxxxxxxxxxxxxxx> Cc: Ian Abbott <abbotti@xxxxxxxxx> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- .../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 @@ -/** -@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@xxxxxxxxxxxxx - -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@xxxxxxxxxxxxx | - | 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 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/devel