mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-13 19:06:43 +07:00
Staging: comedi: add dt2811 driver
Driver for DataTranslation DT2811 From: David Schleef <ds@schleef.org> Cc: Ian Abbott <abbotti@mev.co.uk> Cc: Frank Mori Hess <fmhess@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
a4c879482e
commit
3c44371621
601
drivers/staging/comedi/drivers/dt2811.c
Normal file
601
drivers/staging/comedi/drivers/dt2811.c
Normal file
@ -0,0 +1,601 @@
|
||||
/*
|
||||
comedi/drivers/dt2811.c
|
||||
Hardware driver for Data Translation DT2811
|
||||
|
||||
COMEDI - Linux Control and Measurement Device Interface
|
||||
History:
|
||||
Base Version - David A. Schleef <ds@schleef.org>
|
||||
December 1998 - Updated to work. David does not have a DT2811
|
||||
board any longer so this was suffering from bitrot.
|
||||
Updated performed by ...
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
Driver: dt2811
|
||||
Description: Data Translation DT2811
|
||||
Author: ds
|
||||
Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
|
||||
Status: works
|
||||
|
||||
Configuration options:
|
||||
[0] - I/O port base address
|
||||
[1] - IRQ, although this is currently unused
|
||||
[2] - A/D reference
|
||||
0 = signle-ended
|
||||
1 = differential
|
||||
2 = pseudo-differential (common reference)
|
||||
[3] - A/D range
|
||||
0 = [-5,5]
|
||||
1 = [-2.5,2.5]
|
||||
2 = [0,5]
|
||||
[4] - D/A 0 range (same choices)
|
||||
[4] - D/A 1 range (same choices)
|
||||
*/
|
||||
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include <linux/ioport.h>
|
||||
|
||||
static const char *driver_name = "dt2811";
|
||||
|
||||
static const comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
|
||||
RANGE(0, 5),
|
||||
RANGE(0, 2.5),
|
||||
RANGE(0, 1.25),
|
||||
RANGE(0, 0.625)
|
||||
}
|
||||
};
|
||||
static const comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
|
||||
RANGE(-2.5, 2.5),
|
||||
RANGE(-1.25, 1.25),
|
||||
RANGE(-0.625, 0.625),
|
||||
RANGE(-0.3125, 0.3125)
|
||||
}
|
||||
};
|
||||
static const comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
|
||||
RANGE(-5, 5),
|
||||
RANGE(-2.5, 2.5),
|
||||
RANGE(-1.25, 1.25),
|
||||
RANGE(-0.625, 0.625)
|
||||
}
|
||||
};
|
||||
static const comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
|
||||
RANGE(0, 5),
|
||||
RANGE(0, 0.5),
|
||||
RANGE(0, 0.05),
|
||||
RANGE(0, 0.01)
|
||||
}
|
||||
};
|
||||
static const comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
|
||||
RANGE(-2.5, 2.5),
|
||||
RANGE(-0.25, 0.25),
|
||||
RANGE(-0.025, 0.025),
|
||||
RANGE(-0.005, 0.005)
|
||||
}
|
||||
};
|
||||
static const comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
|
||||
RANGE(-5, 5),
|
||||
RANGE(-0.5, 0.5),
|
||||
RANGE(-0.05, 0.05),
|
||||
RANGE(-0.01, 0.01)
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
0x00 ADCSR R/W A/D Control/Status Register
|
||||
bit 7 - (R) 1 indicates A/D conversion done
|
||||
reading ADDAT clears bit
|
||||
(W) ignored
|
||||
bit 6 - (R) 1 indicates A/D error
|
||||
(W) ignored
|
||||
bit 5 - (R) 1 indicates A/D busy, cleared at end
|
||||
of conversion
|
||||
(W) ignored
|
||||
bit 4 - (R) 0
|
||||
(W)
|
||||
bit 3 - (R) 0
|
||||
bit 2 - (R/W) 1 indicates interrupts enabled
|
||||
bits 1,0 - (R/W) mode bits
|
||||
00 single conversion on ADGCR load
|
||||
01 continuous conversion, internal clock,
|
||||
(clock enabled on ADGCR load)
|
||||
10 continuous conversion, internal clock,
|
||||
external trigger
|
||||
11 continuous conversion, external clock,
|
||||
external trigger
|
||||
|
||||
0x01 ADGCR R/W A/D Gain/Channel Register
|
||||
bit 6,7 - (R/W) gain select
|
||||
00 gain=1, both PGH, PGL models
|
||||
01 gain=2 PGH, 10 PGL
|
||||
10 gain=4 PGH, 100 PGL
|
||||
11 gain=8 PGH, 500 PGL
|
||||
bit 4,5 - reserved
|
||||
bit 3-0 - (R/W) channel select
|
||||
channel number from 0-15
|
||||
|
||||
0x02,0x03 (R) ADDAT A/D Data Register
|
||||
(W) DADAT0 D/A Data Register 0
|
||||
0x02 low byte
|
||||
0x03 high byte
|
||||
|
||||
0x04,0x05 (W) DADAT0 D/A Data Register 1
|
||||
|
||||
0x06 (R) DIO0 Digital Input Port 0
|
||||
(W) DIO1 Digital Output Port 1
|
||||
|
||||
0x07 TMRCTR (R/W) Timer/Counter Register
|
||||
bits 6,7 - reserved
|
||||
bits 5-3 - Timer frequency control (mantissa)
|
||||
543 divisor freqency (kHz)
|
||||
000 1 600
|
||||
001 10 60
|
||||
010 2 300
|
||||
011 3 200
|
||||
100 4 150
|
||||
101 5 120
|
||||
110 6 100
|
||||
111 12 50
|
||||
bits 2-0 - Timer frequency control (exponent)
|
||||
210 multiply divisor/divide frequency by
|
||||
000 1
|
||||
001 10
|
||||
010 100
|
||||
011 1000
|
||||
100 10000
|
||||
101 100000
|
||||
110 1000000
|
||||
111 10000000
|
||||
|
||||
*/
|
||||
|
||||
#define TIMEOUT 10000
|
||||
|
||||
#define DT2811_SIZE 8
|
||||
|
||||
#define DT2811_ADCSR 0
|
||||
#define DT2811_ADGCR 1
|
||||
#define DT2811_ADDATLO 2
|
||||
#define DT2811_ADDATHI 3
|
||||
#define DT2811_DADAT0LO 2
|
||||
#define DT2811_DADAT0HI 3
|
||||
#define DT2811_DADAT1LO 4
|
||||
#define DT2811_DADAT1HI 5
|
||||
#define DT2811_DIO 6
|
||||
#define DT2811_TMRCTR 7
|
||||
|
||||
/*
|
||||
* flags
|
||||
*/
|
||||
|
||||
/* ADCSR */
|
||||
|
||||
#define DT2811_ADDONE 0x80
|
||||
#define DT2811_ADERROR 0x40
|
||||
#define DT2811_ADBUSY 0x20
|
||||
#define DT2811_CLRERROR 0x10
|
||||
#define DT2811_INTENB 0x04
|
||||
#define DT2811_ADMODE 0x03
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const comedi_lrange *bip_5;
|
||||
const comedi_lrange *bip_2_5;
|
||||
const comedi_lrange *unip_5;
|
||||
} boardtype;
|
||||
static const boardtype boardtypes[] = {
|
||||
{"dt2811-pgh",
|
||||
&range_dt2811_pgh_ai_5_bipolar,
|
||||
&range_dt2811_pgh_ai_2_5_bipolar,
|
||||
&range_dt2811_pgh_ai_5_unipolar,
|
||||
},
|
||||
{"dt2811-pgl",
|
||||
&range_dt2811_pgl_ai_5_bipolar,
|
||||
&range_dt2811_pgl_ai_2_5_bipolar,
|
||||
&range_dt2811_pgl_ai_5_unipolar,
|
||||
},
|
||||
};
|
||||
|
||||
#define this_board ((const boardtype *)dev->board_ptr)
|
||||
|
||||
static int dt2811_attach(comedi_device * dev, comedi_devconfig * it);
|
||||
static int dt2811_detach(comedi_device * dev);
|
||||
static comedi_driver driver_dt2811 = {
|
||||
driver_name:"dt2811",
|
||||
module:THIS_MODULE,
|
||||
attach:dt2811_attach,
|
||||
detach:dt2811_detach,
|
||||
board_name:&boardtypes[0].name,
|
||||
num_names:sizeof(boardtypes) / sizeof(boardtype),
|
||||
offset:sizeof(boardtype),
|
||||
};
|
||||
|
||||
COMEDI_INITCLEANUP(driver_dt2811);
|
||||
|
||||
static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data);
|
||||
static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data);
|
||||
static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data);
|
||||
static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data);
|
||||
static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data);
|
||||
|
||||
enum { card_2811_pgh, card_2811_pgl };
|
||||
typedef struct {
|
||||
int ntrig;
|
||||
int curadchan;
|
||||
enum {
|
||||
adc_singleended, adc_diff, adc_pseudo_diff
|
||||
} adc_mux;
|
||||
enum {
|
||||
dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
|
||||
} dac_range[2];
|
||||
const comedi_lrange *range_type_list[2];
|
||||
lsampl_t ao_readback[2];
|
||||
} dt2811_private;
|
||||
|
||||
#define devpriv ((dt2811_private *)dev->private)
|
||||
|
||||
static const comedi_lrange *dac_range_types[] = {
|
||||
&range_bipolar5,
|
||||
&range_bipolar2_5,
|
||||
&range_unipolar5
|
||||
};
|
||||
|
||||
#define DT2811_TIMEOUT 5
|
||||
|
||||
#if 0
|
||||
static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
|
||||
{
|
||||
int lo, hi;
|
||||
int data;
|
||||
comedi_device *dev = d;
|
||||
|
||||
if (!dev->attached) {
|
||||
comedi_error(dev, "spurious interrupt");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
lo = inb(dev->iobase + DT2811_ADDATLO);
|
||||
hi = inb(dev->iobase + DT2811_ADDATHI);
|
||||
|
||||
data = lo + (hi << 8);
|
||||
|
||||
if (!(--devpriv->ntrig)) {
|
||||
/* how to turn off acquisition */
|
||||
s->async->events |= COMEDI_SB_EOA;
|
||||
}
|
||||
comedi_event(dev, s);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
options[0] Board base address
|
||||
options[1] IRQ
|
||||
options[2] Input configuration
|
||||
0 == single-ended
|
||||
1 == differential
|
||||
2 == pseudo-differential
|
||||
options[3] Analog input range configuration
|
||||
0 == bipolar 5 (-5V -- +5V)
|
||||
1 == bipolar 2.5V (-2.5V -- +2.5V)
|
||||
2 == unipolar 5V (0V -- +5V)
|
||||
options[4] Analog output 0 range configuration
|
||||
0 == bipolar 5 (-5V -- +5V)
|
||||
1 == bipolar 2.5V (-2.5V -- +2.5V)
|
||||
2 == unipolar 5V (0V -- +5V)
|
||||
options[5] Analog output 1 range configuration
|
||||
0 == bipolar 5 (-5V -- +5V)
|
||||
1 == bipolar 2.5V (-2.5V -- +2.5V)
|
||||
2 == unipolar 5V (0V -- +5V)
|
||||
*/
|
||||
|
||||
static int dt2811_attach(comedi_device * dev, comedi_devconfig * it)
|
||||
{
|
||||
//int i, irq;
|
||||
//unsigned long irqs;
|
||||
//long flags;
|
||||
int ret;
|
||||
comedi_subdevice *s;
|
||||
unsigned long iobase;
|
||||
|
||||
iobase = it->options[0];
|
||||
|
||||
printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
|
||||
|
||||
if (!request_region(iobase, DT2811_SIZE, driver_name)) {
|
||||
printk("I/O port conflict\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev->iobase = iobase;
|
||||
dev->board_name = this_board->name;
|
||||
|
||||
#if 0
|
||||
outb(0, dev->iobase + DT2811_ADCSR);
|
||||
comedi_udelay(100);
|
||||
i = inb(dev->iobase + DT2811_ADDATLO);
|
||||
i = inb(dev->iobase + DT2811_ADDATHI);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
irq = it->options[1];
|
||||
if (irq < 0) {
|
||||
save_flags(flags);
|
||||
sti();
|
||||
irqs = probe_irq_on();
|
||||
|
||||
outb(DT2811_CLRERROR | DT2811_INTENB,
|
||||
dev->iobase + DT2811_ADCSR);
|
||||
outb(0, dev->iobase + DT2811_ADGCR);
|
||||
|
||||
comedi_udelay(100);
|
||||
|
||||
irq = probe_irq_off(irqs);
|
||||
restore_flags(flags);
|
||||
|
||||
/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
|
||||
|
||||
if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
|
||||
printk("error probing irq (bad) \n");
|
||||
}
|
||||
dev->irq = 0;
|
||||
if (irq > 0) {
|
||||
i = inb(dev->iobase + DT2811_ADDATLO);
|
||||
i = inb(dev->iobase + DT2811_ADDATHI);
|
||||
printk("(irq = %d)\n", irq);
|
||||
ret = comedi_request_irq(irq, dt2811_interrupt, 0,
|
||||
driver_name, dev);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
dev->irq = irq;
|
||||
} else if (irq == 0) {
|
||||
printk("(no irq)\n");
|
||||
} else {
|
||||
printk("( multiple irq's -- this is bad! )\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ret = alloc_subdevices(dev, 4)) < 0)
|
||||
return ret;
|
||||
if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0)
|
||||
return ret;
|
||||
switch (it->options[2]) {
|
||||
case 0:
|
||||
devpriv->adc_mux = adc_singleended;
|
||||
break;
|
||||
case 1:
|
||||
devpriv->adc_mux = adc_diff;
|
||||
break;
|
||||
case 2:
|
||||
devpriv->adc_mux = adc_pseudo_diff;
|
||||
break;
|
||||
default:
|
||||
devpriv->adc_mux = adc_singleended;
|
||||
break;
|
||||
}
|
||||
switch (it->options[4]) {
|
||||
case 0:
|
||||
devpriv->dac_range[0] = dac_bipolar_5;
|
||||
break;
|
||||
case 1:
|
||||
devpriv->dac_range[0] = dac_bipolar_2_5;
|
||||
break;
|
||||
case 2:
|
||||
devpriv->dac_range[0] = dac_unipolar_5;
|
||||
break;
|
||||
default:
|
||||
devpriv->dac_range[0] = dac_bipolar_5;
|
||||
break;
|
||||
}
|
||||
switch (it->options[5]) {
|
||||
case 0:
|
||||
devpriv->dac_range[1] = dac_bipolar_5;
|
||||
break;
|
||||
case 1:
|
||||
devpriv->dac_range[1] = dac_bipolar_2_5;
|
||||
break;
|
||||
case 2:
|
||||
devpriv->dac_range[1] = dac_unipolar_5;
|
||||
break;
|
||||
default:
|
||||
devpriv->dac_range[1] = dac_bipolar_5;
|
||||
break;
|
||||
}
|
||||
|
||||
s = dev->subdevices + 0;
|
||||
/* initialize the ADC subdevice */
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags = SDF_READABLE | SDF_GROUND;
|
||||
s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
|
||||
s->insn_read = dt2811_ai_insn;
|
||||
s->maxdata = 0xfff;
|
||||
switch (it->options[3]) {
|
||||
case 0:
|
||||
default:
|
||||
s->range_table = this_board->bip_5;
|
||||
break;
|
||||
case 1:
|
||||
s->range_table = this_board->bip_2_5;
|
||||
break;
|
||||
case 2:
|
||||
s->range_table = this_board->unip_5;
|
||||
break;
|
||||
}
|
||||
|
||||
s = dev->subdevices + 1;
|
||||
/* ao subdevice */
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 2;
|
||||
s->insn_write = dt2811_ao_insn;
|
||||
s->insn_read = dt2811_ao_insn_read;
|
||||
s->maxdata = 0xfff;
|
||||
s->range_table_list = devpriv->range_type_list;
|
||||
devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
|
||||
devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
|
||||
|
||||
s = dev->subdevices + 2;
|
||||
/* di subdevice */
|
||||
s->type = COMEDI_SUBD_DI;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
s->n_chan = 8;
|
||||
s->insn_bits = dt2811_di_insn_bits;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
|
||||
s = dev->subdevices + 3;
|
||||
/* do subdevice */
|
||||
s->type = COMEDI_SUBD_DO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 8;
|
||||
s->insn_bits = dt2811_do_insn_bits;
|
||||
s->maxdata = 1;
|
||||
s->state = 0;
|
||||
s->range_table = &range_digital;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dt2811_detach(comedi_device * dev)
|
||||
{
|
||||
printk("comedi%d: dt2811: remove\n", dev->minor);
|
||||
|
||||
if (dev->irq) {
|
||||
comedi_free_irq(dev->irq, dev);
|
||||
}
|
||||
if (dev->iobase) {
|
||||
release_region(dev->iobase, DT2811_SIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data)
|
||||
{
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
int timeout = DT2811_TIMEOUT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < insn->n; i++) {
|
||||
outb(chan, dev->iobase + DT2811_ADGCR);
|
||||
|
||||
while (timeout
|
||||
&& inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
|
||||
timeout--;
|
||||
if (!timeout)
|
||||
return -ETIME;
|
||||
|
||||
data[i] = inb(dev->iobase + DT2811_ADDATLO);
|
||||
data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
|
||||
data[i] &= 0xfff;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Wow. This is code from the Comedi stone age. But it hasn't been
|
||||
* replaced, so I'll let it stay. */
|
||||
int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
|
||||
{
|
||||
comedi_device *dev = comedi_devices + minor;
|
||||
|
||||
if (adtrig->n < 1)
|
||||
return 0;
|
||||
dev->curadchan = adtrig->chan;
|
||||
switch (dev->i_admode) {
|
||||
case COMEDI_MDEMAND:
|
||||
dev->ntrig = adtrig->n - 1;
|
||||
/*printk("dt2811: AD soft trigger\n"); */
|
||||
/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
|
||||
outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
|
||||
do_gettimeofday(&trigtime);
|
||||
break;
|
||||
case COMEDI_MCONTS:
|
||||
dev->ntrig = adtrig->n;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data)
|
||||
{
|
||||
int i;
|
||||
int chan;
|
||||
|
||||
chan = CR_CHAN(insn->chanspec);
|
||||
|
||||
for (i = 0; i < insn->n; i++) {
|
||||
outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
|
||||
outb((data[i] >> 8) & 0xff,
|
||||
dev->iobase + DT2811_DADAT0HI + 2 * chan);
|
||||
devpriv->ao_readback[chan] = data[i];
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data)
|
||||
{
|
||||
int i;
|
||||
int chan;
|
||||
|
||||
chan = CR_CHAN(insn->chanspec);
|
||||
|
||||
for (i = 0; i < insn->n; i++) {
|
||||
data[i] = devpriv->ao_readback[chan];
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data)
|
||||
{
|
||||
if (insn->n != 2)
|
||||
return -EINVAL;
|
||||
|
||||
data[1] = inb(dev->iobase + DT2811_DIO);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
|
||||
comedi_insn * insn, lsampl_t * data)
|
||||
{
|
||||
if (insn->n != 2)
|
||||
return -EINVAL;
|
||||
|
||||
s->state &= ~data[0];
|
||||
s->state |= data[0] & data[1];
|
||||
outb(s->state, dev->iobase + DT2811_DIO);
|
||||
|
||||
data[1] = s->state;
|
||||
|
||||
return 2;
|
||||
}
|
Loading…
Reference in New Issue
Block a user