Commit 0e8db97a authored by Michal Dobes's avatar Michal Dobes Committed by Greg Kroah-Hartman

Staging: comedi: add adv_pci1710 driver

For Advantech cards:
	PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720,

From: Michal Dobes <dobes@tesnet.cz>
Cc: David Schleef <ds@schleef.org>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3063d6de
/*
* comedi/drivers/adv_pci1710.c
*
* Author: Michal Dobes <dobes@tesnet.cz>
*
* Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
* for testing and informations.
*
* hardware driver for Advantech cards:
* card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
* driver: pci1710, pci1710hg, pci1711, pci1713, pci1720, pci1731
*
* Options:
* [0] - PCI bus number - if bus number and slot number are 0,
* then driver search for first unused card
* [1] - PCI slot number
*
*/
/*
Driver: adv_pci1710
Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
Advantech PCI-1720, PCI-1731
Author: Michal Dobes <dobes@tesnet.cz>
Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
PCI-1731
Status: works
This driver supports AI, AO, DI and DO subdevices.
AI subdevice supports cmd and insn interface,
other subdevices support only insn interface.
The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
driver cannot distinguish between them, as would be normal for a
PCI driver.
Configuration options:
[0] - PCI bus of device (optional)
[1] - PCI slot of device (optional)
If bus/slot is not specified, the first available PCI
device will be used.
*/
#include "../comedidev.h"
#include "comedi_pci.h"
#include "8253.h"
#include "amcc_s5933.h"
#define PCI171x_PARANOIDCHECK /* if defined, then is used code which control correct channel number on every 12 bit sample */
#undef PCI171X_EXTDEBUG
#define DRV_NAME "adv_pci1710"
#undef DPRINTK
#ifdef PCI171X_EXTDEBUG
#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
#else
#define DPRINTK(fmt, args...)
#endif
// hardware types of the cards
#define TYPE_PCI171X 0
#define TYPE_PCI1713 2
#define TYPE_PCI1720 3
#define IORANGE_171x 32
#define IORANGE_1720 16
#define PCI171x_AD_DATA 0 /* R: A/D data */
#define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
#define PCI171x_RANGE 2 /* W: A/D gain/range register */
#define PCI171x_MUX 4 /* W: A/D multiplexor control */
#define PCI171x_STATUS 6 /* R: status register */
#define PCI171x_CONTROL 6 /* W: control register */
#define PCI171x_CLRINT 8 /* W: clear interrupts request */
#define PCI171x_CLRFIFO 9 /* W: clear FIFO */
#define PCI171x_DA1 10 /* W: D/A register */
#define PCI171x_DA2 12 /* W: D/A register */
#define PCI171x_DAREF 14 /* W: D/A reference control */
#define PCI171x_DI 16 /* R: digi inputs */
#define PCI171x_DO 16 /* R: digi inputs */
#define PCI171x_CNT0 24 /* R/W: 8254 couter 0 */
#define PCI171x_CNT1 26 /* R/W: 8254 couter 1 */
#define PCI171x_CNT2 28 /* R/W: 8254 couter 2 */
#define PCI171x_CNTCTRL 30 /* W: 8254 counter control */
// upper bits from status register (PCI171x_STATUS) (lower is same woth control reg)
#define Status_FE 0x0100 /* 1=FIFO is empty */
#define Status_FH 0x0200 /* 1=FIFO is half full */
#define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
#define Status_IRQ 0x0800 /* 1=IRQ occured */
// bits from control register (PCI171x_CONTROL)
#define Control_CNT0 0x0040 /* 1=CNT0 have external source, 0=have internal 100kHz source */
#define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
#define Control_IRQEN 0x0010 /* 1=enable IRQ */
#define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
#define Control_EXT 0x0004 /* 1=external trigger source */
#define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
#define Control_SW 0x0001 /* 1=enable software trigger source */
// bits from counter control register (PCI171x_CNTCTRL)
#define Counter_BCD 0x0001 /* 0 = binary counter, 1 = BCD counter */
#define Counter_M0 0x0002 /* M0-M2 select modes 0-5 */
#define Counter_M1 0x0004 /* 000 = mode 0, 010 = mode 2 ... */
#define Counter_M2 0x0008
#define Counter_RW0 0x0010 /* RW0/RW1 select read/write mode */
#define Counter_RW1 0x0020
#define Counter_SC0 0x0040 /* Select Counter. Only 00 or 11 may */
#define Counter_SC1 0x0080 /* be used, 00 for CNT0, 11 for read-back command */
#define PCI1720_DA0 0 /* W: D/A register 0 */
#define PCI1720_DA1 2 /* W: D/A register 1 */
#define PCI1720_DA2 4 /* W: D/A register 2 */
#define PCI1720_DA3 6 /* W: D/A register 3 */
#define PCI1720_RANGE 8 /* R/W: D/A range register */
#define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
#define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
// D/A synchronized control (PCI1720_SYNCONT)
#define Syncont_SC0 1 /* set synchronous output mode */
static const comedi_lrange range_pci1710_3 = { 9, {
BIP_RANGE(5),
BIP_RANGE(2.5),
BIP_RANGE(1.25),
BIP_RANGE(0.625),
BIP_RANGE(10),
UNI_RANGE(10),
UNI_RANGE(5),
UNI_RANGE(2.5),
UNI_RANGE(1.25)
}
};
static const char range_codes_pci1710_3[] =
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x11, 0x12, 0x13 };
static const comedi_lrange range_pci1710hg = { 12, {
BIP_RANGE(5),
BIP_RANGE(0.5),
BIP_RANGE(0.05),
BIP_RANGE(0.005),
BIP_RANGE(10),
BIP_RANGE(1),
BIP_RANGE(0.1),
BIP_RANGE(0.01),
UNI_RANGE(10),
UNI_RANGE(1),
UNI_RANGE(0.1),
UNI_RANGE(0.01)
}
};
static const char range_codes_pci1710hg[] =
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12,
0x13 };
static const comedi_lrange range_pci17x1 = { 5, {
BIP_RANGE(10),
BIP_RANGE(5),
BIP_RANGE(2.5),
BIP_RANGE(1.25),
BIP_RANGE(0.625)
}
};
static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
static const comedi_lrange range_pci1720 = { 4, {
UNI_RANGE(5),
UNI_RANGE(10),
BIP_RANGE(5),
BIP_RANGE(10)
}
};
static const comedi_lrange range_pci171x_da = { 2, {
UNI_RANGE(5),
UNI_RANGE(10),
}
};
static int pci1710_attach(comedi_device * dev, comedi_devconfig * it);
static int pci1710_detach(comedi_device * dev);
typedef struct {
const char *name; // board name
int device_id;
int iorange; // I/O range len
char have_irq; // 1=card support IRQ
char cardtype; // 0=1710& co. 2=1713, ...
int n_aichan; // num of A/D chans
int n_aichand; // num of A/D chans in diff mode
int n_aochan; // num of D/A chans
int n_dichan; // num of DI chans
int n_dochan; // num of DO chans
int n_counter; // num of counters
int ai_maxdata; // resolution of A/D
int ao_maxdata; // resolution of D/A
const comedi_lrange *rangelist_ai; // rangelist for A/D
const char *rangecode_ai; // range codes for programming
const comedi_lrange *rangelist_ao; // rangelist for D/A
unsigned int ai_ns_min; // max sample speed of card v ns
unsigned int fifo_half_size; // size of FIFO/2
} boardtype;
static DEFINE_PCI_DEVICE_TABLE(pci1710_pci_table) = {
{PCI_VENDOR_ID_ADVANTECH, 0x1710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ADVANTECH, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ADVANTECH, 0x1713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ADVANTECH, 0x1720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_ADVANTECH, 0x1731, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0}
};
MODULE_DEVICE_TABLE(pci, pci1710_pci_table);
static const boardtype boardtypes[] = {
{"pci1710", 0x1710,
IORANGE_171x, 1, TYPE_PCI171X,
16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
&range_pci1710_3, range_codes_pci1710_3,
&range_pci171x_da,
10000, 2048},
{"pci1710hg", 0x1710,
IORANGE_171x, 1, TYPE_PCI171X,
16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
&range_pci1710hg, range_codes_pci1710hg,
&range_pci171x_da,
10000, 2048},
{"pci1711", 0x1711,
IORANGE_171x, 1, TYPE_PCI171X,
16, 0, 2, 16, 16, 1, 0x0fff, 0x0fff,
&range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
10000, 512},
{"pci1713", 0x1713,
IORANGE_171x, 1, TYPE_PCI1713,
32, 16, 0, 0, 0, 0, 0x0fff, 0x0000,
&range_pci1710_3, range_codes_pci1710_3, NULL,
10000, 2048},
{"pci1720", 0x1720,
IORANGE_1720, 0, TYPE_PCI1720,
0, 0, 4, 0, 0, 0, 0x0000, 0x0fff,
NULL, NULL, &range_pci1720,
0, 0},
{"pci1731", 0x1731,
IORANGE_171x, 1, TYPE_PCI171X,
16, 0, 0, 16, 16, 0, 0x0fff, 0x0000,
&range_pci17x1, range_codes_pci17x1, NULL,
10000, 512},
// dummy entry corresponding to driver name
{.name = DRV_NAME},
};
#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
static comedi_driver driver_pci1710 = {
.driver_name = DRV_NAME,
.module = THIS_MODULE,
.attach = pci1710_attach,
.detach = pci1710_detach,
.num_names = n_boardtypes,
.board_name = &boardtypes[0].name,
.offset = sizeof(boardtype),
};
typedef struct {
struct pci_dev *pcidev; // ptr to PCI device
char valid; // card is usable
char neverending_ai; // we do unlimited AI
unsigned int CntrlReg; // Control register
unsigned int i8254_osc_base; // frequence of onboard oscilator
unsigned int ai_do; // what do AI? 0=nothing, 1 to 4 mode
unsigned int ai_act_scan; // how many scans we finished
unsigned int ai_act_chan; // actual position in actual scan
unsigned int ai_buf_ptr; // data buffer ptr in samples
unsigned char ai_eos; // 1=EOS wake up
unsigned char ai_et;
unsigned int ai_et_CntrlReg;
unsigned int ai_et_MuxVal;
unsigned int ai_et_div1, ai_et_div2;
unsigned int act_chanlist[32]; // list of scaned channel
unsigned char act_chanlist_len; // len of scanlist
unsigned char act_chanlist_pos; // actual position in MUX list
unsigned char da_ranges; // copy of D/A outpit range register
unsigned int ai_scans; // len of scanlist
unsigned int ai_n_chan; // how many channels is measured
unsigned int *ai_chanlist; // actaul chanlist
unsigned int ai_flags; // flaglist
unsigned int ai_data_len; // len of data buffer
sampl_t *ai_data; // data buffer
unsigned int ai_timer1; // timers
unsigned int ai_timer2;
sampl_t ao_data[4]; // data output buffer
unsigned int cnt0_write_wait; // after a write, wait for update of the internal state
} pci1710_private;
#define devpriv ((pci1710_private *)dev->private)
#define this_board ((const boardtype *)dev->board_ptr)
/*
==============================================================================
*/
static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
unsigned int *chanlist, unsigned int n_chan);
static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
unsigned int *chanlist, unsigned int n_chan, unsigned int seglen);
static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1,
unsigned int divisor2);
static int pci1710_reset(comedi_device * dev);
static int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s);
static const unsigned int muxonechan[] = { 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707, // used for gain list programming
0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
};
/*
==============================================================================
*/
static int pci171x_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int n, timeout;
#ifdef PCI171x_PARANOIDCHECK
unsigned int idata;
#endif
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
devpriv->CntrlReg &= Control_CNT0;
devpriv->CntrlReg |= Control_SW; // set software trigger
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
setup_channel_list(dev, s, &insn->chanspec, 1, 1);
DPRINTK("adv_pci1710 A ST=%4x IO=%x\n",
inw(dev->iobase + PCI171x_STATUS),
dev->iobase + PCI171x_STATUS);
for (n = 0; n < insn->n; n++) {
outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
DPRINTK("adv_pci1710 B n=%d ST=%4x\n", n,
inw(dev->iobase + PCI171x_STATUS));
//comedi_udelay(1);
DPRINTK("adv_pci1710 C n=%d ST=%4x\n", n,
inw(dev->iobase + PCI171x_STATUS));
timeout = 100;
while (timeout--) {
if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
goto conv_finish;
if (!(timeout % 10))
DPRINTK("adv_pci1710 D n=%d tm=%d ST=%4x\n", n,
timeout,
inw(dev->iobase + PCI171x_STATUS));
}
comedi_error(dev, "A/D insn timeout");
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
data[n] = 0;
DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
return -ETIME;
conv_finish:
#ifdef PCI171x_PARANOIDCHECK
idata = inw(dev->iobase + PCI171x_AD_DATA);
if (this_board->cardtype != TYPE_PCI1713)
if ((idata & 0xf000) != devpriv->act_chanlist[0]) {
comedi_error(dev, "A/D insn data droput!");
return -ETIME;
}
data[n] = idata & 0x0fff;
#else
data[n] = inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff;
#endif
}
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
return n;
}
/*
==============================================================================
*/
static int pci171x_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int n, chan, range, ofs;
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
if (chan) {
devpriv->da_ranges &= 0xfb;
devpriv->da_ranges |= (range << 2);
outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
ofs = PCI171x_DA2;
} else {
devpriv->da_ranges &= 0xfe;
devpriv->da_ranges |= range;
outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
ofs = PCI171x_DA1;
}
for (n = 0; n < insn->n; n++)
outw(data[n], dev->iobase + ofs);
devpriv->ao_data[chan] = data[n];
return n;
}
/*
==============================================================================
*/
static int pci171x_insn_read_ao(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int n, chan;
chan = CR_CHAN(insn->chanspec);
for (n = 0; n < insn->n; n++)
data[n] = devpriv->ao_data[chan];
return n;
}
/*
==============================================================================
*/
static int pci171x_insn_bits_di(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
data[1] = inw(dev->iobase + PCI171x_DI);
return 2;
}
/*
==============================================================================
*/
static int pci171x_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
if (data[0]) {
s->state &= ~data[0];
s->state |= (data[0] & data[1]);
outw(s->state, dev->iobase + PCI171x_DO);
}
data[1] = s->state;
return 2;
}
/*
==============================================================================
*/
static int pci171x_insn_counter_read(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
unsigned int msb, lsb, ccntrl;
int i;
ccntrl = 0xD2; /* count only */
for (i = 0; i < insn->n; i++) {
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
data[0] = lsb | (msb << 8);
}
return insn->n;
}
/*
==============================================================================
*/
static int pci171x_insn_counter_write(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
uint msb, lsb, ccntrl, status;
lsb = data[0] & 0x00FF;
msb = (data[0] & 0xFF00) >> 8;
/* write lsb, then msb */
outw(lsb, dev->iobase + PCI171x_CNT0);
outw(msb, dev->iobase + PCI171x_CNT0);
if (devpriv->cnt0_write_wait) {
/* wait for the new count to be loaded */
ccntrl = 0xE2;
do {
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
} while (status & 0x40);
}
return insn->n;
}
/*
==============================================================================
*/
static int pci171x_insn_counter_config(comedi_device * dev,
comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
{
#ifdef unused
/* This doesn't work like a normal Comedi counter config */
uint ccntrl = 0;
devpriv->cnt0_write_wait = data[0] & 0x20;
/* internal or external clock? */
if (!(data[0] & 0x10)) { /* internal */
devpriv->CntrlReg &= ~Control_CNT0;
} else {
devpriv->CntrlReg |= Control_CNT0;
}
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
if (data[0] & 0x01)
ccntrl |= Counter_M0;
if (data[0] & 0x02)
ccntrl |= Counter_M1;
if (data[0] & 0x04)
ccntrl |= Counter_M2;
if (data[0] & 0x08)
ccntrl |= Counter_BCD;
ccntrl |= Counter_RW0; /* set read/write mode */
ccntrl |= Counter_RW1;
outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
#endif
return 1;
}
/*
==============================================================================
*/
static int pci1720_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int n, rangereg, chan;
chan = CR_CHAN(insn->chanspec);
rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
if (rangereg != devpriv->da_ranges) {
outb(rangereg, dev->iobase + PCI1720_RANGE);
devpriv->da_ranges = rangereg;
}
for (n = 0; n < insn->n; n++) {
outw(data[n], dev->iobase + PCI1720_DA0 + (chan << 1));
outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
}
devpriv->ao_data[chan] = data[n];
return n;
}
/*
==============================================================================
*/
static void interrupt_pci1710_every_sample(void *d)
{
comedi_device *dev = d;
comedi_subdevice *s = dev->subdevices + 0;
int m;
#ifdef PCI171x_PARANOIDCHECK
sampl_t sampl;
#endif
DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
m = inw(dev->iobase + PCI171x_STATUS);
if (m & Status_FE) {
rt_printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return;
}
if (m & Status_FF) {
rt_printk
("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
dev->minor, m);
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return;
}
outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
DPRINTK("FOR ");
for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
#ifdef PCI171x_PARANOIDCHECK
sampl = inw(dev->iobase + PCI171x_AD_DATA);
DPRINTK("%04x:", sampl);
if (this_board->cardtype != TYPE_PCI1713)
if ((sampl & 0xf000) !=
devpriv->act_chanlist[s->async->cur_chan]) {
rt_printk
("comedi: A/D data dropout: received data from channel %d, expected %d!\n",
(sampl & 0xf000) >> 12,
(devpriv->act_chanlist[s->async->
cur_chan] & 0xf000) >>
12);
pci171x_ai_cancel(dev, s);
s->async->events |=
COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return;
}
DPRINTK("%8d %2d %8d~", s->async->buf_int_ptr,
s->async->cur_chan, s->async->buf_int_count);
comedi_buf_put(s->async, sampl & 0x0fff);
#else
comedi_buf_put(s->async,
inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
#endif
++s->async->cur_chan;
if (s->async->cur_chan >= devpriv->ai_n_chan) {
s->async->cur_chan = 0;
}
if (s->async->cur_chan == 0) { // one scan done
devpriv->ai_act_scan++;
DPRINTK("adv_pci1710 EDBG: EOS1 bic %d bip %d buc %d bup %d\n", s->async->buf_int_count, s->async->buf_int_ptr, s->async->buf_user_count, s->async->buf_user_ptr);
DPRINTK("adv_pci1710 EDBG: EOS2\n");
if ((!devpriv->neverending_ai) && (devpriv->ai_act_scan >= devpriv->ai_scans)) { // all data sampled
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA;
comedi_event(dev, s);
return;
}
}
}
outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
comedi_event(dev, s);
}
/*
==============================================================================
*/
static int move_block_from_fifo(comedi_device * dev, comedi_subdevice * s,
int n, int turn)
{
int i, j;
#ifdef PCI171x_PARANOIDCHECK
int sampl;
#endif
DPRINTK("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n", n,
turn);
j = s->async->cur_chan;
for (i = 0; i < n; i++) {
#ifdef PCI171x_PARANOIDCHECK
sampl = inw(dev->iobase + PCI171x_AD_DATA);
if (this_board->cardtype != TYPE_PCI1713)
if ((sampl & 0xf000) != devpriv->act_chanlist[j]) {
rt_printk
("comedi%d: A/D FIFO data dropout: received data from channel %d, expected %d! (%d/%d/%d/%d/%d/%4x)\n",
dev->minor, (sampl & 0xf000) >> 12,
(devpriv->
act_chanlist[j] & 0xf000) >> 12,
i, j, devpriv->ai_act_scan, n, turn,
sampl);
pci171x_ai_cancel(dev, s);
s->async->events |=
COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return 1;
}
comedi_buf_put(s->async, sampl & 0x0fff);
#else
comedi_buf_put(s->async,
inw(dev->iobase + PCI171x_AD_DATA) & 0x0fff);
#endif
j++;
if (j >= devpriv->ai_n_chan) {
j = 0;
devpriv->ai_act_scan++;
}
}
DPRINTK("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
return 0;
}
/*
==============================================================================
*/
static void interrupt_pci1710_half_fifo(void *d)
{
comedi_device *dev = d;
comedi_subdevice *s = dev->subdevices + 0;
int m, samplesinbuf;
DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
m = inw(dev->iobase + PCI171x_STATUS);
if (!(m & Status_FH)) {
rt_printk("comedi%d: A/D FIFO not half full! (%4x)\n",
dev->minor, m);
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return;
}
if (m & Status_FF) {
rt_printk
("comedi%d: A/D FIFO Full status (Fatal Error!) (%4x)\n",
dev->minor, m);
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
comedi_event(dev, s);
return;
}
samplesinbuf = this_board->fifo_half_size;
if (samplesinbuf * sizeof(sampl_t) >= devpriv->ai_data_len) {
m = devpriv->ai_data_len / sizeof(sampl_t);
if (move_block_from_fifo(dev, s, m, 0))
return;
samplesinbuf -= m;
}
if (samplesinbuf) {
if (move_block_from_fifo(dev, s, samplesinbuf, 1))
return;
}
if (!devpriv->neverending_ai)
if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
pci171x_ai_cancel(dev, s);
s->async->events |= COMEDI_CB_EOA;
comedi_event(dev, s);
return;
}
outb(0, dev->iobase + PCI171x_CLRINT); // clear our INT request
DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
comedi_event(dev, s);
}
/*
==============================================================================
*/
static irqreturn_t interrupt_service_pci1710(int irq, void *d PT_REGS_ARG)
{
comedi_device *dev = d;
DPRINTK("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",
irq);
if (!dev->attached) // is device attached?
return IRQ_NONE; // no, exit
if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ)) // is this interrupt from our board?
return IRQ_NONE; // no, exit
DPRINTK("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",
inw(dev->iobase + PCI171x_STATUS));
if (devpriv->ai_et) { // Switch from initial TRIG_EXT to TRIG_xxx.
devpriv->ai_et = 0;
devpriv->CntrlReg &= Control_CNT0;
devpriv->CntrlReg |= Control_SW; // set software trigger
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
// start pacer
start_pacer(dev, 1, devpriv->ai_et_div1, devpriv->ai_et_div2);
return IRQ_HANDLED;
}
if (devpriv->ai_eos) { // We use FIFO half full INT or not?
interrupt_pci1710_every_sample(d);
} else {
interrupt_pci1710_half_fifo(d);
}
DPRINTK("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
return IRQ_HANDLED;
}
/*
==============================================================================
*/
static int pci171x_ai_docmd_and_mode(int mode, comedi_device * dev,
comedi_subdevice * s)
{
unsigned int divisor1, divisor2;
unsigned int seglen;
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",
mode);
start_pacer(dev, -1, 0, 0); // stop pacer
seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
devpriv->ai_n_chan);
if (seglen < 1)
return -EINVAL;
setup_channel_list(dev, s, devpriv->ai_chanlist,
devpriv->ai_n_chan, seglen);
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
devpriv->ai_do = mode;
devpriv->ai_act_scan = 0;
s->async->cur_chan = 0;
devpriv->ai_buf_ptr = 0;
devpriv->neverending_ai = 0;
devpriv->CntrlReg &= Control_CNT0;
if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan? devpriv->ai_eos=1;
devpriv->ai_eos = 1;
} else {
devpriv->CntrlReg |= Control_ONEFH;
devpriv->ai_eos = 0;
}
if ((devpriv->ai_scans == 0) || (devpriv->ai_scans == -1)) {
devpriv->neverending_ai = 1;
} //well, user want neverending
else {
devpriv->neverending_ai = 0;
}
switch (mode) {
case 1:
case 2:
if (devpriv->ai_timer1 < this_board->ai_ns_min)
devpriv->ai_timer1 = this_board->ai_ns_min;
devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
if (mode == 2) {
devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
devpriv->CntrlReg &=
~(Control_PACER | Control_ONEFH | Control_GATE);
devpriv->CntrlReg |= Control_EXT;
devpriv->ai_et = 1;
} else {
devpriv->ai_et = 0;
}
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
&divisor2, &devpriv->ai_timer1,
devpriv->ai_flags & TRIG_ROUND_MASK);
DPRINTK("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n", devpriv->i8254_osc_base, divisor1, divisor2, devpriv->ai_timer1);
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
if (mode != 2) {
// start pacer
start_pacer(dev, mode, divisor1, divisor2);
} else {
devpriv->ai_et_div1 = divisor1;
devpriv->ai_et_div2 = divisor2;
}
break;
case 3:
devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
break;
}
DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
return 0;
}
#ifdef PCI171X_EXTDEBUG
/*
==============================================================================
*/
static void pci171x_cmdtest_out(int e, comedi_cmd * cmd)
{
rt_printk("adv_pci1710 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
rt_printk("adv_pci1710 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
rt_printk("adv_pci1710 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
cmd->scan_end_src);
rt_printk("adv_pci1710 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
}
#endif
/*
==============================================================================
*/
static int pci171x_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
comedi_cmd * cmd)
{
int err = 0;
int tmp, divisor1, divisor2;
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
#ifdef PCI171X_EXTDEBUG
pci171x_cmdtest_out(-1, cmd);
#endif
/* step 1: make sure trigger sources are trivially valid */
tmp = cmd->start_src;
cmd->start_src &= TRIG_NOW | TRIG_EXT;
if (!cmd->start_src || tmp != cmd->start_src)
err++;
tmp = cmd->scan_begin_src;
cmd->scan_begin_src &= TRIG_FOLLOW;
if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
err++;
tmp = cmd->convert_src;
cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
if (!cmd->convert_src || tmp != cmd->convert_src)
err++;
tmp = cmd->scan_end_src;
cmd->scan_end_src &= TRIG_COUNT;
if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
err++;
tmp = cmd->stop_src;
cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
if (!cmd->stop_src || tmp != cmd->stop_src)
err++;
if (err) {
#ifdef PCI171X_EXTDEBUG
pci171x_cmdtest_out(1, cmd);
#endif
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n", err);
return 1;
}
/* step 2: make sure trigger sources are unique and mutually compatible */
if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
cmd->start_src = TRIG_NOW;
err++;
}
if (cmd->scan_begin_src != TRIG_FOLLOW) {
cmd->scan_begin_src = TRIG_FOLLOW;
err++;
}
if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
err++;
if (cmd->scan_end_src != TRIG_COUNT) {
cmd->scan_end_src = TRIG_COUNT;
err++;
}
if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
err++;
if (err) {
#ifdef PCI171X_EXTDEBUG
pci171x_cmdtest_out(2, cmd);
#endif
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n", err);
return 2;
}
/* step 3: make sure arguments are trivially compatible */
if (cmd->start_arg != 0) {
cmd->start_arg = 0;
err++;
}
if (cmd->scan_begin_arg != 0) {
cmd->scan_begin_arg = 0;
err++;
}
if (cmd->convert_src == TRIG_TIMER) {
if (cmd->convert_arg < this_board->ai_ns_min) {
cmd->convert_arg = this_board->ai_ns_min;
err++;
}
} else { /* TRIG_FOLLOW */
if (cmd->convert_arg != 0) {
cmd->convert_arg = 0;
err++;
}
}
if (!cmd->chanlist_len) {
cmd->chanlist_len = 1;
err++;
}
if (cmd->chanlist_len > this_board->n_aichan) {
cmd->chanlist_len = this_board->n_aichan;
err++;
}
if (cmd->scan_end_arg != cmd->chanlist_len) {
cmd->scan_end_arg = cmd->chanlist_len;
err++;
}
if (cmd->stop_src == TRIG_COUNT) {
if (!cmd->stop_arg) {
cmd->stop_arg = 1;
err++;
}
} else { /* TRIG_NONE */
if (cmd->stop_arg != 0) {
cmd->stop_arg = 0;
err++;
}
}
if (err) {
#ifdef PCI171X_EXTDEBUG
pci171x_cmdtest_out(3, cmd);
#endif
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n", err);
return 3;
}
/* step 4: fix up any arguments */
if (cmd->convert_src == TRIG_TIMER) {
tmp = cmd->convert_arg;
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
&divisor2, &cmd->convert_arg,
cmd->flags & TRIG_ROUND_MASK);
if (cmd->convert_arg < this_board->ai_ns_min)
cmd->convert_arg = this_board->ai_ns_min;
if (tmp != cmd->convert_arg)
err++;
}
if (err) {
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n", err);
return 4;
}
/* step 5: complain about special chanlist considerations */
if (cmd->chanlist) {
if (!check_channel_list(dev, s, cmd->chanlist,
cmd->chanlist_len))
return 5; // incorrect channels list
}
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
return 0;
}
/*
==============================================================================
*/
static int pci171x_ai_cmd(comedi_device * dev, comedi_subdevice * s)
{
comedi_cmd *cmd = &s->async->cmd;
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
devpriv->ai_n_chan = cmd->chanlist_len;
devpriv->ai_chanlist = cmd->chanlist;
devpriv->ai_flags = cmd->flags;
devpriv->ai_data_len = s->async->prealloc_bufsz;
devpriv->ai_data = s->async->prealloc_buf;
devpriv->ai_timer1 = 0;
devpriv->ai_timer2 = 0;
if (cmd->stop_src == TRIG_COUNT) {
devpriv->ai_scans = cmd->stop_arg;
} else {
devpriv->ai_scans = 0;
}
if (cmd->scan_begin_src == TRIG_FOLLOW) { // mode 1, 2, 3
if (cmd->convert_src == TRIG_TIMER) { // mode 1 and 2
devpriv->ai_timer1 = cmd->convert_arg;
return pci171x_ai_docmd_and_mode(cmd->start_src ==
TRIG_EXT ? 2 : 1, dev, s);
}
if (cmd->convert_src == TRIG_EXT) { // mode 3
return pci171x_ai_docmd_and_mode(3, dev, s);
}
}
return -1;
}
/*
==============================================================================
Check if channel list from user is builded correctly
If it's ok, then program scan/gain logic.
This works for all cards.
*/
static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
unsigned int *chanlist, unsigned int n_chan)
{
unsigned int chansegment[32];
unsigned int i, nowmustbechan, seglen, segpos;
DPRINTK("adv_pci1710 EDBG: check_channel_list(...,%d)\n", n_chan);
/* correct channel and range number check itself comedi/range.c */
if (n_chan < 1) {
comedi_error(dev, "range/channel list is empty!");
return 0;
}
if (n_chan > 1) {
chansegment[0] = chanlist[0]; // first channel is everytime ok
for (i = 1, seglen = 1; i < n_chan; i++, seglen++) { // build part of chanlist
// rt_printk("%d. %d %d\n",i,CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
if (chanlist[0] == chanlist[i])
break; // we detect loop, this must by finish
if (CR_CHAN(chanlist[i]) & 1) // odd channel cann't by differencial
if (CR_AREF(chanlist[i]) == AREF_DIFF) {
comedi_error(dev,
"Odd channel can't be differential input!\n");
return 0;
}
nowmustbechan =
(CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
nowmustbechan = (nowmustbechan + 1) % s->n_chan;
if (nowmustbechan != CR_CHAN(chanlist[i])) { // channel list isn't continous :-(
rt_printk
("channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
i, CR_CHAN(chanlist[i]), nowmustbechan,
CR_CHAN(chanlist[0]));
return 0;
}
chansegment[i] = chanlist[i]; // well, this is next correct channel in list
}
for (i = 0, segpos = 0; i < n_chan; i++) { // check whole chanlist
//rt_printk("%d %d=%d %d\n",CR_CHAN(chansegment[i%seglen]),CR_RANGE(chansegment[i%seglen]),CR_CHAN(chanlist[i]),CR_RANGE(chanlist[i]));
if (chanlist[i] != chansegment[i % seglen]) {
rt_printk
("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
i, CR_CHAN(chansegment[i]),
CR_RANGE(chansegment[i]),
CR_AREF(chansegment[i]),
CR_CHAN(chanlist[i % seglen]),
CR_RANGE(chanlist[i % seglen]),
CR_AREF(chansegment[i % seglen]));
return 0; // chan/gain list is strange
}
}
} else {
seglen = 1;
}
return seglen;
}
static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
unsigned int *chanlist, unsigned int n_chan, unsigned int seglen)
{
unsigned int i, range, chanprog;
DPRINTK("adv_pci1710 EDBG: setup_channel_list(...,%d,%d)\n", n_chan,
seglen);
devpriv->act_chanlist_len = seglen;
devpriv->act_chanlist_pos = 0;
DPRINTK("SegLen: %d\n", seglen);
for (i = 0; i < seglen; i++) { // store range list to card
chanprog = muxonechan[CR_CHAN(chanlist[i])];
outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
if (CR_AREF(chanlist[i]) == AREF_DIFF)
range |= 0x0020;
outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
#ifdef PCI171x_PARANOIDCHECK
devpriv->act_chanlist[i] =
(CR_CHAN(chanlist[i]) << 12) & 0xf000;
#endif
DPRINTK("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
devpriv->act_chanlist[i]);
}
devpriv->ai_et_MuxVal =
CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX); /* select channel interval to scan */
DPRINTK("MUX: %4x L%4x.H%4x\n",
CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8),
CR_CHAN(chanlist[0]), CR_CHAN(chanlist[seglen - 1]));
}
/*
==============================================================================
*/
static void start_pacer(comedi_device * dev, int mode, unsigned int divisor1,
unsigned int divisor2)
{
DPRINTK("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode,
divisor1, divisor2);
outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
outw(0x74, dev->iobase + PCI171x_CNTCTRL);
if (mode == 1) {
outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
}
DPRINTK("adv_pci1710 EDBG: END: start_pacer(...)\n");
}
/*
==============================================================================
*/
static int pci171x_ai_cancel(comedi_device * dev, comedi_subdevice * s)
{
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
switch (this_board->cardtype) {
default:
devpriv->CntrlReg &= Control_CNT0;
devpriv->CntrlReg |= Control_SW;
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
start_pacer(dev, -1, 0, 0);
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
break;
}
devpriv->ai_do = 0;
devpriv->ai_act_scan = 0;
s->async->cur_chan = 0;
devpriv->ai_buf_ptr = 0;
devpriv->neverending_ai = 0;
DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
return 0;
}
/*
==============================================================================
*/
static int pci171x_reset(comedi_device * dev)
{
DPRINTK("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
outw(0x30, dev->iobase + PCI171x_CNTCTRL);
devpriv->CntrlReg = Control_SW | Control_CNT0; // Software trigger, CNT0=external
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); // reset any operations
outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
outb(0, dev->iobase + PCI171x_CLRINT); // clear INT request
start_pacer(dev, -1, 0, 0); // stop 8254
devpriv->da_ranges = 0;
if (this_board->n_aochan) {
outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF); // set DACs to 0..5V
outw(0, dev->iobase + PCI171x_DA1); // set DA outputs to 0V
devpriv->ao_data[0] = 0x0000;
if (this_board->n_aochan > 1) {
outw(0, dev->iobase + PCI171x_DA2);
devpriv->ao_data[1] = 0x0000;
}
}
outw(0, dev->iobase + PCI171x_DO); // digital outputs to 0
outb(0, dev->iobase + PCI171x_CLRFIFO); // clear FIFO
outb(0, dev->iobase + PCI171x_CLRINT); // clear INT request
DPRINTK("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
return 0;
}
/*
==============================================================================
*/
static int pci1720_reset(comedi_device * dev)
{
DPRINTK("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); // set synchronous output mode
devpriv->da_ranges = 0xAA;
outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); // set all ranges to +/-5V
outw(0x0800, dev->iobase + PCI1720_DA0); // set outputs to 0V
outw(0x0800, dev->iobase + PCI1720_DA1);
outw(0x0800, dev->iobase + PCI1720_DA2);
outw(0x0800, dev->iobase + PCI1720_DA3);
outb(0, dev->iobase + PCI1720_SYNCOUT); // update outputs
devpriv->ao_data[0] = 0x0800;
devpriv->ao_data[1] = 0x0800;
devpriv->ao_data[2] = 0x0800;
devpriv->ao_data[3] = 0x0800;
DPRINTK("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
return 0;
}
/*
==============================================================================
*/
static int pci1710_reset(comedi_device * dev)
{
DPRINTK("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
switch (this_board->cardtype) {
case TYPE_PCI1720:
return pci1720_reset(dev);
default:
return pci171x_reset(dev);
}
DPRINTK("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
}
/*
==============================================================================
*/
static int pci1710_attach(comedi_device * dev, comedi_devconfig * it)
{
comedi_subdevice *s;
int ret, subdev, n_subdevices;
unsigned int irq;
unsigned long iobase;
struct pci_dev *pcidev;
int opt_bus, opt_slot;
const char *errstr;
unsigned char pci_bus, pci_slot, pci_func;
int i;
int board_index;
rt_printk("comedi%d: adv_pci1710: ", dev->minor);
opt_bus = it->options[0];
opt_slot = it->options[1];
if ((ret = alloc_private(dev, sizeof(pci1710_private))) < 0) {
rt_printk(" - Allocation failed!\n");
return -ENOMEM;
}
/* Look for matching PCI device */
errstr = "not found!";
pcidev = NULL;
board_index = this_board - boardtypes;
while (NULL != (pcidev = pci_get_device(PCI_VENDOR_ID_ADVANTECH,
PCI_ANY_ID, pcidev))) {
if(strcmp(this_board->name, DRV_NAME) == 0)
{
for(i = 0; i < n_boardtypes; ++i)
{
if(pcidev->device == boardtypes[i].device_id)
{
board_index = i;
break;
}
}
if(i == n_boardtypes) continue;
}else
{
if(pcidev->device != boardtypes[board_index].device_id) continue;
}
/* Found matching vendor/device. */
if (opt_bus || opt_slot) {
/* Check bus/slot. */
if (opt_bus != pcidev->bus->number
|| opt_slot != PCI_SLOT(pcidev->devfn))
continue; /* no match */
}
/*
* Look for device that isn't in use.
* Enable PCI device and request regions.
*/
if (comedi_pci_enable(pcidev, DRV_NAME)) {
errstr = "failed to enable PCI device and request regions!";
continue;
}
// fixup board_ptr in case we were using the dummy entry with the driver name
dev->board_ptr = &boardtypes[board_index];
break;
}
if (!pcidev) {
if (opt_bus || opt_slot) {
rt_printk(" - Card at b:s %d:%d %s\n",
opt_bus, opt_slot, errstr);
} else {
rt_printk(" - Card %s\n", errstr);
}
return -EIO;
}
pci_bus = pcidev->bus->number;
pci_slot = PCI_SLOT(pcidev->devfn);
pci_func = PCI_FUNC(pcidev->devfn);
irq = pcidev->irq;
iobase = pci_resource_start(pcidev, 2);
rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx", pci_bus, pci_slot, pci_func,
iobase);
dev->iobase = iobase;
dev->board_name = this_board->name;
devpriv->pcidev = pcidev;
n_subdevices = 0;
if (this_board->n_aichan)
n_subdevices++;
if (this_board->n_aochan)
n_subdevices++;
if (this_board->n_dichan)
n_subdevices++;
if (this_board->n_dochan)
n_subdevices++;
if (this_board->n_counter)
n_subdevices++;
if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
rt_printk(" - Allocation failed!\n");
return ret;
}
pci1710_reset(dev);
if (this_board->have_irq) {
if (irq) {
if (comedi_request_irq(irq, interrupt_service_pci1710,
IRQF_SHARED, "Advantech PCI-1710",
dev)) {
rt_printk
(", unable to allocate IRQ %d, DISABLING IT",
irq);
irq = 0; /* Can't use IRQ */
} else {
rt_printk(", irq=%u", irq);
}
} else {
rt_printk(", IRQ disabled");
}
} else {
irq = 0;
}
dev->irq = irq;
printk(".\n");
subdev = 0;
if (this_board->n_aichan) {
s = dev->subdevices + subdev;
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
if (this_board->n_aichand)
s->subdev_flags |= SDF_DIFF;
s->n_chan = this_board->n_aichan;
s->maxdata = this_board->ai_maxdata;
s->len_chanlist = this_board->n_aichan;
s->range_table = this_board->rangelist_ai;
s->cancel = pci171x_ai_cancel;
s->insn_read = pci171x_insn_read_ai;
if (irq) {
s->subdev_flags |= SDF_CMD_READ;
s->do_cmdtest = pci171x_ai_cmdtest;
s->do_cmd = pci171x_ai_cmd;
}
devpriv->i8254_osc_base = 100; // 100ns=10MHz
subdev++;
}
if (this_board->n_aochan) {
s = dev->subdevices + subdev;
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_aochan;
s->maxdata = this_board->ao_maxdata;
s->len_chanlist = this_board->n_aochan;
s->range_table = this_board->rangelist_ao;
switch (this_board->cardtype) {
case TYPE_PCI1720:
s->insn_write = pci1720_insn_write_ao;
break;
default:
s->insn_write = pci171x_insn_write_ao;
break;
}
s->insn_read = pci171x_insn_read_ao;
subdev++;
}
if (this_board->n_dichan) {
s = dev->subdevices + subdev;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_dichan;
s->maxdata = 1;
s->len_chanlist = this_board->n_dichan;
s->range_table = &range_digital;
s->io_bits = 0; /* all bits input */
s->insn_bits = pci171x_insn_bits_di;
subdev++;
}
if (this_board->n_dochan) {
s = dev->subdevices + subdev;
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_dochan;
s->maxdata = 1;
s->len_chanlist = this_board->n_dochan;
s->range_table = &range_digital;
s->io_bits = (1 << this_board->n_dochan) - 1; /* all bits output */
s->state = 0;
s->insn_bits = pci171x_insn_bits_do;
subdev++;
}
if (this_board->n_counter) {
s = dev->subdevices + subdev;
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = this_board->n_counter;
s->len_chanlist = this_board->n_counter;
s->maxdata = 0xffff;
s->range_table = &range_unknown;
s->insn_read = pci171x_insn_counter_read;
s->insn_write = pci171x_insn_counter_write;
s->insn_config = pci171x_insn_counter_config;
subdev++;
}
devpriv->valid = 1;
return 0;
}
/*
==============================================================================
*/
static int pci1710_detach(comedi_device * dev)
{
if (dev->private) {
if (devpriv->valid)
pci1710_reset(dev);
if (dev->irq)
comedi_free_irq(dev->irq, dev);
if (devpriv->pcidev) {
if (dev->iobase) {
comedi_pci_disable(devpriv->pcidev);
}
pci_dev_put(devpriv->pcidev);
}
}
return 0;
}
/*
==============================================================================
*/
COMEDI_PCI_INITCLEANUP(driver_pci1710, pci1710_pci_table);
/*
==============================================================================
*/
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