1/*
2    comedi/drivers/dt3000.c
3    Data Translation DT3000 series driver
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1999 David A. Schleef <ds@schleef.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17*/
18/*
19Driver: dt3000
20Description: Data Translation DT3000 series
21Author: ds
22Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
23  DT3003-PGL, DT3004, DT3005, DT3004-200
24Updated: Mon, 14 Apr 2008 15:41:24 +0100
25Status: works
26
27Configuration Options: not applicable, uses PCI auto config
28
29There is code to support AI commands, but it may not work.
30
31AO commands are not supported.
32*/
33
34/*
35   The DT3000 series is Data Translation's attempt to make a PCI
36   data acquisition board.  The design of this series is very nice,
37   since each board has an on-board DSP (Texas Instruments TMS320C52).
38   However, a few details are a little annoying.  The boards lack
39   bus-mastering DMA, which eliminates them from serious work.
40   They also are not capable of autocalibration, which is a common
41   feature in modern hardware.  The default firmware is pretty bad,
42   making it nearly impossible to write an RT compatible driver.
43   It would make an interesting project to write a decent firmware
44   for these boards.
45
46   Data Translation originally wanted an NDA for the documentation
47   for the 3k series.  However, if you ask nicely, they might send
48   you the docs without one, also.
49*/
50
51#include <linux/module.h>
52#include <linux/pci.h>
53#include <linux/delay.h>
54#include <linux/interrupt.h>
55
56#include "../comedidev.h"
57
58#include "comedi_fc.h"
59
60static const struct comedi_lrange range_dt3000_ai = {
61	4, {
62		BIP_RANGE(10),
63		BIP_RANGE(5),
64		BIP_RANGE(2.5),
65		BIP_RANGE(1.25)
66	}
67};
68
69static const struct comedi_lrange range_dt3000_ai_pgl = {
70	4, {
71		BIP_RANGE(10),
72		BIP_RANGE(1),
73		BIP_RANGE(0.1),
74		BIP_RANGE(0.02)
75	}
76};
77
78enum dt3k_boardid {
79	BOARD_DT3001,
80	BOARD_DT3001_PGL,
81	BOARD_DT3002,
82	BOARD_DT3003,
83	BOARD_DT3003_PGL,
84	BOARD_DT3004,
85	BOARD_DT3005,
86};
87
88struct dt3k_boardtype {
89	const char *name;
90	int adchan;
91	int adbits;
92	int ai_speed;
93	const struct comedi_lrange *adrange;
94	int dachan;
95	int dabits;
96};
97
98static const struct dt3k_boardtype dt3k_boardtypes[] = {
99	[BOARD_DT3001] = {
100		.name		= "dt3001",
101		.adchan		= 16,
102		.adbits		= 12,
103		.adrange	= &range_dt3000_ai,
104		.ai_speed	= 3000,
105		.dachan		= 2,
106		.dabits		= 12,
107	},
108	[BOARD_DT3001_PGL] = {
109		.name		= "dt3001-pgl",
110		.adchan		= 16,
111		.adbits		= 12,
112		.adrange	= &range_dt3000_ai_pgl,
113		.ai_speed	= 3000,
114		.dachan		= 2,
115		.dabits		= 12,
116	},
117	[BOARD_DT3002] = {
118		.name		= "dt3002",
119		.adchan		= 32,
120		.adbits		= 12,
121		.adrange	= &range_dt3000_ai,
122		.ai_speed	= 3000,
123	},
124	[BOARD_DT3003] = {
125		.name		= "dt3003",
126		.adchan		= 64,
127		.adbits		= 12,
128		.adrange	= &range_dt3000_ai,
129		.ai_speed	= 3000,
130		.dachan		= 2,
131		.dabits		= 12,
132	},
133	[BOARD_DT3003_PGL] = {
134		.name		= "dt3003-pgl",
135		.adchan		= 64,
136		.adbits		= 12,
137		.adrange	= &range_dt3000_ai_pgl,
138		.ai_speed	= 3000,
139		.dachan		= 2,
140		.dabits		= 12,
141	},
142	[BOARD_DT3004] = {
143		.name		= "dt3004",
144		.adchan		= 16,
145		.adbits		= 16,
146		.adrange	= &range_dt3000_ai,
147		.ai_speed	= 10000,
148		.dachan		= 2,
149		.dabits		= 12,
150	},
151	[BOARD_DT3005] = {
152		.name		= "dt3005",	/* a.k.a. 3004-200 */
153		.adchan		= 16,
154		.adbits		= 16,
155		.adrange	= &range_dt3000_ai,
156		.ai_speed	= 5000,
157		.dachan		= 2,
158		.dabits		= 12,
159	},
160};
161
162/* dual-ported RAM location definitions */
163
164#define DPR_DAC_buffer		(4*0x000)
165#define DPR_ADC_buffer		(4*0x800)
166#define DPR_Command		(4*0xfd3)
167#define DPR_SubSys		(4*0xfd3)
168#define DPR_Encode		(4*0xfd4)
169#define DPR_Params(a)		(4*(0xfd5+(a)))
170#define DPR_Tick_Reg_Lo		(4*0xff5)
171#define DPR_Tick_Reg_Hi		(4*0xff6)
172#define DPR_DA_Buf_Front	(4*0xff7)
173#define DPR_DA_Buf_Rear		(4*0xff8)
174#define DPR_AD_Buf_Front	(4*0xff9)
175#define DPR_AD_Buf_Rear		(4*0xffa)
176#define DPR_Int_Mask		(4*0xffb)
177#define DPR_Intr_Flag		(4*0xffc)
178#define DPR_Response_Mbx	(4*0xffe)
179#define DPR_Command_Mbx		(4*0xfff)
180
181#define AI_FIFO_DEPTH	2003
182#define AO_FIFO_DEPTH	2048
183
184/* command list */
185
186#define CMD_GETBRDINFO		0
187#define CMD_CONFIG		1
188#define CMD_GETCONFIG		2
189#define CMD_START		3
190#define CMD_STOP		4
191#define CMD_READSINGLE		5
192#define CMD_WRITESINGLE		6
193#define CMD_CALCCLOCK		7
194#define CMD_READEVENTS		8
195#define CMD_WRITECTCTRL		16
196#define CMD_READCTCTRL		17
197#define CMD_WRITECT		18
198#define CMD_READCT		19
199#define CMD_WRITEDATA		32
200#define CMD_READDATA		33
201#define CMD_WRITEIO		34
202#define CMD_READIO		35
203#define CMD_WRITECODE		36
204#define CMD_READCODE		37
205#define CMD_EXECUTE		38
206#define CMD_HALT		48
207
208#define SUBS_AI		0
209#define SUBS_AO		1
210#define SUBS_DIN	2
211#define SUBS_DOUT	3
212#define SUBS_MEM	4
213#define SUBS_CT		5
214
215/* interrupt flags */
216#define DT3000_CMDONE		0x80
217#define DT3000_CTDONE		0x40
218#define DT3000_DAHWERR		0x20
219#define DT3000_DASWERR		0x10
220#define DT3000_DAEMPTY		0x08
221#define DT3000_ADHWERR		0x04
222#define DT3000_ADSWERR		0x02
223#define DT3000_ADFULL		0x01
224
225#define DT3000_COMPLETION_MASK	0xff00
226#define DT3000_COMMAND_MASK	0x00ff
227#define DT3000_NOTPROCESSED	0x0000
228#define DT3000_NOERROR		0x5500
229#define DT3000_ERROR		0xaa00
230#define DT3000_NOTSUPPORTED	0xff00
231
232#define DT3000_EXTERNAL_CLOCK	1
233#define DT3000_RISING_EDGE	2
234
235#define TMODE_MASK		0x1c
236
237#define DT3000_AD_TRIG_INTERNAL		(0<<2)
238#define DT3000_AD_TRIG_EXTERNAL		(1<<2)
239#define DT3000_AD_RETRIG_INTERNAL	(2<<2)
240#define DT3000_AD_RETRIG_EXTERNAL	(3<<2)
241#define DT3000_AD_EXTRETRIG		(4<<2)
242
243#define DT3000_CHANNEL_MODE_SE		0
244#define DT3000_CHANNEL_MODE_DI		1
245
246struct dt3k_private {
247	unsigned int lock;
248	unsigned int ai_front;
249	unsigned int ai_rear;
250};
251
252#define TIMEOUT 100
253
254static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
255{
256	int i;
257	unsigned int status = 0;
258
259	writew(cmd, dev->mmio + DPR_Command_Mbx);
260
261	for (i = 0; i < TIMEOUT; i++) {
262		status = readw(dev->mmio + DPR_Command_Mbx);
263		if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
264			break;
265		udelay(1);
266	}
267
268	if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR)
269		dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
270			__func__, status);
271}
272
273static unsigned int dt3k_readsingle(struct comedi_device *dev,
274				    unsigned int subsys, unsigned int chan,
275				    unsigned int gain)
276{
277	writew(subsys, dev->mmio + DPR_SubSys);
278
279	writew(chan, dev->mmio + DPR_Params(0));
280	writew(gain, dev->mmio + DPR_Params(1));
281
282	dt3k_send_cmd(dev, CMD_READSINGLE);
283
284	return readw(dev->mmio + DPR_Params(2));
285}
286
287static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
288			     unsigned int chan, unsigned int data)
289{
290	writew(subsys, dev->mmio + DPR_SubSys);
291
292	writew(chan, dev->mmio + DPR_Params(0));
293	writew(0, dev->mmio + DPR_Params(1));
294	writew(data, dev->mmio + DPR_Params(2));
295
296	dt3k_send_cmd(dev, CMD_WRITESINGLE);
297}
298
299static void dt3k_ai_empty_fifo(struct comedi_device *dev,
300			       struct comedi_subdevice *s)
301{
302	struct dt3k_private *devpriv = dev->private;
303	int front;
304	int rear;
305	int count;
306	int i;
307	unsigned short data;
308
309	front = readw(dev->mmio + DPR_AD_Buf_Front);
310	count = front - devpriv->ai_front;
311	if (count < 0)
312		count += AI_FIFO_DEPTH;
313
314	rear = devpriv->ai_rear;
315
316	for (i = 0; i < count; i++) {
317		data = readw(dev->mmio + DPR_ADC_buffer + rear);
318		comedi_buf_put(s, data);
319		rear++;
320		if (rear >= AI_FIFO_DEPTH)
321			rear = 0;
322	}
323
324	devpriv->ai_rear = rear;
325	writew(rear, dev->mmio + DPR_AD_Buf_Rear);
326}
327
328static int dt3k_ai_cancel(struct comedi_device *dev,
329			  struct comedi_subdevice *s)
330{
331	writew(SUBS_AI, dev->mmio + DPR_SubSys);
332	dt3k_send_cmd(dev, CMD_STOP);
333
334	writew(0, dev->mmio + DPR_Int_Mask);
335
336	return 0;
337}
338
339static int debug_n_ints;
340
341/* FIXME! Assumes shared interrupt is for this card. */
342/* What's this debug_n_ints stuff? Obviously needs some work... */
343static irqreturn_t dt3k_interrupt(int irq, void *d)
344{
345	struct comedi_device *dev = d;
346	struct comedi_subdevice *s = dev->read_subdev;
347	unsigned int status;
348
349	if (!dev->attached)
350		return IRQ_NONE;
351
352	status = readw(dev->mmio + DPR_Intr_Flag);
353
354	if (status & DT3000_ADFULL) {
355		dt3k_ai_empty_fifo(dev, s);
356		s->async->events |= COMEDI_CB_BLOCK;
357	}
358
359	if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
360		s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
361
362	debug_n_ints++;
363	if (debug_n_ints >= 10)
364		s->async->events |= COMEDI_CB_EOA;
365
366	cfc_handle_events(dev, s);
367	return IRQ_HANDLED;
368}
369
370static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
371			    unsigned int flags)
372{
373	int divider, base, prescale;
374
375	/* This function needs improvment */
376	/* Don't know if divider==0 works. */
377
378	for (prescale = 0; prescale < 16; prescale++) {
379		base = timer_base * (prescale + 1);
380		switch (flags & CMDF_ROUND_MASK) {
381		case CMDF_ROUND_NEAREST:
382		default:
383			divider = (*nanosec + base / 2) / base;
384			break;
385		case CMDF_ROUND_DOWN:
386			divider = (*nanosec) / base;
387			break;
388		case CMDF_ROUND_UP:
389			divider = (*nanosec) / base;
390			break;
391		}
392		if (divider < 65536) {
393			*nanosec = divider * base;
394			return (prescale << 16) | (divider);
395		}
396	}
397
398	prescale = 15;
399	base = timer_base * (1 << prescale);
400	divider = 65535;
401	*nanosec = divider * base;
402	return (prescale << 16) | (divider);
403}
404
405static int dt3k_ai_cmdtest(struct comedi_device *dev,
406			   struct comedi_subdevice *s, struct comedi_cmd *cmd)
407{
408	const struct dt3k_boardtype *this_board = dev->board_ptr;
409	int err = 0;
410	unsigned int arg;
411
412	/* Step 1 : check if triggers are trivially valid */
413
414	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
415	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
416	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
417	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
418	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
419
420	if (err)
421		return 1;
422
423	/* Step 2a : make sure trigger sources are unique */
424	/* Step 2b : and mutually compatible */
425
426	/* Step 3: check if arguments are trivially valid */
427
428	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
429
430	if (cmd->scan_begin_src == TRIG_TIMER) {
431		err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
432						 this_board->ai_speed);
433		err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
434						 100 * 16 * 65535);
435	}
436
437	if (cmd->convert_src == TRIG_TIMER) {
438		err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
439						 this_board->ai_speed);
440		err |= cfc_check_trigger_arg_max(&cmd->convert_arg,
441						 50 * 16 * 65535);
442	}
443
444	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
445
446	if (cmd->stop_src == TRIG_COUNT)
447		err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
448	else	/* TRIG_NONE */
449		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
450
451	if (err)
452		return 3;
453
454	/* step 4: fix up any arguments */
455
456	if (cmd->scan_begin_src == TRIG_TIMER) {
457		arg = cmd->scan_begin_arg;
458		dt3k_ns_to_timer(100, &arg, cmd->flags);
459		err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
460	}
461
462	if (cmd->convert_src == TRIG_TIMER) {
463		arg = cmd->convert_arg;
464		dt3k_ns_to_timer(50, &arg, cmd->flags);
465		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
466
467		if (cmd->scan_begin_src == TRIG_TIMER) {
468			arg = cmd->convert_arg * cmd->scan_end_arg;
469			err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
470							 arg);
471		}
472	}
473
474	if (err)
475		return 4;
476
477	return 0;
478}
479
480static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
481{
482	struct comedi_cmd *cmd = &s->async->cmd;
483	int i;
484	unsigned int chan, range, aref;
485	unsigned int divider;
486	unsigned int tscandiv;
487
488	for (i = 0; i < cmd->chanlist_len; i++) {
489		chan = CR_CHAN(cmd->chanlist[i]);
490		range = CR_RANGE(cmd->chanlist[i]);
491
492		writew((range << 6) | chan, dev->mmio + DPR_ADC_buffer + i);
493	}
494	aref = CR_AREF(cmd->chanlist[0]);
495
496	writew(cmd->scan_end_arg, dev->mmio + DPR_Params(0));
497
498	if (cmd->convert_src == TRIG_TIMER) {
499		divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
500		writew((divider >> 16), dev->mmio + DPR_Params(1));
501		writew((divider & 0xffff), dev->mmio + DPR_Params(2));
502	}
503
504	if (cmd->scan_begin_src == TRIG_TIMER) {
505		tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
506					    cmd->flags);
507		writew((tscandiv >> 16), dev->mmio + DPR_Params(3));
508		writew((tscandiv & 0xffff), dev->mmio + DPR_Params(4));
509	}
510
511	writew(DT3000_AD_RETRIG_INTERNAL, dev->mmio + DPR_Params(5));
512	writew(aref == AREF_DIFF, dev->mmio + DPR_Params(6));
513
514	writew(AI_FIFO_DEPTH / 2, dev->mmio + DPR_Params(7));
515
516	writew(SUBS_AI, dev->mmio + DPR_SubSys);
517	dt3k_send_cmd(dev, CMD_CONFIG);
518
519	writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
520	       dev->mmio + DPR_Int_Mask);
521
522	debug_n_ints = 0;
523
524	writew(SUBS_AI, dev->mmio + DPR_SubSys);
525	dt3k_send_cmd(dev, CMD_START);
526
527	return 0;
528}
529
530static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
531			struct comedi_insn *insn, unsigned int *data)
532{
533	int i;
534	unsigned int chan, gain, aref;
535
536	chan = CR_CHAN(insn->chanspec);
537	gain = CR_RANGE(insn->chanspec);
538	/* XXX docs don't explain how to select aref */
539	aref = CR_AREF(insn->chanspec);
540
541	for (i = 0; i < insn->n; i++)
542		data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
543
544	return i;
545}
546
547static int dt3k_ao_insn_write(struct comedi_device *dev,
548			      struct comedi_subdevice *s,
549			      struct comedi_insn *insn,
550			      unsigned int *data)
551{
552	unsigned int chan = CR_CHAN(insn->chanspec);
553	unsigned int val = s->readback[chan];
554	int i;
555
556	for (i = 0; i < insn->n; i++) {
557		val = data[i];
558		dt3k_writesingle(dev, SUBS_AO, chan, val);
559	}
560	s->readback[chan] = val;
561
562	return insn->n;
563}
564
565static void dt3k_dio_config(struct comedi_device *dev, int bits)
566{
567	/* XXX */
568	writew(SUBS_DOUT, dev->mmio + DPR_SubSys);
569
570	writew(bits, dev->mmio + DPR_Params(0));
571#if 0
572	/* don't know */
573	writew(0, dev->mmio + DPR_Params(1));
574	writew(0, dev->mmio + DPR_Params(2));
575#endif
576
577	dt3k_send_cmd(dev, CMD_CONFIG);
578}
579
580static int dt3k_dio_insn_config(struct comedi_device *dev,
581				struct comedi_subdevice *s,
582				struct comedi_insn *insn,
583				unsigned int *data)
584{
585	unsigned int chan = CR_CHAN(insn->chanspec);
586	unsigned int mask;
587	int ret;
588
589	if (chan < 4)
590		mask = 0x0f;
591	else
592		mask = 0xf0;
593
594	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
595	if (ret)
596		return ret;
597
598	dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
599
600	return insn->n;
601}
602
603static int dt3k_dio_insn_bits(struct comedi_device *dev,
604			      struct comedi_subdevice *s,
605			      struct comedi_insn *insn,
606			      unsigned int *data)
607{
608	if (comedi_dio_update_state(s, data))
609		dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
610
611	data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
612
613	return insn->n;
614}
615
616static int dt3k_mem_insn_read(struct comedi_device *dev,
617			      struct comedi_subdevice *s,
618			      struct comedi_insn *insn,
619			      unsigned int *data)
620{
621	unsigned int addr = CR_CHAN(insn->chanspec);
622	int i;
623
624	for (i = 0; i < insn->n; i++) {
625		writew(SUBS_MEM, dev->mmio + DPR_SubSys);
626		writew(addr, dev->mmio + DPR_Params(0));
627		writew(1, dev->mmio + DPR_Params(1));
628
629		dt3k_send_cmd(dev, CMD_READCODE);
630
631		data[i] = readw(dev->mmio + DPR_Params(2));
632	}
633
634	return i;
635}
636
637static int dt3000_auto_attach(struct comedi_device *dev,
638			      unsigned long context)
639{
640	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
641	const struct dt3k_boardtype *this_board = NULL;
642	struct dt3k_private *devpriv;
643	struct comedi_subdevice *s;
644	int ret = 0;
645
646	if (context < ARRAY_SIZE(dt3k_boardtypes))
647		this_board = &dt3k_boardtypes[context];
648	if (!this_board)
649		return -ENODEV;
650	dev->board_ptr = this_board;
651	dev->board_name = this_board->name;
652
653	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
654	if (!devpriv)
655		return -ENOMEM;
656
657	ret = comedi_pci_enable(dev);
658	if (ret < 0)
659		return ret;
660
661	dev->mmio = pci_ioremap_bar(pcidev, 0);
662	if (!dev->mmio)
663		return -ENOMEM;
664
665	if (pcidev->irq) {
666		ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
667				  dev->board_name, dev);
668		if (ret == 0)
669			dev->irq = pcidev->irq;
670	}
671
672	ret = comedi_alloc_subdevices(dev, 4);
673	if (ret)
674		return ret;
675
676	s = &dev->subdevices[0];
677	/* ai subdevice */
678	s->type		= COMEDI_SUBD_AI;
679	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
680	s->n_chan	= this_board->adchan;
681	s->insn_read	= dt3k_ai_insn;
682	s->maxdata	= (1 << this_board->adbits) - 1;
683	s->range_table	= &range_dt3000_ai;	/* XXX */
684	if (dev->irq) {
685		dev->read_subdev = s;
686		s->subdev_flags	|= SDF_CMD_READ;
687		s->len_chanlist	= 512;
688		s->do_cmd	= dt3k_ai_cmd;
689		s->do_cmdtest	= dt3k_ai_cmdtest;
690		s->cancel	= dt3k_ai_cancel;
691	}
692
693	s = &dev->subdevices[1];
694	/* ao subsystem */
695	s->type		= COMEDI_SUBD_AO;
696	s->subdev_flags	= SDF_WRITABLE;
697	s->n_chan	= 2;
698	s->maxdata	= (1 << this_board->dabits) - 1;
699	s->len_chanlist	= 1;
700	s->range_table	= &range_bipolar10;
701	s->insn_write	= dt3k_ao_insn_write;
702	s->insn_read	= comedi_readback_insn_read;
703
704	ret = comedi_alloc_subdev_readback(s);
705	if (ret)
706		return ret;
707
708	s = &dev->subdevices[2];
709	/* dio subsystem */
710	s->type		= COMEDI_SUBD_DIO;
711	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
712	s->n_chan	= 8;
713	s->insn_config	= dt3k_dio_insn_config;
714	s->insn_bits	= dt3k_dio_insn_bits;
715	s->maxdata	= 1;
716	s->len_chanlist	= 8;
717	s->range_table	= &range_digital;
718
719	s = &dev->subdevices[3];
720	/* mem subsystem */
721	s->type		= COMEDI_SUBD_MEMORY;
722	s->subdev_flags	= SDF_READABLE;
723	s->n_chan	= 0x1000;
724	s->insn_read	= dt3k_mem_insn_read;
725	s->maxdata	= 0xff;
726	s->len_chanlist	= 1;
727	s->range_table	= &range_unknown;
728
729#if 0
730	s = &dev->subdevices[4];
731	/* proc subsystem */
732	s->type = COMEDI_SUBD_PROC;
733#endif
734
735	return 0;
736}
737
738static struct comedi_driver dt3000_driver = {
739	.driver_name	= "dt3000",
740	.module		= THIS_MODULE,
741	.auto_attach	= dt3000_auto_attach,
742	.detach		= comedi_pci_detach,
743};
744
745static int dt3000_pci_probe(struct pci_dev *dev,
746			    const struct pci_device_id *id)
747{
748	return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
749}
750
751static const struct pci_device_id dt3000_pci_table[] = {
752	{ PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
753	{ PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
754	{ PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
755	{ PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
756	{ PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
757	{ PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
758	{ PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
759	{ 0 }
760};
761MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
762
763static struct pci_driver dt3000_pci_driver = {
764	.name		= "dt3000",
765	.id_table	= dt3000_pci_table,
766	.probe		= dt3000_pci_probe,
767	.remove		= comedi_pci_auto_unconfig,
768};
769module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
770
771MODULE_AUTHOR("Comedi http://www.comedi.org");
772MODULE_DESCRIPTION("Comedi low-level driver");
773MODULE_LICENSE("GPL");
774