/* * comedi/drivers/dt2801.c * Device Driver for DataTranslation DT2801 * */ /* Driver: dt2801 Description: Data Translation DT2801 series and DT01-EZ Author: ds Status: works Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A, DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ This driver can autoprobe the type of board. Configuration options: [0] - I/O port base address [1] - unused [2] - A/D reference 0=differential, 1=single-ended [3] - A/D range 0 = [-10, 10] 1 = [0,10] [4] - D/A 0 range 0 = [-10, 10] 1 = [-5,5] 2 = [-2.5,2.5] 3 = [0,10] 4 = [0,5] [5] - D/A 1 range (same choices) */ #include "../comedidev.h" #include #include #define DT2801_TIMEOUT 1000 /* Hardware Configuration */ /* ====================== */ #define DT2801_MAX_DMA_SIZE (64 * 1024) /* Ports */ #define DT2801_IOSIZE 2 /* define's */ /* ====================== */ /* Commands */ #define DT_C_RESET 0x0 #define DT_C_CLEAR_ERR 0x1 #define DT_C_READ_ERRREG 0x2 #define DT_C_SET_CLOCK 0x3 #define DT_C_TEST 0xb #define DT_C_STOP 0xf #define DT_C_SET_DIGIN 0x4 #define DT_C_SET_DIGOUT 0x5 #define DT_C_READ_DIG 0x6 #define DT_C_WRITE_DIG 0x7 #define DT_C_WRITE_DAIM 0x8 #define DT_C_SET_DA 0x9 #define DT_C_WRITE_DA 0xa #define DT_C_READ_ADIM 0xc #define DT_C_SET_AD 0xd #define DT_C_READ_AD 0xe /* Command modifiers (only used with read/write), EXTTRIG can be used with some other commands. */ #define DT_MOD_DMA (1<<4) #define DT_MOD_CONT (1<<5) #define DT_MOD_EXTCLK (1<<6) #define DT_MOD_EXTTRIG (1<<7) /* Bits in status register */ #define DT_S_DATA_OUT_READY (1<<0) #define DT_S_DATA_IN_FULL (1<<1) #define DT_S_READY (1<<2) #define DT_S_COMMAND (1<<3) #define DT_S_COMPOSITE_ERROR (1<<7) /* registers */ #define DT2801_DATA 0 #define DT2801_STATUS 1 #define DT2801_CMD 1 #if 0 /* ignore 'defined but not used' warning */ static const struct comedi_lrange range_dt2801_ai_pgh_bipolar = { 4, { RANGE(-10, 10), RANGE(-5, 5), RANGE (-2.5, 2.5), RANGE (-1.25, 1.25), } }; #endif static const struct comedi_lrange range_dt2801_ai_pgl_bipolar = { 4, { RANGE(-10, 10), RANGE(-1, 1), RANGE (-0.1, 0.1), RANGE (-0.02, 0.02), } }; #if 0 /* ignore 'defined but not used' warning */ static const struct comedi_lrange range_dt2801_ai_pgh_unipolar = { 4, { RANGE(0, 10), RANGE(0, 5), RANGE(0, 2.5), RANGE(0, 1.25), } }; #endif static const struct comedi_lrange range_dt2801_ai_pgl_unipolar = { 4, { RANGE(0, 10), RANGE(0, 1), RANGE(0, 0.1), RANGE(0, 0.02), } }; struct dt2801_board { const char *name; int boardcode; int ad_diff; int ad_chan; int adbits; int adrangetype; int dabits; }; /* Typeid's for the different boards of the DT2801-series (taken from the test-software, that comes with the board) */ static const struct dt2801_board boardtypes[] = { { .name = "dt2801", .boardcode = 0x09, .ad_diff = 2, .ad_chan = 16, .adbits = 12, .adrangetype = 0, .dabits = 12}, { .name = "dt2801-a", .boardcode = 0x52, .ad_diff = 2, .ad_chan = 16, .adbits = 12, .adrangetype = 0, .dabits = 12}, { .name = "dt2801/5716a", .boardcode = 0x82, .ad_diff = 1, .ad_chan = 16, .adbits = 16, .adrangetype = 1, .dabits = 12}, { .name = "dt2805", .boardcode = 0x12, .ad_diff = 1, .ad_chan = 16, .adbits = 12, .adrangetype = 0, .dabits = 12}, { .name = "dt2805/5716a", .boardcode = 0x92, .ad_diff = 1, .ad_chan = 16, .adbits = 16, .adrangetype = 1, .dabits = 12}, { .name = "dt2808", .boardcode = 0x20, .ad_diff = 0, .ad_chan = 16, .adbits = 12, .adrangetype = 2, .dabits = 8}, { .name = "dt2818", .boardcode = 0xa2, .ad_diff = 0, .ad_chan = 4, .adbits = 12, .adrangetype = 0, .dabits = 12}, { .name = "dt2809", .boardcode = 0xb0, .ad_diff = 0, .ad_chan = 8, .adbits = 12, .adrangetype = 1, .dabits = 12}, }; struct dt2801_private { const struct comedi_lrange *dac_range_types[2]; unsigned int ao_readback[2]; }; /* These are the low-level routines: writecommand: write a command to the board writedata: write data byte readdata: read data byte */ /* Only checks DataOutReady-flag, not the Ready-flag as it is done in the examples of the manual. I don't see why this should be necessary. */ static int dt2801_readdata(struct comedi_device *dev, int *data) { int stat = 0; int timeout = DT2801_TIMEOUT; do { stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY)) return stat; if (stat & DT_S_DATA_OUT_READY) { *data = inb_p(dev->iobase + DT2801_DATA); return 0; } } while (--timeout > 0); return -ETIME; } static int dt2801_readdata2(struct comedi_device *dev, int *data) { int lb, hb; int ret; ret = dt2801_readdata(dev, &lb); if (ret) return ret; ret = dt2801_readdata(dev, &hb); if (ret) return ret; *data = (hb << 8) + lb; return 0; } static int dt2801_writedata(struct comedi_device *dev, unsigned int data) { int stat = 0; int timeout = DT2801_TIMEOUT; do { stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) return stat; if (!(stat & DT_S_DATA_IN_FULL)) { outb_p(data & 0xff, dev->iobase + DT2801_DATA); return 0; } #if 0 if (stat & DT_S_READY) { printk ("dt2801: ready flag set (bad!) in dt2801_writedata()\n"); return -EIO; } #endif } while (--timeout > 0); return -ETIME; } static int dt2801_writedata2(struct comedi_device *dev, unsigned int data) { int ret; ret = dt2801_writedata(dev, data & 0xff); if (ret < 0) return ret; ret = dt2801_writedata(dev, (data >> 8)); if (ret < 0) return ret; return 0; } static int dt2801_wait_for_ready(struct comedi_device *dev) { int timeout = DT2801_TIMEOUT; int stat; stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_READY) return 0; do { stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) return stat; if (stat & DT_S_READY) return 0; } while (--timeout > 0); return -ETIME; } static int dt2801_writecmd(struct comedi_device *dev, int command) { int stat; dt2801_wait_for_ready(dev); stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_COMPOSITE_ERROR) { printk ("dt2801: composite-error in dt2801_writecmd(), ignoring\n"); } if (!(stat & DT_S_READY)) printk("dt2801: !ready in dt2801_writecmd(), ignoring\n"); outb_p(command, dev->iobase + DT2801_CMD); return 0; } static int dt2801_reset(struct comedi_device *dev) { int board_code = 0; unsigned int stat; int timeout; DPRINTK("dt2801: resetting board...\n"); DPRINTK("fingerprint: 0x%02x 0x%02x\n", inb_p(dev->iobase), inb_p(dev->iobase + 1)); /* pull random data from data port */ inb_p(dev->iobase + DT2801_DATA); inb_p(dev->iobase + DT2801_DATA); inb_p(dev->iobase + DT2801_DATA); inb_p(dev->iobase + DT2801_DATA); DPRINTK("dt2801: stop\n"); /* dt2801_writecmd(dev,DT_C_STOP); */ outb_p(DT_C_STOP, dev->iobase + DT2801_CMD); /* dt2801_wait_for_ready(dev); */ udelay(100); timeout = 10000; do { stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_READY) break; } while (timeout--); if (!timeout) printk("dt2801: timeout 1 status=0x%02x\n", stat); /* printk("dt2801: reading dummy\n"); */ /* dt2801_readdata(dev,&board_code); */ DPRINTK("dt2801: reset\n"); outb_p(DT_C_RESET, dev->iobase + DT2801_CMD); /* dt2801_writecmd(dev,DT_C_RESET); */ udelay(100); timeout = 10000; do { stat = inb_p(dev->iobase + DT2801_STATUS); if (stat & DT_S_READY) break; } while (timeout--); if (!timeout) printk("dt2801: timeout 2 status=0x%02x\n", stat); DPRINTK("dt2801: reading code\n"); dt2801_readdata(dev, &board_code); DPRINTK("dt2801: ok. code=0x%02x\n", board_code); return board_code; } static int probe_number_of_ai_chans(struct comedi_device *dev) { int n_chans; int stat; int data; for (n_chans = 0; n_chans < 16; n_chans++) { stat = dt2801_writecmd(dev, DT_C_READ_ADIM); dt2801_writedata(dev, 0); dt2801_writedata(dev, n_chans); stat = dt2801_readdata2(dev, &data); if (stat) break; } dt2801_reset(dev); dt2801_reset(dev); return n_chans; } static const struct comedi_lrange *dac_range_table[] = { &range_bipolar10, &range_bipolar5, &range_bipolar2_5, &range_unipolar10, &range_unipolar5 }; static const struct comedi_lrange *dac_range_lkup(int opt) { if (opt < 0 || opt >= 5) return &range_unknown; return dac_range_table[opt]; } static const struct comedi_lrange *ai_range_lkup(int type, int opt) { switch (type) { case 0: return (opt) ? &range_dt2801_ai_pgl_unipolar : &range_dt2801_ai_pgl_bipolar; case 1: return (opt) ? &range_unipolar10 : &range_bipolar10; case 2: return &range_unipolar5; } return &range_unknown; } static int dt2801_error(struct comedi_device *dev, int stat) { if (stat < 0) { if (stat == -ETIME) printk("dt2801: timeout\n"); else printk("dt2801: error %d\n", stat); return stat; } printk("dt2801: error status 0x%02x, resetting...\n", stat); dt2801_reset(dev); dt2801_reset(dev); return -EIO; } static int dt2801_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { int d; int stat; int i; for (i = 0; i < insn->n; i++) { stat = dt2801_writecmd(dev, DT_C_READ_ADIM); dt2801_writedata(dev, CR_RANGE(insn->chanspec)); dt2801_writedata(dev, CR_CHAN(insn->chanspec)); stat = dt2801_readdata2(dev, &d); if (stat != 0) return dt2801_error(dev, stat); data[i] = d; } return i; } static int dt2801_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { struct dt2801_private *devpriv = dev->private; data[0] = devpriv->ao_readback[CR_CHAN(insn->chanspec)]; return 1; } static int dt2801_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { struct dt2801_private *devpriv = dev->private; dt2801_writecmd(dev, DT_C_WRITE_DAIM); dt2801_writedata(dev, CR_CHAN(insn->chanspec)); dt2801_writedata2(dev, data[0]); devpriv->ao_readback[CR_CHAN(insn->chanspec)] = data[0]; return 1; } static int dt2801_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { int which = 0; if (s == &dev->subdevices[3]) which = 1; if (data[0]) { s->state &= ~data[0]; s->state |= (data[0] & data[1]); dt2801_writecmd(dev, DT_C_WRITE_DIG); dt2801_writedata(dev, which); dt2801_writedata(dev, s->state); } dt2801_writecmd(dev, DT_C_READ_DIG); dt2801_writedata(dev, which); dt2801_readdata(dev, data + 1); return insn->n; } static int dt2801_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { int which = 0; if (s == &dev->subdevices[3]) which = 1; /* configure */ switch (data[0]) { case INSN_CONFIG_DIO_OUTPUT: s->io_bits = 0xff; dt2801_writecmd(dev, DT_C_SET_DIGOUT); break; case INSN_CONFIG_DIO_INPUT: s->io_bits = 0; dt2801_writecmd(dev, DT_C_SET_DIGIN); break; case INSN_CONFIG_DIO_QUERY: data[1] = s->io_bits ? COMEDI_OUTPUT : COMEDI_INPUT; return insn->n; default: return -EINVAL; } dt2801_writedata(dev, which); return 1; } /* options: [0] - i/o base [1] - unused [2] - a/d 0=differential, 1=single-ended [3] - a/d range 0=[-10,10], 1=[0,10] [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] */ static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it) { const struct dt2801_board *board = comedi_board(dev); struct dt2801_private *devpriv; struct comedi_subdevice *s; int board_code, type; int ret = 0; int n_ai_chans; ret = comedi_request_region(dev, it->options[0], DT2801_IOSIZE); if (ret) return ret; /* do some checking */ board_code = dt2801_reset(dev); /* heh. if it didn't work, try it again. */ if (!board_code) board_code = dt2801_reset(dev); for (type = 0; type < ARRAY_SIZE(boardtypes); type++) { if (boardtypes[type].boardcode == board_code) goto havetype; } printk("dt2801: unrecognized board code=0x%02x, contact author\n", board_code); type = 0; havetype: dev->board_ptr = boardtypes + type; board = comedi_board(dev); n_ai_chans = probe_number_of_ai_chans(dev); ret = comedi_alloc_subdevices(dev, 4); if (ret) goto out; devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); if (!devpriv) return -ENOMEM; dev->private = devpriv; dev->board_name = board->name; s = &dev->subdevices[0]; /* ai subdevice */ s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE | SDF_GROUND; #if 1 s->n_chan = n_ai_chans; #else if (it->options[2]) s->n_chan = board->ad_chan; else s->n_chan = board->ad_chan / 2; #endif s->maxdata = (1 << board->adbits) - 1; s->range_table = ai_range_lkup(board->adrangetype, it->options[3]); s->insn_read = dt2801_ai_insn_read; s = &dev->subdevices[1]; /* ao subdevice */ s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_WRITABLE; s->n_chan = 2; s->maxdata = (1 << board->dabits) - 1; s->range_table_list = devpriv->dac_range_types; devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]); devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]); s->insn_read = dt2801_ao_insn_read; s->insn_write = dt2801_ao_insn_write; s = &dev->subdevices[2]; /* 1st digital subdevice */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE; s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = dt2801_dio_insn_bits; s->insn_config = dt2801_dio_insn_config; s = &dev->subdevices[3]; /* 2nd digital subdevice */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE; s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = dt2801_dio_insn_bits; s->insn_config = dt2801_dio_insn_config; ret = 0; out: return ret; } static struct comedi_driver dt2801_driver = { .driver_name = "dt2801", .module = THIS_MODULE, .attach = dt2801_attach, .detach = comedi_legacy_detach, }; module_comedi_driver(dt2801_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL");