Commit da6578ab authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: addi_apci_3xxx: refactor the ttl digital i/o support

Currently, the subdevice functions used to configure and read/write the
ttl digital i/o ports is way over to complicated. The (*insn_config)
function also abuses the comedi API by overriding the instruction
command passed in data[0].

Fix the ttl digital i/o support to work like the comedi core expects.

The (*insn_config) function supports the three instructions common for
COMEDI_SUBD_DIO subdevices:

    INSN_CONFIG_DIO_INPUT - configure the specified channel as input
    INSN_CONFIG_DIO_OUTPUT - configure the specified channel as output
    INSN_CONFIG_DIO_QUERY - returns the status of the specified channel

    Port 0 (channels 0-7) is always input
    Port 1 (channels 8-15) is always output
    Port 2 (channels 9-23) are programmable i/o (all channels are input or output)

The (*insn_bits) function allows writing to the output channels and returns
the state of all channels.

The (*insn_read) and (*insn_write) functions are not required. The comedi
core will emulate them using the (*insn_bits) function.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0e771e49
......@@ -514,523 +514,3 @@ static int i_APCI3XXX_InsnReadAnalogInput(struct comedi_device *dev,
}
return i_ReturnValue;
}
/*
+----------------------------------------------------------------------------+
| TTL FUNCTIONS |
+----------------------------------------------------------------------------+
*/
/*
+----------------------------------------------------------------------------+
| Function Name : int i_APCI3XXX_InsnConfigInitTTLIO |
| (struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
+----------------------------------------------------------------------------+
| Task You must calling this function be |
| for you call any other function witch access of TTL. |
| APCI3XXX_TTL_INIT_DIRECTION_PORT2(user inputs for direction)|
+----------------------------------------------------------------------------+
| Input Parameters : b_InitType = (unsigned char) data[0]; |
| b_Port2Mode = (unsigned char) data[1]; |
+----------------------------------------------------------------------------+
| Output Parameters : - |
+----------------------------------------------------------------------------+
| Return Value :>0: No error |
| -1: Port 2 mode selection is wrong |
| .... |
| -100 : Config command error |
| -101 : Data size error |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnConfigInitTTLIO(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
int i_ReturnValue = insn->n;
unsigned char b_Command = 0;
/************************/
/* Test the buffer size */
/************************/
if (insn->n >= 1) {
/*******************/
/* Get the command */
/* **************** */
b_Command = (unsigned char) data[0];
/********************/
/* Test the command */
/********************/
if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
/***************************************/
/* Test the initialisation buffer size */
/***************************************/
if ((b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)
&& (insn->n != 2)) {
/*******************/
/* Data size error */
/*******************/
printk("Buffer size error\n");
i_ReturnValue = -101;
}
} else {
/************************/
/* Config command error */
/************************/
printk("Command selection error\n");
i_ReturnValue = -100;
}
} else {
/*******************/
/* Data size error */
/*******************/
printk("Buffer size error\n");
i_ReturnValue = -101;
}
/*********************************************************************************/
/* Test if no error occur and APCI3XXX_TTL_INIT_DIRECTION_PORT2 command selected */
/*********************************************************************************/
if ((i_ReturnValue >= 0)
&& (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2)) {
/**********************/
/* Test the direction */
/**********************/
if ((data[1] == 0) || (data[1] == 0xFF)) {
/**************************/
/* Save the configuration */
/**************************/
devpriv->ul_TTLPortConfiguration[0] =
devpriv->ul_TTLPortConfiguration[0] | data[1];
} else {
/************************/
/* Port direction error */
/************************/
printk("Port 2 direction selection error\n");
i_ReturnValue = -1;
}
}
/**************************/
/* Test if no error occur */
/**************************/
if (i_ReturnValue >= 0) {
/***********************************/
/* Test if TTL port initilaisation */
/***********************************/
if (b_Command == APCI3XXX_TTL_INIT_DIRECTION_PORT2) {
/*************************/
/* Set the configuration */
/*************************/
outl(data[1], devpriv->iobase + 224);
}
}
return i_ReturnValue;
}
/*
+----------------------------------------------------------------------------+
| TTL INPUT FUNCTIONS |
+----------------------------------------------------------------------------+
*/
/*
+----------------------------------------------------------------------------+
| Function Name : int i_APCI3XXX_InsnBitsTTLIO |
| (struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
+----------------------------------------------------------------------------+
| Task : Write the selected output mask and read the status from|
| all TTL channles |
+----------------------------------------------------------------------------+
| Input Parameters : dw_ChannelMask = data [0]; |
| dw_BitMask = data [1]; |
+----------------------------------------------------------------------------+
| Output Parameters : data[1] : All TTL channles states |
+----------------------------------------------------------------------------+
| Return Value : >0 : No error |
| -4 : Channel mask error |
| -101 : Data size error |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnBitsTTLIO(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
int i_ReturnValue = insn->n;
unsigned char b_ChannelCpt = 0;
unsigned int dw_ChannelMask = 0;
unsigned int dw_BitMask = 0;
unsigned int dw_Status = 0;
/************************/
/* Test the buffer size */
/************************/
if (insn->n >= 2) {
/*******************************/
/* Get the channe and bit mask */
/*******************************/
dw_ChannelMask = data[0];
dw_BitMask = data[1];
/*************************/
/* Test the channel mask */
/*************************/
if (((dw_ChannelMask & 0XFF00FF00) == 0) &&
(((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0xFF)
|| (((devpriv->ul_TTLPortConfiguration[0] &
0xFF) == 0)
&& ((dw_ChannelMask & 0XFF0000) ==
0)))) {
/*********************************/
/* Test if set/reset any channel */
/*********************************/
if (dw_ChannelMask) {
/****************************************/
/* Test if set/rest any port 0 channels */
/****************************************/
if (dw_ChannelMask & 0xFF) {
/*******************************************/
/* Read port 0 (first digital output port) */
/*******************************************/
dw_Status = inl(devpriv->iobase + 80);
for (b_ChannelCpt = 0; b_ChannelCpt < 8;
b_ChannelCpt++) {
if ((dw_ChannelMask >>
b_ChannelCpt) &
1) {
dw_Status =
(dw_Status &
(0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
}
}
outl(dw_Status, devpriv->iobase + 80);
}
/****************************************/
/* Test if set/rest any port 2 channels */
/****************************************/
if (dw_ChannelMask & 0xFF0000) {
dw_BitMask = dw_BitMask >> 16;
dw_ChannelMask = dw_ChannelMask >> 16;
/********************************************/
/* Read port 2 (second digital output port) */
/********************************************/
dw_Status = inl(devpriv->iobase + 112);
for (b_ChannelCpt = 0; b_ChannelCpt < 8;
b_ChannelCpt++) {
if ((dw_ChannelMask >>
b_ChannelCpt) &
1) {
dw_Status =
(dw_Status &
(0xFF - (1 << b_ChannelCpt))) | (dw_BitMask & (1 << b_ChannelCpt));
}
}
outl(dw_Status, devpriv->iobase + 112);
}
}
/*******************************************/
/* Read port 0 (first digital output port) */
/*******************************************/
data[1] = inl(devpriv->iobase + 80);
/******************************************/
/* Read port 1 (first digital input port) */
/******************************************/
data[1] = data[1] | (inl(devpriv->iobase + 64) << 8);
/************************/
/* Test if port 2 input */
/************************/
if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF) == 0) {
data[1] =
data[1] | (inl(devpriv->iobase +
96) << 16);
} else {
data[1] =
data[1] | (inl(devpriv->iobase +
112) << 16);
}
} else {
/************************/
/* Config command error */
/************************/
printk("Channel mask error\n");
i_ReturnValue = -4;
}
} else {
/*******************/
/* Data size error */
/*******************/
printk("Buffer size error\n");
i_ReturnValue = -101;
}
return i_ReturnValue;
}
/*
+----------------------------------------------------------------------------+
| Function Name : int i_APCI3XXX_InsnReadTTLIO |
| (struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
+----------------------------------------------------------------------------+
| Task : Read the status from selected channel |
+----------------------------------------------------------------------------+
| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
+----------------------------------------------------------------------------+
| Output Parameters : data[0] : Selected TTL channel state |
+----------------------------------------------------------------------------+
| Return Value : 0 : No error |
| -3 : Channel selection error |
| -101 : Data size error |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnReadTTLIO(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
unsigned char b_Channel = (unsigned char) CR_CHAN(insn->chanspec);
int i_ReturnValue = insn->n;
unsigned int *pls_ReadData = data;
/************************/
/* Test the buffer size */
/************************/
if (insn->n >= 1) {
/***********************/
/* Test if read port 0 */
/***********************/
if (b_Channel < 8) {
/*******************************************/
/* Read port 0 (first digital output port) */
/*******************************************/
pls_ReadData[0] = inl(devpriv->iobase + 80);
pls_ReadData[0] = (pls_ReadData[0] >> b_Channel) & 1;
} else {
/***********************/
/* Test if read port 1 */
/***********************/
if ((b_Channel > 7) && (b_Channel < 16)) {
/******************************************/
/* Read port 1 (first digital input port) */
/******************************************/
pls_ReadData[0] = inl(devpriv->iobase + 64);
pls_ReadData[0] =
(pls_ReadData[0] >> (b_Channel -
8)) & 1;
} else {
/***********************/
/* Test if read port 2 */
/***********************/
if ((b_Channel > 15) && (b_Channel < 24)) {
/************************/
/* Test if port 2 input */
/************************/
if ((devpriv->ul_TTLPortConfiguration[0]
& 0xFF) == 0) {
pls_ReadData[0] =
inl(devpriv->iobase +
96);
pls_ReadData[0] =
(pls_ReadData[0] >>
(b_Channel - 16)) & 1;
} else {
pls_ReadData[0] =
inl(devpriv->iobase +
112);
pls_ReadData[0] =
(pls_ReadData[0] >>
(b_Channel - 16)) & 1;
}
} else {
/***************************/
/* Channel selection error */
/***************************/
i_ReturnValue = -3;
printk("Channel %d selection error\n",
b_Channel);
}
}
}
} else {
/*******************/
/* Data size error */
/*******************/
printk("Buffer size error\n");
i_ReturnValue = -101;
}
return i_ReturnValue;
}
/*
+----------------------------------------------------------------------------+
| TTL OUTPUT FUNCTIONS |
+----------------------------------------------------------------------------+
*/
/*
+----------------------------------------------------------------------------+
| Function Name : int i_APCI3XXX_InsnWriteTTLIO |
| (struct comedi_device *dev, |
| struct comedi_subdevice *s, |
| struct comedi_insn *insn, |
| unsigned int *data) |
+----------------------------------------------------------------------------+
| Task : Set the state from TTL output channel |
+----------------------------------------------------------------------------+
| Input Parameters : b_Channel = CR_CHAN(insn->chanspec) |
| b_State = data [0] |
+----------------------------------------------------------------------------+
| Output Parameters : - |
+----------------------------------------------------------------------------+
| Return Value : 0 : No error |
| -3 : Channel selection error |
| -101 : Data size error |
+----------------------------------------------------------------------------+
*/
static int i_APCI3XXX_InsnWriteTTLIO(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
int i_ReturnValue = insn->n;
unsigned char b_Channel = (unsigned char) CR_CHAN(insn->chanspec);
unsigned char b_State = 0;
unsigned int dw_Status = 0;
/************************/
/* Test the buffer size */
/************************/
if (insn->n >= 1) {
b_State = (unsigned char) data[0];
/***********************/
/* Test if read port 0 */
/***********************/
if (b_Channel < 8) {
/*****************************************************************************/
/* Read port 0 (first digital output port) and set/reset the selected channel */
/*****************************************************************************/
dw_Status = inl(devpriv->iobase + 80);
dw_Status =
(dw_Status & (0xFF -
(1 << b_Channel))) | ((b_State & 1) <<
b_Channel);
outl(dw_Status, devpriv->iobase + 80);
} else {
/***********************/
/* Test if read port 2 */
/***********************/
if ((b_Channel > 15) && (b_Channel < 24)) {
/*************************/
/* Test if port 2 output */
/*************************/
if ((devpriv->ul_TTLPortConfiguration[0] & 0xFF)
== 0xFF) {
/*****************************************************************************/
/* Read port 2 (first digital output port) and set/reset the selected channel */
/*****************************************************************************/
dw_Status = inl(devpriv->iobase + 112);
dw_Status =
(dw_Status & (0xFF -
(1 << (b_Channel -
16)))) |
((b_State & 1) << (b_Channel -
16));
outl(dw_Status, devpriv->iobase + 112);
} else {
/***************************/
/* Channel selection error */
/***************************/
i_ReturnValue = -3;
printk("Channel %d selection error\n",
b_Channel);
}
} else {
/***************************/
/* Channel selection error */
/***************************/
i_ReturnValue = -3;
printk("Channel %d selection error\n",
b_Channel);
}
}
} else {
/*******************/
/* Data size error */
/*******************/
printk("Buffer size error\n");
i_ReturnValue = -101;
}
return i_ReturnValue;
}
......@@ -378,7 +378,6 @@ struct apci3xxx_private {
unsigned char b_EocEosConversionTimeBase;
unsigned char b_SingelDiff;
struct task_struct *tsk_Current;
unsigned int ul_TTLPortConfiguration[10];
};
#include "addi-data/hwdrv_apci3xxx.c"
......@@ -477,6 +476,83 @@ static int apci3xxx_do_insn_bits(struct comedi_device *dev,
return insn->n;
}
static int apci3xxx_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int mask = 1 << chan;
unsigned int bits;
/*
* Port 0 (channels 0-7) are always inputs
* Port 1 (channels 8-15) are always outputs
* Port 2 (channels 16-23) are programmable i/o
*
* Changing any channel in port 2 changes the entire port.
*/
if (mask & 0xff0000)
bits = 0xff0000;
else
bits = 0;
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;
default:
return -EINVAL;
}
/* update port 2 configuration */
if (bits)
outl((s->io_bits >> 24) & 0xff, devpriv->iobase + 224);
return insn->n;
}
static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct apci3xxx_private *devpriv = dev->private;
unsigned int mask = data[0];
unsigned int bits = data[1];
unsigned int val;
/* only update output channels */
mask &= s->io_bits;
if (mask) {
s->state &= ~mask;
s->state |= (bits & mask);
if (mask & 0xff)
outl(s->state & 0xff, devpriv->iobase + 80);
if (mask & 0xff0000)
outl((s->state >> 16) & 0xff, devpriv->iobase + 112);
}
val = inl(devpriv->iobase + 80);
val |= (inl(devpriv->iobase + 64) << 8);
if (s->io_bits & 0xff0000)
val |= (inl(devpriv->iobase + 112) << 16);
else
val |= (inl(devpriv->iobase + 96) << 16);
data[1] = val;
return insn->n;
}
static int apci3xxx_reset(struct comedi_device *dev)
{
struct apci3xxx_private *devpriv = dev->private;
......@@ -632,10 +708,8 @@ static int apci3xxx_auto_attach(struct comedi_device *dev,
s->maxdata = 1;
s->io_bits = 0xff; /* channels 0-7 are always outputs */
s->range_table = &range_digital;
s->insn_config = i_APCI3XXX_InsnConfigInitTTLIO;
s->insn_bits = i_APCI3XXX_InsnBitsTTLIO;
s->insn_read = i_APCI3XXX_InsnReadTTLIO;
s->insn_write = i_APCI3XXX_InsnWriteTTLIO;
s->insn_config = apci3xxx_dio_insn_config;
s->insn_bits = apci3xxx_dio_insn_bits;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment