From: David Schleef Date: Mon, 23 Jul 2001 18:47:38 +0000 (+0000) Subject: Update from Michal Dobes X-Git-Tag: r0_7_60~22 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=33cb30f334d1db353c0020f1625e4240dff9dabc;p=comedi.git Update from Michal Dobes --- diff --git a/comedi/Config.in b/comedi/Config.in index 5d3134a7..cff40025 100644 --- a/comedi/Config.in +++ b/comedi/Config.in @@ -81,7 +81,7 @@ dep_tristate 'PCL-711, PCL-711b, ACL-8112, and compatibles' CONFIG_COMEDI_PCL711 dep_tristate 'PCL-722/724/731, ACL-7122/7124, PET-48DIO' CONFIG_COMEDI_PCL724 $CONFIG_COMEDI dep_tristate 'PCL-725' CONFIG_COMEDI_PCL725 $CONFIG_COMEDI dep_tristate 'PCL-726' CONFIG_COMEDI_PCL726 $CONFIG_COMEDI -dep_tristate 'Advantech PCL-812PG, PCL-813B' CONFIG_COMEDI_PCL812 $CONFIG_COMEDI +dep_tristate 'Advantech PCL-812/PG, PCL-813/B and similar' CONFIG_COMEDI_PCL812 $CONFIG_COMEDI dep_tristate 'Advantech PCL-818/L/H/HD/HG, PCL-718' CONFIG_COMEDI_PCL818 $CONFIG_COMEDI dep_tristate 'Real-Time Devices PCI4520/DM7520' CONFIG_COMEDI_RTD520 $CONFIG_COMEDI dep_tristate 'Analog Devices RTI-800/815' CONFIG_COMEDI_RTI800 $CONFIG_COMEDI diff --git a/comedi/drivers/pcl812.c b/comedi/drivers/pcl812.c index 069d2863..19a3ba6f 100644 --- a/comedi/drivers/pcl812.c +++ b/comedi/drivers/pcl812.c @@ -1,30 +1,88 @@ -// *INDENT-OFF* /* - * module/pcl812.c + * comedi/drivers/pcl812.c + * + * Author: Michal Dobes + * * hardware driver for Advantech cards - * card: PCL-812PG, PCL-813B - * driver: pcl812pg, pcl813b - * - * Michal Dobes - * Based on 711.c - * - * Options for PCL-812PG: + * card: PCL-812, PCL-812PG, PCL-813, PCL-813B + * driver: pcl812, pcl812pg, pcl813, pcl813b + * and for ADlink cards + * card: ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216 + * driver: acl8112dg, acl8112hg, acl8112pg, acl8113, acl8216 + * and for ICP DAS cards + * card: ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL, + * driver: iso813, a821pgh, a-821pgl, a-821pglnda, a822pgh, a822pgl, + * card: A-823PGH, A-823PGL, A-826PG + * driver: a823pgh, a823pgl, a826pg + * + * Options for PCL-812: * [0] - IO Base * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) - * [2] - 0=trigger source is internal 8253 with 2MHz clock + * [2] - DMA (0=disable, 1, 3) + * [3] - 0=trigger source is internal 8253 with 2MHz clock * 1=trigger source is external - * [3] - 0=A/D have max +/-5V input + * [4] - 0=A/D input range is +/-10V + * 1=A/D input range is +/-5V + * 2=A/D input range is +/-2.5V + * 3=A/D input range is +/-1.25V + * 4=A/D input range is +/-0.625V + * 5=A/D input range is +/-0.3125V + * [5] - 0=D/A outputs 0-5V (internal reference -5V) + * 1=D/A outputs 0-10V (internal reference -10V) + * 2=D/A outputs unknow (external reference) + * + * Options for PCL-812PG, ACL-8112PG: + * [0] - IO Base + * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) + * [2] - DMA (0=disable, 1, 3) + * [3] - 0=trigger source is internal 8253 with 2MHz clock + * 1=trigger source is external + * [4] - 0=A/D have max +/-5V input * 1=A/D have max +/-10V input - * [4] - 0=D/A outputs 0-5V (internal reference -5V) + * [5] - 0=D/A outputs 0-5V (internal reference -5V) + * 1=D/A outputs 0-10V (internal reference -10V) + * 2=D/A outputs unknow (external reference) + * + * Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG: + * [0] - IO Base + * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) + * [2] - DMA (0=disable, 1, 3) + * [3] - 0=trigger source is internal 8253 with 2MHz clock + * 1=trigger source is external + * [4] - 0=A/D channels are S.E. + * 1=A/D channels are DIFF + * [5] - 0=D/A outputs 0-5V (internal reference -5V) * 1=D/A outputs 0-10V (internal reference -10V) * 2=D/A outputs unknow (external reference) * + * Options for A-821PGL/PGH: + * [0] - IO Base + * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) + * [2] - 0=A/D channels are S.E. + * 1=A/D channels are DIFF + * [3] - 0=D/A output 0-5V (internal reference -5V) + * 1=D/A output 0-10V (internal reference -10V) + * + * Options for A-821PGL-NDA: + * [0] - IO Base + * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) + * [2] - 0=A/D channels are S.E. + * 1=A/D channels are DIFF + * + * Options for PCL-813: + * [0] - IO Base + * * Options for PCL-813B: * [0] - IO Base * [1] - 0= bipolar inputs * 1= unipolar inputs - * [2] - max number of samples in ai_mode0 (defaul=1scan) - * + * + * Options for ACL-8113, ISO-813: + * [0] - IO Base + * [1] - 0= 10V bipolar inputs + * 1= 10V unipolar inputs + * 2= 20V bipolar inputs + * 3= 20V unipolar inputs * */ @@ -44,51 +102,51 @@ #include #include "8253.h" -/* #define MD_DEBUG */ -#define boardPCL812PG 0 -#define boardPCL813B 1 +#undef PCL812_EXTDEBUG /* if this is defined then a lot of messages is printed */ -#define PCLx1x_RANGE 16 -#define PCL812_CLRINT 8 -#define PCL812_GAIN 9 -#define PCL812_MUX 10 -#define PCL812_MODE 11 -#define PCL812_CNTENABLE 10 -#define PCL812_SOFTTRIG 12 -#define PCL812_CTR0 0 -#define PCL812_CTR1 1 -#define PCL812_CTR2 2 -#define PCL812_CTRCTL 3 +// hardware types of the cards +#define boardPCL812PG 0 /* and ACL-8112PG */ +#define boardPCL813B 1 +#define boardPCL812 2 +#define boardPCL813 3 +#define boardISO813 5 +#define boardACL8113 6 +#define boardACL8112 7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */ +#define boardACL8216 8 /* and ICP DAS A-826PG */ +#define boardA821 9 /* PGH, PGL, PGL/NDA versions */ -#define PCL812_AD_LO 4 -#define PCL812_AD_HI 5 -#define PCL812_DA1_LO 4 -#define PCL812_DA1_HI 5 -#define PCL812_DA2_LO 6 -#define PCL812_DA2_HI 7 -#define PCL812_DI_LO 6 -#define PCL812_DI_HI 7 -#define PCL812_DO_LO 13 -#define PCL812_DO_HI 14 +#define PCLx1x_IORANGE 16 -#define PCL812_DRDY 0x10 +#define PCL812_CTR0 0 +#define PCL812_CTR1 1 +#define PCL812_CTR2 2 +#define PCL812_CTRCTL 3 +#define PCL812_AD_LO 4 +#define PCL812_DA1_LO 4 +#define PCL812_AD_HI 5 +#define PCL812_DA1_HI 5 +#define PCL812_DA2_LO 6 +#define PCL812_DI_LO 6 +#define PCL812_DA2_HI 7 +#define PCL812_DI_HI 7 +#define PCL812_CLRINT 8 +#define PCL812_GAIN 9 +#define PCL812_MUX 10 +#define PCL812_MODE 11 +#define PCL812_CNTENABLE 10 +#define PCL812_SOFTTRIG 12 +#define PCL812_DO_LO 13 +#define PCL812_DO_HI 14 -#define AI_LEN_CHANLIST 16 +#define PCL812_DRDY 0x10 /* =0 data ready */ -/* - For PCL-813B: - I don't know if timeouts which are specified at a documentation - are miliseconds or microseconds. If your card don't work properly then - undef next #define. -*/ -#define PCL813_MICROSECS +#define ACL8216_STATUS 8 /* 5. bit signalize data ready */ -#define INT_TYPE_AI1_INT 1 -/* #define INT_TYPE_AI1_DMA 2 */ -#define INT_TYPE_AI3_INT 3 -/* #define INT_TYPE_AI3_DMA 4 */ +#define ACL8216_DRDY 0x20 /* =0 data ready */ + +#define MAX_CHANLIST_LEN 256 /* length of scan list */ static comedi_lrange range_pcl812pg_ai = { 5, { BIP_RANGE(5), @@ -104,6 +162,15 @@ static comedi_lrange range_pcl812pg2_ai = { 5, { BIP_RANGE(1.25), BIP_RANGE(0.625), }}; +static comedi_lrange range812_bipolar1_25 = { 1, { + BIP_RANGE(1.25), +}}; +static comedi_lrange range812_bipolar0_625 = { 1, { + BIP_RANGE(0.625), +}}; +static comedi_lrange range812_bipolar0_3125 = { 1, { + BIP_RANGE(0.3125), +}}; static comedi_lrange range_pcl813b_ai = { 4, { BIP_RANGE(5), BIP_RANGE(2.5), @@ -116,68 +183,165 @@ static comedi_lrange range_pcl813b2_ai = { 4, { UNI_RANGE(2.5), UNI_RANGE(1.25), }}; +static comedi_lrange range_iso813_1_ai = { 5, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), + BIP_RANGE(0.3125), +}}; +static comedi_lrange range_iso813_1_2_ai = { 5, { + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), + UNI_RANGE(0.625), +}}; +static comedi_lrange range_iso813_2_ai = { 4, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), +}}; +static comedi_lrange range_iso813_2_2_ai = { 4, { + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), +}}; +static comedi_lrange range_acl8113_1_ai = { 4, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), +}}; +static comedi_lrange range_acl8113_1_2_ai = { 4, { + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), +}}; +static comedi_lrange range_acl8113_2_ai = { 3, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), +}}; +static comedi_lrange range_acl8113_2_2_ai = { 3, { + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), +}}; +static comedi_lrange range_acl8112dg_ai = { 9, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), + BIP_RANGE(10), +}}; +static comedi_lrange range_acl8112hg_ai = { 12, { + BIP_RANGE(5), + BIP_RANGE(0.5), + BIP_RANGE(0.05), + BIP_RANGE(0.005), + UNI_RANGE(10), + UNI_RANGE(1), + UNI_RANGE(0.1), + UNI_RANGE(0.01), + BIP_RANGE(10), + BIP_RANGE(1), + BIP_RANGE(0.1), + BIP_RANGE(0.01), +}}; +static comedi_lrange range_a821pgh_ai = { 4, { + BIP_RANGE(5), + BIP_RANGE(0.5), + BIP_RANGE(0.05), + BIP_RANGE(0.005), +}}; static int pcl812_attach(comedi_device *dev,comedi_devconfig *it); static int pcl812_detach(comedi_device *dev); -static int i8253_osc_base = 500; /* 2 Mhz */ - typedef struct { - char *name; - int is_812pg; - int is_813b; - int n_ranges; - int n_aichan; - int ai_maxsample; - int n_aochan; - int n_dichan; - int n_dochan; - comedi_lrange *ai_range_type; - comedi_lrange *ao_range_type; - int io_range; - unsigned int IRQbits; -#ifdef USE_DMA - unsigned int DMAbits; -#endif + char *name; // driver name + int board_type; // type of this board + int n_aichan; // num of AI chans in S.E. + int n_aichan_diff; // DIFF num of chans + int n_aochan; // num of DA chans + int n_dichan; // DI and DO chans + int n_dochan; + int ai_maxdata; // AI resolution + unsigned int ai_ns_min; // max sample speed of card v ns + unsigned int i8254_osc_base; // clock base + comedi_lrange *rangelist_ai; // rangelist for A/D + comedi_lrange *rangelist_ao; // rangelist for D/A + unsigned int IRQbits; // allowed IRQ + unsigned char DMAbits; // allowed DMA chans + unsigned char io_range; // iorange for this board + unsigned char haveMPC508; // 1=board use MPC508A multiplexor } boardtype; static boardtype boardtypes[] = { - { - name: "pcl812pg", - is_812pg: 1, - n_ranges: 5, - n_aichan: 16, - ai_maxsample: 30, - n_aochan: 2, - n_dichan: 16, - n_dochan: 16, - ai_range_type: &range_pcl812pg_ai, - ao_range_type: &range_unipolar5, - io_range: PCLx1x_RANGE, - IRQbits: 0xdcfc, -#ifdef USE_DMA - DMAbits: 0x00, -#endif - }, - { - name: "pcl813b", - is_813b: 1, - n_ranges: 4, - n_aichan: 32, - ai_maxsample: 25, - n_aochan: 0, - n_dichan: 0, - n_dochan: 0, - ai_range_type: &range_pcl813b_ai, - ao_range_type: NULL, - io_range: PCLx1x_RANGE, - IRQbits: 0x0000 -#ifdef USE_DMA - DMAbits: 0x00, -#endif - }, + {"pcl812", boardPCL812, 16, 0, 2, 16, 16, 0x0fff, + 33000, 500, &range_bipolar10, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"pcl812pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff, + 33000, 500, &range_pcl812pg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"acl8112pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff, + 10000, 500, &range_pcl812pg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"acl8112dg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 10000, 500, &range_acl8112dg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, + {"acl8112hg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 10000, 500, &range_acl8112hg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, + {"a821pgl", boardA821, 16, 8, 1, 16, 16, 0x0fff, + 10000, 500, &range_pcl813b_ai, &range_unipolar5, + 0x000c, 0x00, PCLx1x_IORANGE, 0}, + {"a821pglnda", boardA821, 16, 8, 0, 0, 0, 0x0fff, + 10000, 500, &range_pcl813b_ai, NULL, + 0x000c, 0x00, PCLx1x_IORANGE, 0}, + {"a821pgh", boardA821, 16, 8, 1, 16, 16, 0x0fff, + 10000, 500, &range_a821pgh_ai, &range_unipolar5, + 0x000c, 0x00, PCLx1x_IORANGE, 0}, + {"a822pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 10000, 500, &range_acl8112dg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"a822pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 10000, 500, &range_acl8112hg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"a823pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 8000, 500, &range_acl8112dg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"a823pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, + 8000, 500, &range_acl8112hg_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, + {"pcl813", boardPCL813, 32, 0, 0, 0, 0, 0x0fff, + 0, 0, &range_pcl813b_ai, NULL, + 0x0000, 0x00, PCLx1x_IORANGE, 0}, + {"pcl813b", boardPCL813B, 32, 0, 0, 0, 0, 0x0fff, + 0, 0, &range_pcl813b_ai, NULL, + 0x0000, 0x00, PCLx1x_IORANGE, 0}, + {"acl8113", boardACL8113, 32, 0, 0, 0, 0, 0x0fff, + 0, 0, &range_acl8113_1_ai, NULL, + 0x0000, 0x00, PCLx1x_IORANGE, 0}, + {"iso813", boardISO813, 32, 0, 0, 0, 0, 0x0fff, + 0, 0, &range_iso813_1_ai, NULL, + 0x0000, 0x00, PCLx1x_IORANGE, 0}, + {"acl8216", boardACL8216, 16, 8, 2, 16, 16, 0xffff, + 10000, 500, &range_pcl813b2_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, + {"a826pg", boardACL8216, 16, 8, 2, 16, 16, 0xffff, + 10000, 500, &range_pcl813b2_ai, &range_unipolar5, + 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, }; #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) @@ -195,36 +359,50 @@ comedi_driver driver_pcl812={ COMEDI_INITCLEANUP(driver_pcl812); typedef struct { -#ifdef USE_DMA - int dma; - unsigned long dmabuf[2]; - unsigned int dmapages[2]; - unsigned int hwdmaptr[2]; - unsigned int hwdmasize[2]; - int next_dma_buf; - unsigned long dma_runs_to_end; -#endif - int irq_free; - int irq_blocked; - int irq_was_now_closed; - int max_812_ai_mode0_samples; - int max_812_ai_mode0_rangewait; - int max_812_ai_mode0_chanset; - int max_812_ai_mode0_convstart; - int int812_mode; /*1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */ - //int int13_act_ptr; - int int13_act_scan; - unsigned int chanlist[AI_LEN_CHANLIST]; - lsampl_t ao_readback[2]; - + unsigned char valid; // =1 device is OK + unsigned char dma; // >0 use dma ( usedDMA channel) + unsigned char use_diff; // =1 diff inputs + unsigned char use_MPC; // 1=board uses MPC508A multiplexor + unsigned char use_ext_trg; // 1=board uses external trigger + unsigned char range_correction; // =1 we must add 1 to range number + unsigned char old_chan_reg; // lastly used chan/gain pair + unsigned char old_gain_reg; + unsigned char mode_reg_int; // there is stored INT number for some card + unsigned char ai_neverending; // =1 we do unlimited AI + unsigned char ai_eos; // 1=EOS wake up + unsigned char ai_dma; // =1 we use DMA + unsigned int ai_poll_ptr; // how many sampes transfer poll + unsigned int ai_scans; // len of scanlist + unsigned int ai_act_scan; // how many scans we finished + unsigned int ai_chanlist[MAX_CHANLIST_LEN];// our copy of channel/range list + unsigned int ai_n_chan; // how many channels is measured + unsigned int ai_flags; // flaglist + unsigned int ai_data_len; // len of data buffer + sampl_t *ai_data; // data buffer + unsigned int ai_is16b; // =1 we have 16 bit card + unsigned long dmabuf[2]; // PTR to DMA buf + unsigned int dmapages[2]; // how many pages we have allocated + unsigned int hwdmaptr[2]; // HW PTR to DMA buf + unsigned int hwdmasize[2]; // DMA buf size in bytes + unsigned int dmabytestomove[2]; // how many bytes DMA transfer + int next_dma_buf; // which buffer is next to use + unsigned int dma_runs_to_end; // how many times we must switch DMA buffers + unsigned int last_dma_run; // how many bytes to transfer on last DMA buffer + unsigned int max_812_ai_mode0_rangewait;// setling time for gain + lsampl_t ao_readback[2]; // data for AO readback } pcl812_private; #define devpriv ((pcl812_private *)dev->private) -// *INDENT-ON* /* ============================================================================== - ANALOG INPUT MODE0, 812pg and 813b card +*/ +void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2); +void setup_range_channel(comedi_device * dev, comedi_subdevice * s, + unsigned int rangechan, char wait); +int pcl812_ai_cancel(comedi_device * dev, comedi_subdevice * s); +/* +============================================================================== */ static int pcl812_ai_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) @@ -232,39 +410,62 @@ static int pcl812_ai_insn_read(comedi_device *dev,comedi_subdevice *s, int n; int timeout, hi; - outb(1, dev->iobase + PCL812_MODE); /* select software trigger */ - - /* select gain */ - outb(CR_RANGE(insn->chanspec), dev->iobase + PCL812_GAIN); - /* select channel */ - outb(CR_CHAN(insn->chanspec), dev->iobase + PCL812_MUX); -#define max(a,b) ((a>b)?(a):(b)) - udelay(max(devpriv->max_812_ai_mode0_rangewait, - devpriv->max_812_ai_mode0_chanset)); + outb(devpriv->mode_reg_int|1, dev->iobase + PCL812_MODE); /* select software trigger */ + setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge for(n=0;nn;n++){ outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */ - udelay(devpriv->max_812_ai_mode0_convstart); - timeout = 20; /* wait max 100us, it must finish under 33us */ + udelay(5); + timeout = 50; /* wait max 50us, it must finish under 33us */ while (timeout--) { hi = inb(dev->iobase + PCL812_AD_HI); if (!(hi & PCL812_DRDY)) goto conv_finish; + udelay(1); } - rt_printk("comedi%d: pcl812: (%s at 0x%x) A/D mode0 timeout\n", dev->minor, dev->board_name, dev->iobase); - outb(0, dev->iobase + PCL812_MODE); + rt_printk("comedi%d: pcl812: (%s at 0x%x) A/D insn read timeout\n", dev->minor, dev->board_name, dev->iobase); + outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE); return -ETIME; conv_finish: data[n] = ((hi & 0xf) << 8) | inb(dev->iobase + PCL812_AD_LO); } + outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE); + return n; +} + +/* +============================================================================== +*/ +static int acl8216_ai_insn_read(comedi_device *dev,comedi_subdevice *s, + comedi_insn *insn,lsampl_t *data) +{ + int n; + int timeout; + + outb(1, dev->iobase + PCL812_MODE); /* select software trigger */ + setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge + for(n=0;nn;n++){ + outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */ + udelay(5); + timeout = 50; /* wait max 50us, it must finish under 33us */ + while (timeout--) { + if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) + goto conv_finish; + udelay(1); + } + rt_printk("comedi%d: pcl812: (%s at 0x%x) A/D insn read timeout\n", dev->minor, dev->board_name, dev->iobase); + outb(0, dev->iobase + PCL812_MODE); + return -ETIME; + + conv_finish: + data[n] = (inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase + PCL812_AD_LO); + } outb(0, dev->iobase + PCL812_MODE); return n; } /* ============================================================================== - ANALOG OUTPUT MODE0, 812pg card - only one sample per call is supported */ static int pcl812_ao_insn_write(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) @@ -281,6 +482,9 @@ static int pcl812_ao_insn_write(comedi_device *dev,comedi_subdevice *s, return i; } +/* +============================================================================== +*/ static int pcl812_ao_insn_read(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) { @@ -296,9 +500,6 @@ static int pcl812_ao_insn_read(comedi_device *dev,comedi_subdevice *s, /* ============================================================================== - DIGITAL INPUT MODE0, 812pg card - - only one sample per call is supported */ static int pcl812_di_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) @@ -313,9 +514,6 @@ static int pcl812_di_insn_bits(comedi_device *dev,comedi_subdevice *s, /* ============================================================================== - DIGITAL OUTPUT MODE0, 812pg card - - only one sample per call is supported */ static int pcl812_do_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data) @@ -333,247 +531,578 @@ static int pcl812_do_insn_bits(comedi_device *dev,comedi_subdevice *s, return 2; } +#ifdef PCL812_EXTDEBUG /* ============================================================================== - analog input interrupt mode 1 & 3, 812pg card - one sample per interrupt version */ -static void interrupt_pcl812_ai_mode13_int(int irq, void *d, struct pt_regs *regs) +void pcl812_cmdtest_out(int e,comedi_cmd *cmd) { + rt_printk("pcl812 e=%d startsrc=%x scansrc=%x convsrc=%x\n",e,cmd->start_src,cmd->scan_begin_src,cmd->convert_src); + rt_printk("pcl812 e=%d startarg=%d scanarg=%d convarg=%d\n",e,cmd->start_arg,cmd->scan_begin_arg,cmd->convert_arg); + rt_printk("pcl812 e=%d stopsrc=%x scanend=%x\n",e,cmd->stop_src,cmd->scan_end_src); + rt_printk("pcl812 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",e,cmd->stop_arg,cmd->scan_end_arg,cmd->chanlist_len); +} +#endif + +/* +============================================================================== +*/ +static int pcl812_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) { + int err=0; + int tmp,divisor1,divisor2; - int hi; - comedi_device *dev = d; - comedi_subdevice *s = dev->subdevices + 0; +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...)\n"); + pcl812_cmdtest_out(-1, cmd); +#endif + /* step 1: make sure trigger sources are trivially valid */ - int timeout = 20; /* wait max 100us, it must finish under 33us */ - while (timeout--) { - hi = inb(dev->iobase + PCL812_AD_HI); - if (!(hi & PCL812_DRDY)) - goto conv_finish; - udelay(5); - } - hi = inb(dev->iobase + PCL812_AD_LO); - outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ - rt_printk("comedi%d: pcl812: (%s at 0x%x) A/D mode1/3 IRQ without DRDY!\n", dev->minor, dev->board_name, dev->iobase); - comedi_done(dev,s); - return; + tmp=cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src || tmp!=cmd->start_src)err++; - conv_finish: + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_FOLLOW; + if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; - *(sampl_t *)(s->async->data+s->async->buf_int_ptr) = - ((hi << 8) | inb(dev->iobase + PCL812_AD_LO)) & 0xfff; - s->async->buf_int_ptr+=sizeof(sampl_t); + tmp=cmd->convert_src; + if (devpriv->use_ext_trg) { cmd->convert_src &= TRIG_EXT; } + else { cmd->convert_src &= TRIG_TIMER; } + if(!cmd->convert_src || tmp!=cmd->convert_src)err++; - outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ + tmp=cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++; - s->async->buf_int_count += sizeof(sampl_t); + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT|TRIG_NONE; + if(!cmd->stop_src || tmp!=cmd->stop_src)err++; - s->async->cur_chan++; - if (s->async->cur_chan >= s->async->cur_chanlist_len) { /* one scan done */ - s->async->cur_chan=0; -#if 0 - /* this uses comedi_eos and comedi_bufcheck incorrectly */ - if (devpriv->cur_flags & TRIG_WAKE_EOS) { - comedi_eos(dev, s); - } else { - comedi_bufcheck(dev, s); + if(err) { +#ifdef PCL812_EXTDEBUG + pcl812_cmdtest_out(1, cmd); + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=1\n",err); +#endif + return 1; + } + + /* step 2: make sure trigger sources are unique and mutually compatible */ + + if(cmd->start_src!=TRIG_NOW) { + cmd->start_src=TRIG_NOW; + err++; + } + + if(cmd->scan_begin_src!=TRIG_FOLLOW) { + cmd->scan_begin_src=TRIG_FOLLOW; + err++; + } + + if (devpriv->use_ext_trg) { + if(cmd->convert_src!=TRIG_EXT) { + cmd->convert_src=TRIG_EXT; + err++; } -#else - comedi_bufcheck(dev, s); + } else { + if(cmd->convert_src!=TRIG_TIMER) { + cmd->convert_src=TRIG_TIMER; + 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 PCL812_EXTDEBUG + pcl812_cmdtest_out(2, cmd); + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=2\n",err); #endif - devpriv->int13_act_scan--; + return 2; } - outb(CR_RANGE(devpriv->chanlist[s->async->cur_chan]), dev->iobase + PCL812_GAIN); /* select next gain */ - outb(CR_CHAN(devpriv->chanlist[s->async->cur_chan]), dev->iobase + PCL812_MUX); /* select next channel */ - if (s->async->buf_int_ptr >= s->async->data_len) { /* buffer rollover */ - s->async->buf_int_ptr = 0; - //devpriv->int13_act_ptr=0; - comedi_eobuf(dev, s); + /* 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_argai_ns_min){ + cmd->convert_arg=this_board->ai_ns_min; + err++; + } + } else { /* TRIG_EXT */ + if(cmd->convert_arg!=0){ + cmd->convert_arg=0; + err++; + } } - if (devpriv->int13_act_scan == 0) { /* all data sampled */ - outb(0, dev->iobase + PCL812_MODE); /* Stop A/D */ - outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ - if (devpriv->int812_mode == 1) { - /* Stop pacer */ - outb(0xb4, dev->iobase + PCL812_CTRCTL); - outb(0x74, dev->iobase + PCL812_CTRCTL); + if(!cmd->chanlist_len){ + cmd->chanlist_len=1; + err++; + } + if(cmd->chanlist_len>MAX_CHANLIST_LEN){ + 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++; } - s->busy = 0; - devpriv->irq_blocked = 0; - devpriv->int812_mode = 0; - devpriv->irq_was_now_closed = 1; - /* printk("comedi_done\n"); */ - comedi_done(dev, s); } -} + if(err) { +#ifdef PCL812_EXTDEBUG + pcl812_cmdtest_out(3, cmd); + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=3\n",err); +#endif + return 3; + } + + /* step 4: fix up any arguments */ + + if(cmd->convert_src==TRIG_TIMER){ + tmp=cmd->convert_arg; + i8253_cascade_ns_to_timer(this_board->i8254_osc_base,&divisor1,&divisor2,&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK); + if(cmd->convert_argai_ns_min) + cmd->convert_arg=this_board->ai_ns_min; + if(tmp!=cmd->convert_arg)err++; + } + + if(err) { +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=4\n",err); +#endif + return 4; + } + + return 0; +} /* ============================================================================== - INT procedure */ -static void interrupt_pcl812(int irq, void *d, struct pt_regs *regs) +static int pcl812_ai_cmd(comedi_device *dev,comedi_subdevice *s) { + unsigned int divisor1, divisor2, i, dma_flags, bytes; + comedi_cmd *cmd=&s->async->cmd; + +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmd(...)\n"); +#endif - comedi_device *dev = d; + if(cmd->start_src!=TRIG_NOW) return -EINVAL; + if(cmd->scan_begin_src!=TRIG_FOLLOW) return -EINVAL; + if (devpriv->use_ext_trg) { + if(cmd->convert_src!=TRIG_EXT) return -EINVAL; + } else { + if(cmd->convert_src!=TRIG_TIMER) return -EINVAL; + } + if(cmd->scan_end_src!=TRIG_COUNT) return -EINVAL; + if(cmd->scan_end_arg!=cmd->chanlist_len) return -EINVAL; + if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; + + if (cmd->convert_src==TRIG_TIMER) { + if(cmd->convert_argai_ns_min) cmd->convert_arg=this_board->ai_ns_min; + i8253_cascade_ns_to_timer(this_board->i8254_osc_base, + &divisor1, &divisor2, &cmd->convert_arg, + cmd->flags&TRIG_ROUND_MASK); + } - if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) | (!devpriv->int812_mode)) { - if (devpriv->irq_was_now_closed) { - devpriv->irq_was_now_closed = 0; - outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ - rt_printk("comedi%d: pcl812: (%s at 0x%x) too much IRQs!\n", dev->minor, dev->board_name, dev->iobase); - return; - } - rt_printk("comedi%d: pcl812: (%s at 0x%x) bad IRQ!\n", dev->minor, dev->board_name, dev->iobase); - return; + start_pacer(dev, -1, 0, 0); // stop pacer + + devpriv->ai_n_chan=cmd->chanlist_len; + memcpy(devpriv->ai_chanlist,cmd->chanlist,sizeof(unsigned int)*cmd->scan_end_arg); + setup_range_channel(dev, s, devpriv->ai_chanlist[0], 1); // select first channel and range + + if (devpriv->dma) { // check if we can use DMA transfer + devpriv->ai_dma=1; + for (i=1; iai_n_chan; i++) + if (devpriv->ai_chanlist[0]!=devpriv->ai_chanlist[i]) { + devpriv->ai_dma=0; // we cann't use DMA :-( + break; + } + } else devpriv->ai_dma=0; + + devpriv->ai_flags=cmd->flags; + devpriv->ai_data_len=s->async->data_len; + devpriv->ai_data=s->async->data; + if (cmd->stop_src==TRIG_COUNT) { devpriv->ai_scans=cmd->stop_arg; devpriv->ai_neverending=0; } + else { devpriv->ai_scans=0; devpriv->ai_neverending=1; } + + devpriv->ai_act_scan=0; + devpriv->ai_poll_ptr=0; + s->async->cur_chan=0; + + if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan? + devpriv->ai_eos=1; + if (devpriv->ai_n_chan==1) + devpriv->ai_dma=0; // DMA is useless for this situation } - switch (devpriv->int812_mode) { - case INT_TYPE_AI1_INT: - interrupt_pcl812_ai_mode13_int(irq, d, regs); - return; - case INT_TYPE_AI3_INT: - interrupt_pcl812_ai_mode13_int(irq, d, regs); - return; -#if USE_DMA - case INT_TYPE_AI1_DMA: - interrupt_pcl812_ai_mode13_dma(irq, d, regs); - return; - case INT_TYPE_AI3_DMA: - interrupt_pcl812_ai_mode13_dma(irq, d, regs); - return; + if (devpriv->ai_dma) { + if (devpriv->ai_eos) { // we use EOS, so adapt DMA buffer to one scan + devpriv->dmabytestomove[0]=devpriv->ai_n_chan*sizeof(sampl_t); + devpriv->dmabytestomove[1]=devpriv->ai_n_chan*sizeof(sampl_t); + devpriv->dma_runs_to_end=1; + } else { + devpriv->dmabytestomove[0]=devpriv->hwdmasize[0]; + devpriv->dmabytestomove[1]=devpriv->hwdmasize[1]; + if (devpriv->ai_data_lenhwdmasize[0]) + devpriv->dmabytestomove[0]=devpriv->ai_data_len; + if (devpriv->ai_data_lenhwdmasize[1]) + devpriv->dmabytestomove[1]=devpriv->ai_data_len; + if (devpriv->ai_neverending) { + devpriv->dma_runs_to_end=1; + } else { + bytes=devpriv->ai_n_chan*devpriv->ai_scans*sizeof(sampl_t); // how many samples we must transfer? + devpriv->dma_runs_to_end=bytes / devpriv->dmabytestomove[0]; // how many DMA pages we must fill + devpriv->last_dma_run=bytes % devpriv->dmabytestomove[0]; //on last dma transfer must be moved + if (devpriv->dma_runs_to_end==0) + devpriv->dmabytestomove[0]=devpriv->last_dma_run; + devpriv->dma_runs_to_end--; + } + } + if (devpriv->dmabytestomove[0]>devpriv->hwdmasize[0]) { + devpriv->dmabytestomove[0]=devpriv->hwdmasize[0]; + devpriv->ai_eos=0; + } + if (devpriv->dmabytestomove[1]>devpriv->hwdmasize[1]) { + devpriv->dmabytestomove[1]=devpriv->hwdmasize[1]; + devpriv->ai_eos=0; + } + devpriv->next_dma_buf=0; + set_dma_mode(devpriv->dma, DMA_MODE_READ); + dma_flags=claim_dma_lock(); + clear_dma_ff(devpriv->dma); + set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]); + set_dma_count(devpriv->dma, devpriv->dmabytestomove[0]); + release_dma_lock(dma_flags); + enable_dma(devpriv->dma); +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: DMA %d PTR 0x%0x/0x%0x LEN %u/%u EOS %d\n", + devpriv->dma,devpriv->hwdmaptr[0],devpriv->hwdmaptr[1], + devpriv->dmabytestomove[0],devpriv->dmabytestomove[1], + devpriv->ai_eos); #endif } + + switch (cmd->convert_src) { + case TRIG_TIMER: + start_pacer(dev, 1, divisor1, divisor2); + break; + } + + if (devpriv->ai_dma) { + outb(devpriv->mode_reg_int|2, dev->iobase + PCL812_MODE); // let's go! + } else { + outb(devpriv->mode_reg_int|6, dev->iobase + PCL812_MODE); // let's go! + } + +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: END: pcl812_ai_cmd(...)\n"); +#endif + + return 0; } /* ============================================================================== - ANALOG INPUT MODE 1, 812pg card - interrupt pacer pooling */ -#ifdef CONFIG_COMEDI_TRIG -static int pcl812_ai_mode1_int(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +static void interrupt_pcl812_ai_int(int irq, void *d, struct pt_regs *regs) { - int timer1,timer2; + char err=1; + unsigned int mask, timeout; + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices + 0; - /* - * Set timers - * timer chip is an 8253, with timers 1 and 2 - * cascaded - * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary - * Mode 2 = Rate generator - * - * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary - */ + s->async->events = 0; - i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,&it->trigvar,TRIG_ROUND_NEAREST); + timeout = 50; /* wait max 50us, it must finish under 33us */ + if (devpriv->ai_is16b) { + mask=0xffff; + while (timeout--) { + if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) { + err=0; break; + } + udelay(1); + } + } else { + mask=0x0fff; + while (timeout--) { + if (!(inb(dev->iobase + PCL812_AD_HI) & PCL812_DRDY)) { + err=0; break; + } + udelay(1); + } + } + + if (err) { + rt_printk("comedi%d: pcl812: (%s at 0x%x) A/D cmd IRQ without DRDY!\n", dev->minor, dev->board_name, dev->iobase); + pcl812_ai_cancel(dev,s); + s->async->events |= COMEDI_CB_EOA|COMEDI_CB_ERROR; + comedi_event(dev,s,s->async->events); + return; + } - outb(0x74, dev->iobase + PCL812_CTRCTL); - outb((timer1) & 0xff, dev->iobase + PCL812_CTR1); - outb((timer1 >> 8) & 0xff, dev->iobase + PCL812_CTR1); - outb(0xb4, dev->iobase + PCL812_CTRCTL); - outb(timer2 & 0xff, dev->iobase + PCL812_CTR2); - outb((timer2 >> 8) & 0xff, dev->iobase + PCL812_CTR2); + *(sampl_t *)(s->async->data+s->async->buf_int_ptr) = + ((inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase + PCL812_AD_LO)) & mask; - /* clear pending interrupts (just in case) */ - outb(0, dev->iobase + PCL812_CLRINT); + setup_range_channel(dev, s, devpriv->ai_chanlist[s->async->cur_chan], 0); // select channel and renge - //devpriv->int13_act_ptr=0; - devpriv->int13_act_scan = 0; - devpriv->int812_mode = INT_TYPE_AI1_INT; /* analog in, mode 0, int driven */ - devpriv->irq_blocked = 1; - devpriv->irq_was_now_closed = 0; + outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ - memcpy(devpriv->chanlist,it->chanlist,sizeof(int)*it->n_chan); + s->async->buf_int_ptr+=sizeof(sampl_t); + s->async->buf_int_count+=sizeof(sampl_t); - outb(6, dev->iobase + PCL812_MODE); /* Pacer+IRQ */ + s->async->cur_chan++; + if (s->async->cur_chan >= s->async->cur_chanlist_len) { /* one scan done */ + s->async->cur_chan=0; + devpriv->ai_act_scan++; + if (devpriv->ai_eos) + s->async->events |= COMEDI_CB_EOS; + if (!(devpriv->ai_neverending)) + if (devpriv->ai_act_scan>=devpriv->ai_scans) { /* all data sampled */ + pcl812_ai_cancel(dev,s); + s->async->events |= COMEDI_CB_EOA; + } + } + + if (s->async->buf_int_ptr >= s->async->data_len) { /* buffer rollover */ + s->async->buf_int_ptr = 0; + s->async->events |= COMEDI_CB_EOBUF; + } - return 0; + comedi_event(dev,s,s->async->events); } -#endif /* ============================================================================== - ANALOG INPUT MODE 1, 812pg card */ -#ifdef CONFIG_COMEDI_TRIG -static int pcl812_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +static void transfer_from_dma_buf(comedi_device *dev,comedi_subdevice *s, + sampl_t *ptr, unsigned int bufptr, unsigned int len) { - - if (!dev->irq) - return -EINVAL; - if (devpriv->irq_blocked) - return -EBUSY; - if (it->n_chan < 0) - return -1; - -#ifdef USE_DMA - if (devpriv->dma) { /* check if we can use DMA? */ - if (it->n_chan == 1) { - return pcl812_ai_mode1_dma(dev, s, it); /* we scanning only one chan, we can */ - } else { - fst = it->chanlist[0]; - for (i = 1; i < it->n_chan; i++) { - if (fst != it->chanlist[0]) { - i = -1; + unsigned int i; + + s->async->events = 0; + for (i=len; i; i--) { + *(sampl_t *)(s->async->data+s->async->buf_int_ptr)=ptr[bufptr++]; // get one sample + + s->async->buf_int_ptr+=sizeof(sampl_t); + s->async->buf_int_count+=sizeof(sampl_t); + + s->async->cur_chan++; + if(s->async->cur_chan>=s->async->cur_chanlist_len){ + s->async->cur_chan=0; + devpriv->ai_act_scan++; + if (devpriv->ai_eos) + s->async->events |= COMEDI_CB_EOS; + if (!devpriv->ai_neverending) + if (devpriv->ai_act_scan>=devpriv->ai_scans) { /* all data sampled */ + pcl812_ai_cancel(dev,s); + s->async->events |= COMEDI_CB_EOA; break; } - } - if (i == -1) { - return pcl812_ai_mode1_int(dev, s, it); - } else { - return pcl812_ai_mode1_dma(dev, s, it); - } } + + if (s->async->buf_int_ptr>=s->async->data_len) { /* buffer rollover */ + s->async->buf_int_ptr=0; + s->async->events |= COMEDI_CB_EOBUF; + comedi_event(dev,s,s->async->events); + s->async->events = 0; + } + } -#endif - return pcl812_ai_mode1_int(dev, s, it); /* no, we can only int driven */ + + s->async->events |= COMEDI_CB_BLOCK; + comedi_event(dev,s,s->async->events); } + +/* +============================================================================== +*/ +static void interrupt_pcl812_ai_dma(int irq, void *d, struct pt_regs *regs) +{ + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices + 0; + unsigned long dma_flags; + int len,bufptr; + sampl_t *ptr; + +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: interrupt_pcl812_ai_dma(...)\n"); +#endif + ptr=(sampl_t *)devpriv->dmabuf[devpriv->next_dma_buf]; + len=(devpriv->dmabytestomove[devpriv->next_dma_buf] >> 1) - devpriv->ai_poll_ptr; + + devpriv->next_dma_buf=1-devpriv->next_dma_buf; + disable_dma(devpriv->dma); + set_dma_mode(devpriv->dma, DMA_MODE_READ); + dma_flags=claim_dma_lock(); + set_dma_addr(devpriv->dma, devpriv->hwdmaptr[devpriv->next_dma_buf]); + if (devpriv->ai_eos) { + set_dma_count(devpriv->dma, devpriv->dmabytestomove[devpriv->next_dma_buf]); + } else { + if (devpriv->dma_runs_to_end) { set_dma_count(devpriv->dma, devpriv->dmabytestomove[devpriv->next_dma_buf]); } + else { set_dma_count(devpriv->dma, devpriv->last_dma_run); } + devpriv->dma_runs_to_end--; + } + release_dma_lock(dma_flags); + enable_dma(devpriv->dma); + + outb(0,dev->iobase+PCL812_CLRINT); /* clear INT request */ + + bufptr=devpriv->ai_poll_ptr; + devpriv->ai_poll_ptr=0; + + transfer_from_dma_buf(dev, s, ptr, bufptr, len); + +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: END: interrupt_pcl812_ai_dma(...)\n"); #endif +} /* ============================================================================== - ANALOG INPUT MODE 3, 812pg card */ -#ifdef CONFIG_COMEDI_TRIG -static int pcl812_ai_mode3_int(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +static void interrupt_pcl812(int irq, void *d, struct pt_regs *regs) { + comedi_device *dev = d; - if (!dev->irq) - return -EINVAL; - if (devpriv->irq_blocked) - return -EBUSY; + if (devpriv->ai_dma) { interrupt_pcl812_ai_dma(irq, d, regs); } + else { interrupt_pcl812_ai_int(irq, d, regs); }; - /* clear pending interrupts (just in case) */ - outb(0, dev->iobase + PCL812_CLRINT); +} - //devpriv->int13_act_ptr=0; - devpriv->int13_act_scan = it->n; - devpriv->int812_mode = 3; /* analog in, mode 3, int driven */ - devpriv->irq_blocked = 1; +/* +============================================================================== +*/ +static int pcl812_ai_poll(comedi_device *dev,comedi_subdevice *s) +{ + unsigned long flags; + unsigned int top1,top2,i; - memcpy(devpriv->chanlist,it->chanlist,sizeof(int)*it->n_chan); + if (!devpriv->ai_dma) return 0; // poll is valid only for DMA transfer + + comedi_spin_lock_irqsave(&dev->spinlock,flags); - outb(6, dev->iobase + PCL812_MODE); /* external trigger+IRQ */ + for (i=0; i<10; i++) { + top1=get_dma_residue(devpriv->ai_dma); // where is now DMA + top2=get_dma_residue(devpriv->ai_dma); + if (top1==top2) break; + } - return 0; + if (top1!=top2) { + comedi_spin_unlock_irqrestore(&dev->spinlock,flags); + return 0; + } + + top1=devpriv->dmabytestomove[1-devpriv->next_dma_buf]-top1; // where is now DMA in buffer + top1>>=1; // sample position + top2=top1-devpriv->ai_poll_ptr; + if (top2<1) { // no new samples + comedi_spin_unlock_irqrestore(&dev->spinlock,flags); + return 0; + } + + transfer_from_dma_buf(dev, s, (void *)devpriv->dmabuf[1-devpriv->next_dma_buf], + devpriv->ai_poll_ptr, top2); + + devpriv->ai_poll_ptr=top1; // new buffer position + + comedi_spin_unlock_irqrestore(&dev->spinlock,flags); + + return s->async->buf_int_count-s->async->buf_user_count; } + +/* +============================================================================== +*/ +void setup_range_channel(comedi_device * dev, comedi_subdevice * s, + unsigned int rangechan, char wait) +{ + unsigned char chan_reg=CR_CHAN(rangechan); // normal board + unsigned char gain_reg=CR_RANGE(rangechan)+devpriv->range_correction; // gain index + + if ((chan_reg==devpriv->old_chan_reg)&&(gain_reg==devpriv->old_gain_reg)) + return; // we can return, no change + + devpriv->old_chan_reg=chan_reg; + devpriv->old_gain_reg=gain_reg; + + if (devpriv->use_MPC) { + if (devpriv->use_diff) { + chan_reg=chan_reg | 0x30; // DIFF inputs + } else { + if (chan_reg&0x80) { + chan_reg=chan_reg | 0x20; // SE inputs 8-15 + } else { + chan_reg=chan_reg | 0x10; // SE inputs 0-7 + } + } + } + + outb(chan_reg, dev->iobase + PCL812_MUX); /* select channel */ + outb(gain_reg, dev->iobase + PCL812_GAIN); /* select gain */ + + if (wait) { + udelay(devpriv->max_812_ai_mode0_rangewait); // XXX this depends on selected range and can be very long for some high gain ranges! + } +} + + +/* +============================================================================== +*/ +void start_pacer(comedi_device * dev, int mode, unsigned int divisor1, unsigned int divisor2) +{ +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: start_pacer(%d,%u,%u)\n",mode,divisor1,divisor2); +#endif + outb(0xb4, dev->iobase + PCL812_CTRCTL); + outb(0x74, dev->iobase + PCL812_CTRCTL); + udelay(1); + + if (mode==1) { + outb(divisor2 & 0xff, dev->iobase + PCL812_CTR2); + outb((divisor2 >> 8) & 0xff, dev->iobase + PCL812_CTR2); + outb(divisor1 & 0xff, dev->iobase + PCL812_CTR1); + outb((divisor1 >> 8) & 0xff, dev->iobase + PCL812_CTR1); + } +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: END: start_pacer(...)\n"); #endif +} /* ============================================================================== - Free any resources that we have claimed */ static void free_resources(comedi_device * dev) { - if (dev->irq) - free_irq(dev->irq, dev); - if (dev->iobase) - release_region(dev->iobase, this_board->io_range); -#ifdef USE_DMA if (dev->private) { if (devpriv->dmabuf[0]) free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]); @@ -582,61 +1111,90 @@ static void free_resources(comedi_device * dev) if (devpriv->dma) free_dma(devpriv->dma); } + if (dev->irq) + free_irq(dev->irq, dev); + if (dev->iobase) + release_region(dev->iobase, this_board->io_range); +} + +/* +============================================================================== +*/ +int pcl812_ai_cancel(comedi_device * dev, comedi_subdevice * s) +{ +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: pcl812_ai_cancel(...)\n"); #endif + if (devpriv->ai_dma) disable_dma(devpriv->dma); + outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ + outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE); /* Stop A/D */ + start_pacer(dev,-1,0,0); // stop 8254 + outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: END: pcl812_ai_cancel(...)\n"); +#endif + return 0; } /* ============================================================================== - reset whole PCL-812 or PCL-813 */ static void pcl812_reset(comedi_device * dev) { - if (this_board->is_812pg){ - outb(0, dev->iobase + PCL812_DA1_LO); - outb(0, dev->iobase + PCL812_DA1_HI); +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: BGN: pcl812_reset(...)\n"); +#endif + outb(0, dev->iobase + PCL812_MUX); + outb(0+devpriv->range_correction, dev->iobase + PCL812_GAIN); + devpriv->old_chan_reg=-1; // invalidate chain/gain memory + devpriv->old_gain_reg=-1; + + switch (this_board->board_type) { + case boardPCL812PG: + case boardPCL812: + case boardACL8112: + case boardACL8216: outb(0, dev->iobase + PCL812_DA2_LO); outb(0, dev->iobase + PCL812_DA2_HI); + case boardA821: + outb(0, dev->iobase + PCL812_DA1_LO); + outb(0, dev->iobase + PCL812_DA1_HI); + start_pacer(dev,-1,0,0); // stop 8254 outb(0, dev->iobase + PCL812_DO_HI); outb(0, dev->iobase + PCL812_DO_LO); - outb(0, dev->iobase + PCL812_MODE); + outb(devpriv->mode_reg_int|0, dev->iobase + PCL812_MODE); outb(0, dev->iobase + PCL812_CLRINT); + break; + case boardPCL813B: + case boardPCL813: + case boardISO813: + case boardACL8113: + udelay(5); + break; } - outb(0, dev->iobase + PCL812_GAIN); - outb(0, dev->iobase + PCL812_MUX); udelay(5); - if (this_board->is_813b){ -#ifdef PCL813_MICROSECS - udelay(5); -#else - udelay(5000); +#ifdef PCL812_EXTDEBUG + rt_printk("pcl812 EDBG: END: pcl812_reset(...)\n"); #endif - } } /* ============================================================================== - - Initialization - */ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) { - - int ret; + int ret,subdev; int iobase; int irq; -#ifdef USE_DMA int dma; unsigned long pages; -#endif comedi_subdevice *s; - int num_of_subdevs, subdevs[5]; - /* claim our I/O space */ iobase = it->options[0]; printk("comedi%d: pcl812: board=%s, ioport=0x%03x", dev->minor, this_board->name, iobase); + if (check_region(iobase, this_board->io_range) < 0) { printk("I/O port conflict\n"); return -EIO; @@ -644,15 +1202,13 @@ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) request_region(iobase, this_board->io_range, "pcl812"); dev->iobase = iobase; - /* there should be a sanity check here */ - - if ((ret = alloc_private(dev, sizeof(pcl812_private))) < 0) + if ((ret = alloc_private(dev, sizeof(pcl812_private))) < 0) { + free_resources(dev); return ret; /* Can't alloc mem */ + } - /* set up some name stuff */ dev->board_name = this_board->name; - /* grab our IRQ */ irq = 0; if (this_board->IRQbits != 0) { /* board support IRQ */ irq = it->options[1]; @@ -672,24 +1228,14 @@ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) } dev->irq = irq; - if (irq) { - devpriv->irq_free = 1; - } /* 1=we have allocated irq */ - else { - devpriv->irq_free = 0; - } - devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */ - devpriv->int812_mode = 0; /* mode of irq */ -#ifdef USE_DMA - /* grab our DMA */ dma = 0; devpriv->dma = dma; - if (!devpriv->irq_free) + if (!dev->irq) goto no_dma; /* if we haven't IRQ, we can't use DMA */ - if (boardtypes[board].DMAbits != 0) { /* board support DMA */ + if (this_board->DMAbits != 0) { /* board support DMA */ dma = it->options[2]; - if (((1 << dma) & boardtypes[board].DMAbits) == 0) { + if (((1 << dma) & this_board->DMAbits) == 0) { printk(", DMA is out of allowed range, FAIL!\n"); return -EINVAL; /* Bad DMA */ } @@ -700,102 +1246,175 @@ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) } devpriv->dma = dma; printk(", dma=%d", dma); - pages = 1; /* we need 8KB */ + pages = 1; /* we want 8KB */ devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages); if (!devpriv->dmabuf[0]) { printk(", unable to allocate DMA buffer, FAIL!\n"); /* maybe experiment with try_to_free_pages() will help .... */ + free_resources(dev); return -EBUSY; /* no buffer :-( */ } devpriv->dmapages[0] = pages; devpriv->hwdmaptr[0] = virt_to_bus((void *) devpriv->dmabuf[0]); - devpriv->hwdmasize[0] = PAGE_SIZE * 2; + devpriv->hwdmasize[0] = PAGE_SIZE * (1<dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages); if (!devpriv->dmabuf[1]) { printk(", unable to allocate DMA buffer, FAIL!\n"); + free_resources(dev); return -EBUSY; } devpriv->dmapages[1] = pages; devpriv->hwdmaptr[1] = virt_to_bus((void *) devpriv->dmabuf[1]); - devpriv->hwdmasize[1] = PAGE_SIZE * 2; + devpriv->hwdmasize[1] = PAGE_SIZE * (1<n_subdevices=0; - /*if (!((board==boardPCL812PG)&&(it->options[3]==1))) { */ - subdevs[num_of_subdevs++] = COMEDI_SUBD_AI; - /* } */ - if (this_board->n_aochan > 0) - subdevs[num_of_subdevs++] = COMEDI_SUBD_AO; - if (this_board->n_dichan > 0) - subdevs[num_of_subdevs++] = COMEDI_SUBD_DI; - if (this_board->n_dochan > 0) - subdevs[num_of_subdevs++] = COMEDI_SUBD_DO; + if (this_board->n_aichan > 0) dev->n_subdevices++; + if (this_board->n_aochan > 0) dev->n_subdevices++; + if (this_board->n_dichan > 0) dev->n_subdevices++; + if (this_board->n_dochan > 0) dev->n_subdevices++; - dev->n_subdevices = 4; - if ((ret = alloc_subdevices(dev)) < 0) + if ((ret = alloc_subdevices(dev)) < 0) { + free_resources(dev); return ret; + } + + subdev=0; /* analog input */ - s = dev->subdevices + 0; - if (this_board->n_aichan == 0) { - s->type = COMEDI_SUBD_UNUSED; - } else { + if (this_board->n_aichan>0) { + s = dev->subdevices + subdev; + dev->read_subdev = s; s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE; - s->n_chan = this_board->n_aichan; - s->maxdata = 0xfff; - s->len_chanlist = AI_LEN_CHANLIST; - s->range_table = this_board->ai_range_type; - s->subdev_flags |= SDF_GROUND; - s->insn_read = pcl812_ai_insn_read; - if(this_board->is_812pg){ - if (it->options[3] == 1) + switch (this_board->board_type) { + case boardA821: + if (it->options[2] == 1) { + s->n_chan = this_board->n_aichan_diff; + s->subdev_flags |= SDF_DIFF; + devpriv->use_diff=1; + } else { + s->n_chan = this_board->n_aichan; + s->subdev_flags |= SDF_GROUND; + } + break; + case boardACL8112: + case boardACL8216: + if (it->options[4] == 1) { + s->n_chan = this_board->n_aichan_diff; + s->subdev_flags |= SDF_DIFF; + devpriv->use_diff=1; + } else { + s->n_chan = this_board->n_aichan; + s->subdev_flags |= SDF_GROUND; + } + break; + default: + s->n_chan = this_board->n_aichan; + s->subdev_flags |= SDF_GROUND; + break; + } + s->maxdata = this_board->ai_maxdata; + s->len_chanlist = MAX_CHANLIST_LEN; + s->range_table = this_board->rangelist_ai; + if (this_board->board_type==boardACL8216) { + s->insn_read = acl8216_ai_insn_read; + } else { + s->insn_read = pcl812_ai_insn_read; + } + devpriv->use_MPC = this_board->haveMPC508; + s->cancel = pcl812_ai_cancel; + if (dev->irq) { + s->do_cmdtest = pcl812_ai_cmdtest; + s->do_cmd = pcl812_ai_cmd; + s->poll = pcl812_ai_poll; + } + switch (this_board->board_type) { + case boardPCL812PG: + if (it->options[4] == 1) s->range_table = &range_pcl812pg2_ai; -#ifdef CONFIG_COMEDI_TRIG - if (dev->irq) { - if (it->options[2] != 1) { - s->trig[1] = pcl812_ai_mode1; - } else { - s->trig[3] = pcl812_ai_mode3_int; - } + break; + case boardPCL812: + switch (it->options[4]) { + case 0: s->range_table = &range_bipolar10; break; + case 1: s->range_table = &range_bipolar5; break; + case 2: s->range_table = &range_bipolar2_5; break; + case 3: s->range_table = &range812_bipolar1_25; break; + case 4: s->range_table = &range812_bipolar0_625; break; + case 5: s->range_table = &range812_bipolar0_3125; break; + default: + s->range_table = &range_bipolar10; break; + printk(", incorrect range number %d, changing to 0 (+/-10V)", it->options[4]); + break; } -#endif - }else{ + break; + break; + case boardPCL813B: if (it->options[1] == 1) s->range_table = &range_pcl813b2_ai; + break; + case boardISO813: + switch (it->options[1]) { + case 0: s->range_table = &range_iso813_1_ai; break; + case 1: s->range_table = &range_iso813_1_2_ai; break; + case 2: s->range_table = &range_iso813_2_ai; devpriv->range_correction=1; break; + case 3: s->range_table = &range_iso813_2_2_ai; devpriv->range_correction=1; break; + default: + s->range_table = &range_iso813_1_ai; break; + printk(", incorrect range number %d, changing to 0 ", it->options[1]); + break; + } + break; + case boardACL8113: + switch (it->options[1]) { + case 0: s->range_table = &range_acl8113_1_ai; break; + case 1: s->range_table = &range_acl8113_1_2_ai; break; + case 2: s->range_table = &range_acl8113_2_ai; devpriv->range_correction=1; break; + case 3: s->range_table = &range_acl8113_2_2_ai; devpriv->range_correction=1; break; + default: + s->range_table = &range_acl8113_1_ai; break; + printk(", incorrect range number %d, changing to 0 ", it->options[1]); + break; + } + break; } + subdev++; } /* analog output */ - s = dev->subdevices + 1; - if (this_board->n_aochan == 0) { - s->type = COMEDI_SUBD_UNUSED; - } else { + if (this_board->n_aochan>0) { + s = dev->subdevices + subdev; s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITEABLE; + s->subdev_flags = SDF_WRITEABLE|SDF_GROUND; s->n_chan = this_board->n_aochan; s->maxdata = 0xfff; s->len_chanlist = 1; - s->range_table = this_board->ao_range_type; - if(this_board->is_812pg){ - s->subdev_flags |= SDF_GROUND; - s->insn_read = pcl812_ao_insn_read; - s->insn_write = pcl812_ao_insn_write; - if (it->options[4] == 1) - s->range_table = &range_unipolar5; - if (it->options[4] == 2) + s->range_table = this_board->rangelist_ao; + s->insn_read = pcl812_ao_insn_read; + s->insn_write = pcl812_ao_insn_write; + switch (this_board->board_type) { + case boardA821: + if (it->options[3] == 1) + s->range_table = &range_unipolar10; + break; + case boardPCL812: + case boardACL8112: + case boardPCL812PG: + case boardACL8216: + if (it->options[5] == 1) + s->range_table = &range_unipolar10; + if (it->options[5] == 2) s->range_table = &range_unknown; + break; } + subdev++; } /* digital input */ - s = dev->subdevices + 2; - if (this_board->n_dichan == 0) { - s->type = COMEDI_SUBD_UNUSED; - } else { + if (this_board->n_dichan>0) { + s = dev->subdevices + subdev; s->type = COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE; s->n_chan = this_board->n_dichan; @@ -803,13 +1422,12 @@ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) s->len_chanlist = this_board->n_dichan; s->range_table = &range_digital; s->insn_bits = pcl812_di_insn_bits; + subdev++; } /* digital output */ - s = dev->subdevices + 3; - if (this_board->n_dochan == 0) { - s->type = COMEDI_SUBD_UNUSED; - } else { + if (this_board->n_dochan>0) { + s = dev->subdevices + subdev; s->type = COMEDI_SUBD_DO; s->subdev_flags = SDF_WRITEABLE; s->n_chan = this_board->n_dochan; @@ -817,108 +1435,48 @@ static int pcl812_attach(comedi_device * dev, comedi_devconfig * it) s->len_chanlist = this_board->n_dochan; s->range_table = &range_digital; s->insn_bits = pcl812_do_insn_bits; + subdev++; } - pcl812_reset(dev); - if(this_board->is_812pg){ - devpriv->max_812_ai_mode0_samples = 32; + switch (this_board->board_type) { + case boardACL8216: + devpriv->ai_is16b=1; + case boardPCL812PG: + case boardPCL812: + case boardACL8112: devpriv->max_812_ai_mode0_rangewait = 1; - devpriv->max_812_ai_mode0_chanset = 1; - devpriv->max_812_ai_mode0_convstart = 5; - } - if(this_board->is_813b){ - if (it->options[2] < 2) { - devpriv->max_812_ai_mode0_samples = 1; - } else { - devpriv->max_812_ai_mode0_samples = it->options[2]; - } -#ifdef PCL813_MICROSECS - devpriv->max_812_ai_mode0_rangewait = 1; /* maybe there must by greatest timeout */ - devpriv->max_812_ai_mode0_chanset = 5; - devpriv->max_812_ai_mode0_convstart = 20; -#else - devpriv->max_812_ai_mode0_rangewaint = 1; - devpriv->max_812_ai_mode0_chanset = 5000; - devpriv->max_812_ai_mode0_convstart = 20000; -#endif + if (it->options[3] > 0) devpriv->use_ext_trg=1; // we use external trigger + case boardA821: + devpriv->max_812_ai_mode0_rangewait = 1; + devpriv->mode_reg_int=(irq<<4) & 0xf0; + break; + case boardPCL813B: + case boardPCL813: + case boardISO813: + case boardACL8113: + devpriv->max_812_ai_mode0_rangewait = 5; /* maybe there must by greatest timeout */ + break; } + printk("\n"); + devpriv->valid=1; + + pcl812_reset(dev); + return 0; } /* ============================================================================== - Removes device */ static int pcl812_detach(comedi_device * dev) { -#ifdef MD_DEBUG - printk("comedi%d: pcl812: remove\n", dev->minor); +#ifdef PCL812_EXTDEBUG + rt_printk("comedi%d: pcl812: remove\n", dev->minor); #endif free_resources(dev); return 0; } - -#if 0 -/* @@Kluvi: magic crystaline sphere error correction, I hope - that work identicaly under C and TP :-) */ -static void Zisti_Div8254(long celk, long *d1, long *d2) -{ - long minch, chyba, mini, i; - - minch = 32000; - mini = 1; - if (celk <= (65536 * 2)) { - i = 2; - } else { - i = celk / 65536; - } - do { - *d1 = i; - *d2 = celk / *d1; - if (*d2 < 65536) { - chyba = celk - *d1 * *d2; - if (chyba < 0) { - chyba = -chyba; - } - if (chyba < minch) { - minch = chyba; - mini = i; - if (chyba == 0) - break; - } - } - i++; - } while ((i < 65536) && (*d2 > 2) && (*d2 > *d1)); - i = mini; - *d1 = i; - *d2 = celk / *d1; -} - -static int pcl812_timer(double freq, unsigned int *trigvar, double *actual_freq) -{ - long divisor1, divisor2, divid; - double Oscilator = 2e6; - - if (freq < 0.0004) { - freq = 0.0004; - } - if (freq > 30000) { - freq = 30000; - } - divid = rint(Oscilator / freq); - Zisti_Div8254(divid, &divisor1, &divisor2); - divid = rint(Oscilator / freq + 0.5 * (divid - divisor1 * divisor2)); - Zisti_Div8254(divid, &divisor1, &divisor2); - *actual_freq = Oscilator / (divisor1 * divisor2); - - *trigvar = (divisor1 << 16) | divisor2; - return 0; -} - -#endif - -