staging: comedi: remove inline alloc_private()
[linux-2.6-block.git] / drivers / staging / comedi / drivers / dmm32at.c
1 /*
2     comedi/drivers/dmm32at.c
3     Diamond Systems mm32at code for a Comedi driver
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000 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     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 /*
24 Driver: dmm32at
25 Description: Diamond Systems mm32at driver.
26 Devices:
27 Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
28 Updated: Fri Jun  4 09:13:24 CDT 2004
29 Status: experimental
30
31 This driver is for the Diamond Systems MM-32-AT board
32 http://www.diamondsystems.com/products/diamondmm32at It is being used
33 on serveral projects inside NASA, without problems so far. For analog
34 input commands, TRIG_EXT is not yet supported at all..
35
36 Configuration Options:
37   comedi_config /dev/comedi0 dmm32at baseaddr,irq
38 */
39
40 #include <linux/interrupt.h>
41 #include "../comedidev.h"
42 #include <linux/ioport.h>
43
44 #include "comedi_fc.h"
45
46 /* Board register addresses */
47
48 #define DMM32AT_MEMSIZE 0x10
49
50 #define DMM32AT_CONV 0x00
51 #define DMM32AT_AILSB 0x00
52 #define DMM32AT_AUXDOUT 0x01
53 #define DMM32AT_AIMSB 0x01
54 #define DMM32AT_AILOW 0x02
55 #define DMM32AT_AIHIGH 0x03
56
57 #define DMM32AT_DACLSB 0x04
58 #define DMM32AT_DACSTAT 0x04
59 #define DMM32AT_DACMSB 0x05
60
61 #define DMM32AT_FIFOCNTRL 0x07
62 #define DMM32AT_FIFOSTAT 0x07
63
64 #define DMM32AT_CNTRL 0x08
65 #define DMM32AT_AISTAT 0x08
66
67 #define DMM32AT_INTCLOCK 0x09
68
69 #define DMM32AT_CNTRDIO 0x0a
70
71 #define DMM32AT_AICONF 0x0b
72 #define DMM32AT_AIRBACK 0x0b
73
74 #define DMM32AT_CLK1 0x0d
75 #define DMM32AT_CLK2 0x0e
76 #define DMM32AT_CLKCT 0x0f
77
78 #define DMM32AT_DIOA 0x0c
79 #define DMM32AT_DIOB 0x0d
80 #define DMM32AT_DIOC 0x0e
81 #define DMM32AT_DIOCONF 0x0f
82
83 /* Board register values. */
84
85 /* DMM32AT_DACSTAT 0x04 */
86 #define DMM32AT_DACBUSY 0x80
87
88 /* DMM32AT_FIFOCNTRL 0x07 */
89 #define DMM32AT_FIFORESET 0x02
90 #define DMM32AT_SCANENABLE 0x04
91
92 /* DMM32AT_CNTRL 0x08 */
93 #define DMM32AT_RESET 0x20
94 #define DMM32AT_INTRESET 0x08
95 #define DMM32AT_CLKACC 0x00
96 #define DMM32AT_DIOACC 0x01
97
98 /* DMM32AT_AISTAT 0x08 */
99 #define DMM32AT_STATUS 0x80
100
101 /* DMM32AT_INTCLOCK 0x09 */
102 #define DMM32AT_ADINT 0x80
103 #define DMM32AT_CLKSEL 0x03
104
105 /* DMM32AT_CNTRDIO 0x0a */
106 #define DMM32AT_FREQ12 0x80
107
108 /* DMM32AT_AICONF 0x0b */
109 #define DMM32AT_RANGE_U10 0x0c
110 #define DMM32AT_RANGE_U5 0x0d
111 #define DMM32AT_RANGE_B10 0x08
112 #define DMM32AT_RANGE_B5 0x00
113 #define DMM32AT_SCINT_20 0x00
114 #define DMM32AT_SCINT_15 0x10
115 #define DMM32AT_SCINT_10 0x20
116 #define DMM32AT_SCINT_5 0x30
117
118 /* DMM32AT_CLKCT 0x0f */
119 #define DMM32AT_CLKCT1 0x56     /* mode3 counter 1 - write low byte only */
120 #define DMM32AT_CLKCT2 0xb6     /*  mode3 counter 2 - write high and low byte */
121
122 /* DMM32AT_DIOCONF 0x0f */
123 #define DMM32AT_DIENABLE 0x80
124 #define DMM32AT_DIRA 0x10
125 #define DMM32AT_DIRB 0x02
126 #define DMM32AT_DIRCL 0x01
127 #define DMM32AT_DIRCH 0x08
128
129 /* board AI ranges in comedi structure */
130 static const struct comedi_lrange dmm32at_airanges = {
131         4,
132         {
133          UNI_RANGE(10),
134          UNI_RANGE(5),
135          BIP_RANGE(10),
136          BIP_RANGE(5),
137          }
138 };
139
140 /* register values for above ranges */
141 static const unsigned char dmm32at_rangebits[] = {
142         DMM32AT_RANGE_U10,
143         DMM32AT_RANGE_U5,
144         DMM32AT_RANGE_B10,
145         DMM32AT_RANGE_B5,
146 };
147
148 /* only one of these ranges is valid, as set by a jumper on the
149  * board. The application should only use the range set by the jumper
150  */
151 static const struct comedi_lrange dmm32at_aoranges = {
152         4,
153         {
154          UNI_RANGE(10),
155          UNI_RANGE(5),
156          BIP_RANGE(10),
157          BIP_RANGE(5),
158          }
159 };
160
161 struct dmm32at_private {
162
163         int data;
164         int ai_inuse;
165         unsigned int ai_scans_left;
166
167         /* Used for AO readback */
168         unsigned int ao_readback[4];
169         unsigned char dio_config;
170
171 };
172
173 static int dmm32at_ai_rinsn(struct comedi_device *dev,
174                             struct comedi_subdevice *s,
175                             struct comedi_insn *insn, unsigned int *data)
176 {
177         int n, i;
178         unsigned int d;
179         unsigned char status;
180         unsigned short msb, lsb;
181         unsigned char chan;
182         int range;
183
184         /* get the channel and range number */
185
186         chan = CR_CHAN(insn->chanspec) & (s->n_chan - 1);
187         range = CR_RANGE(insn->chanspec);
188
189         /* printk("channel=0x%02x, range=%d\n",chan,range); */
190
191         /* zero scan and fifo control and reset fifo */
192         outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
193
194         /* write the ai channel range regs */
195         outb(chan, dev->iobase + DMM32AT_AILOW);
196         outb(chan, dev->iobase + DMM32AT_AIHIGH);
197         /* set the range bits */
198         outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
199
200         /* wait for circuit to settle */
201         for (i = 0; i < 40000; i++) {
202                 status = inb(dev->iobase + DMM32AT_AIRBACK);
203                 if ((status & DMM32AT_STATUS) == 0)
204                         break;
205         }
206         if (i == 40000) {
207                 printk(KERN_WARNING "dmm32at: timeout\n");
208                 return -ETIMEDOUT;
209         }
210
211         /* convert n samples */
212         for (n = 0; n < insn->n; n++) {
213                 /* trigger conversion */
214                 outb(0xff, dev->iobase + DMM32AT_CONV);
215                 /* wait for conversion to end */
216                 for (i = 0; i < 40000; i++) {
217                         status = inb(dev->iobase + DMM32AT_AISTAT);
218                         if ((status & DMM32AT_STATUS) == 0)
219                                 break;
220                 }
221                 if (i == 40000) {
222                         printk(KERN_WARNING "dmm32at: timeout\n");
223                         return -ETIMEDOUT;
224                 }
225
226                 /* read data */
227                 lsb = inb(dev->iobase + DMM32AT_AILSB);
228                 msb = inb(dev->iobase + DMM32AT_AIMSB);
229
230                 /* invert sign bit to make range unsigned, this is an
231                    idiosyncrasy of the diamond board, it return
232                    conversions as a signed value, i.e. -32768 to
233                    32767, flipping the bit and interpreting it as
234                    signed gives you a range of 0 to 65535 which is
235                    used by comedi */
236                 d = ((msb ^ 0x0080) << 8) + lsb;
237
238                 data[n] = d;
239         }
240
241         /* return the number of samples read/written */
242         return n;
243 }
244
245 static int dmm32at_ns_to_timer(unsigned int *ns, int round)
246 {
247         /* trivial timer */
248         return *ns;
249 }
250
251 static int dmm32at_ai_cmdtest(struct comedi_device *dev,
252                               struct comedi_subdevice *s,
253                               struct comedi_cmd *cmd)
254 {
255         int err = 0;
256         int tmp;
257         int start_chan, gain, i;
258
259         /* Step 1 : check if triggers are trivially valid */
260
261         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
262         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
263                                         TRIG_TIMER /*| TRIG_EXT */);
264         err |= cfc_check_trigger_src(&cmd->convert_src,
265                                         TRIG_TIMER /*| TRIG_EXT */);
266         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
267         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
268
269         if (err)
270                 return 1;
271
272         /* Step 2a : make sure trigger sources are unique */
273
274         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
275         err |= cfc_check_trigger_is_unique(cmd->convert_src);
276         err |= cfc_check_trigger_is_unique(cmd->stop_src);
277
278         /* Step 2b : and mutually compatible */
279
280         if (err)
281                 return 2;
282
283         /* step 3: make sure arguments are trivially compatible */
284
285         if (cmd->start_arg != 0) {
286                 cmd->start_arg = 0;
287                 err++;
288         }
289 #define MAX_SCAN_SPEED  1000000 /* in nanoseconds */
290 #define MIN_SCAN_SPEED  1000000000      /* in nanoseconds */
291
292         if (cmd->scan_begin_src == TRIG_TIMER) {
293                 if (cmd->scan_begin_arg < MAX_SCAN_SPEED) {
294                         cmd->scan_begin_arg = MAX_SCAN_SPEED;
295                         err++;
296                 }
297                 if (cmd->scan_begin_arg > MIN_SCAN_SPEED) {
298                         cmd->scan_begin_arg = MIN_SCAN_SPEED;
299                         err++;
300                 }
301         } else {
302                 /* external trigger */
303                 /* should be level/edge, hi/lo specification here */
304                 /* should specify multiple external triggers */
305                 if (cmd->scan_begin_arg > 9) {
306                         cmd->scan_begin_arg = 9;
307                         err++;
308                 }
309         }
310         if (cmd->convert_src == TRIG_TIMER) {
311                 if (cmd->convert_arg >= 17500)
312                         cmd->convert_arg = 20000;
313                 else if (cmd->convert_arg >= 12500)
314                         cmd->convert_arg = 15000;
315                 else if (cmd->convert_arg >= 7500)
316                         cmd->convert_arg = 10000;
317                 else
318                         cmd->convert_arg = 5000;
319
320         } else {
321                 /* external trigger */
322                 /* see above */
323                 if (cmd->convert_arg > 9) {
324                         cmd->convert_arg = 9;
325                         err++;
326                 }
327         }
328
329         if (cmd->scan_end_arg != cmd->chanlist_len) {
330                 cmd->scan_end_arg = cmd->chanlist_len;
331                 err++;
332         }
333         if (cmd->stop_src == TRIG_COUNT) {
334                 if (cmd->stop_arg > 0xfffffff0) {
335                         cmd->stop_arg = 0xfffffff0;
336                         err++;
337                 }
338                 if (cmd->stop_arg == 0) {
339                         cmd->stop_arg = 1;
340                         err++;
341                 }
342         } else {
343                 /* TRIG_NONE */
344                 if (cmd->stop_arg != 0) {
345                         cmd->stop_arg = 0;
346                         err++;
347                 }
348         }
349
350         if (err)
351                 return 3;
352
353         /* step 4: fix up any arguments */
354
355         if (cmd->scan_begin_src == TRIG_TIMER) {
356                 tmp = cmd->scan_begin_arg;
357                 dmm32at_ns_to_timer(&cmd->scan_begin_arg,
358                                     cmd->flags & TRIG_ROUND_MASK);
359                 if (tmp != cmd->scan_begin_arg)
360                         err++;
361         }
362         if (cmd->convert_src == TRIG_TIMER) {
363                 tmp = cmd->convert_arg;
364                 dmm32at_ns_to_timer(&cmd->convert_arg,
365                                     cmd->flags & TRIG_ROUND_MASK);
366                 if (tmp != cmd->convert_arg)
367                         err++;
368                 if (cmd->scan_begin_src == TRIG_TIMER &&
369                     cmd->scan_begin_arg <
370                     cmd->convert_arg * cmd->scan_end_arg) {
371                         cmd->scan_begin_arg =
372                             cmd->convert_arg * cmd->scan_end_arg;
373                         err++;
374                 }
375         }
376
377         if (err)
378                 return 4;
379
380         /* step 5 check the channel list, the channel list for this
381            board must be consecutive and gains must be the same */
382
383         if (cmd->chanlist) {
384                 gain = CR_RANGE(cmd->chanlist[0]);
385                 start_chan = CR_CHAN(cmd->chanlist[0]);
386                 for (i = 1; i < cmd->chanlist_len; i++) {
387                         if (CR_CHAN(cmd->chanlist[i]) !=
388                             (start_chan + i) % s->n_chan) {
389                                 comedi_error(dev,
390                                              "entries in chanlist must be consecutive channels, counting upwards\n");
391                                 err++;
392                         }
393                         if (CR_RANGE(cmd->chanlist[i]) != gain) {
394                                 comedi_error(dev,
395                                              "entries in chanlist must all have the same gain\n");
396                                 err++;
397                         }
398                 }
399         }
400
401         if (err)
402                 return 5;
403
404         return 0;
405 }
406
407 static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
408 {
409         unsigned char lo1, lo2, hi2;
410         unsigned short both2;
411
412         /* based on 10mhz clock */
413         lo1 = 200;
414         both2 = nansec / 20000;
415         hi2 = (both2 & 0xff00) >> 8;
416         lo2 = both2 & 0x00ff;
417
418         /* set the counter frequency to 10mhz */
419         outb(0, dev->iobase + DMM32AT_CNTRDIO);
420
421         /* get access to the clock regs */
422         outb(DMM32AT_CLKACC, dev->iobase + DMM32AT_CNTRL);
423
424         /* write the counter 1 control word and low byte to counter */
425         outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
426         outb(lo1, dev->iobase + DMM32AT_CLK1);
427
428         /* write the counter 2 control word and low byte then to counter */
429         outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
430         outb(lo2, dev->iobase + DMM32AT_CLK2);
431         outb(hi2, dev->iobase + DMM32AT_CLK2);
432
433         /* enable the ai conversion interrupt and the clock to start scans */
434         outb(DMM32AT_ADINT | DMM32AT_CLKSEL, dev->iobase + DMM32AT_INTCLOCK);
435 }
436
437 static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
438 {
439         struct dmm32at_private *devpriv = dev->private;
440         struct comedi_cmd *cmd = &s->async->cmd;
441         int i, range;
442         unsigned char chanlo, chanhi, status;
443
444         if (!cmd->chanlist)
445                 return -EINVAL;
446
447         /* get the channel list and range */
448         chanlo = CR_CHAN(cmd->chanlist[0]) & (s->n_chan - 1);
449         chanhi = chanlo + cmd->chanlist_len - 1;
450         if (chanhi >= s->n_chan)
451                 return -EINVAL;
452         range = CR_RANGE(cmd->chanlist[0]);
453
454         /* reset fifo */
455         outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
456
457         /* set scan enable */
458         outb(DMM32AT_SCANENABLE, dev->iobase + DMM32AT_FIFOCNTRL);
459
460         /* write the ai channel range regs */
461         outb(chanlo, dev->iobase + DMM32AT_AILOW);
462         outb(chanhi, dev->iobase + DMM32AT_AIHIGH);
463
464         /* set the range bits */
465         outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
466
467         /* reset the interrupt just in case */
468         outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
469
470         if (cmd->stop_src == TRIG_COUNT)
471                 devpriv->ai_scans_left = cmd->stop_arg;
472         else {                  /* TRIG_NONE */
473                 devpriv->ai_scans_left = 0xffffffff; /* indicates TRIG_NONE to
474                                                       * isr */
475         }
476
477         /* wait for circuit to settle */
478         for (i = 0; i < 40000; i++) {
479                 status = inb(dev->iobase + DMM32AT_AIRBACK);
480                 if ((status & DMM32AT_STATUS) == 0)
481                         break;
482         }
483         if (i == 40000) {
484                 printk(KERN_WARNING "dmm32at: timeout\n");
485                 return -ETIMEDOUT;
486         }
487
488         if (devpriv->ai_scans_left > 1) {
489                 /* start the clock and enable the interrupts */
490                 dmm32at_setaitimer(dev, cmd->scan_begin_arg);
491         } else {
492                 /* start the interrups and initiate a single scan */
493                 outb(DMM32AT_ADINT, dev->iobase + DMM32AT_INTCLOCK);
494                 outb(0xff, dev->iobase + DMM32AT_CONV);
495         }
496
497 /*      printk("dmmat32 in command\n"); */
498
499 /*      for(i=0;i<cmd->chanlist_len;i++) */
500 /*              comedi_buf_put(s->async,i*100); */
501
502 /*      s->async->events |= COMEDI_CB_EOA; */
503 /*      comedi_event(dev, s); */
504
505         return 0;
506
507 }
508
509 static int dmm32at_ai_cancel(struct comedi_device *dev,
510                              struct comedi_subdevice *s)
511 {
512         struct dmm32at_private *devpriv = dev->private;
513
514         devpriv->ai_scans_left = 1;
515         return 0;
516 }
517
518 static irqreturn_t dmm32at_isr(int irq, void *d)
519 {
520         struct comedi_device *dev = d;
521         struct dmm32at_private *devpriv = dev->private;
522         unsigned char intstat;
523         unsigned int samp;
524         unsigned short msb, lsb;
525         int i;
526
527         if (!dev->attached) {
528                 comedi_error(dev, "spurious interrupt");
529                 return IRQ_HANDLED;
530         }
531
532         intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
533
534         if (intstat & DMM32AT_ADINT) {
535                 struct comedi_subdevice *s = dev->read_subdev;
536                 struct comedi_cmd *cmd = &s->async->cmd;
537
538                 for (i = 0; i < cmd->chanlist_len; i++) {
539                         /* read data */
540                         lsb = inb(dev->iobase + DMM32AT_AILSB);
541                         msb = inb(dev->iobase + DMM32AT_AIMSB);
542
543                         /* invert sign bit to make range unsigned */
544                         samp = ((msb ^ 0x0080) << 8) + lsb;
545                         comedi_buf_put(s->async, samp);
546                 }
547
548                 if (devpriv->ai_scans_left != 0xffffffff) {     /* TRIG_COUNT */
549                         devpriv->ai_scans_left--;
550                         if (devpriv->ai_scans_left == 0) {
551                                 /* disable further interrupts and clocks */
552                                 outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
553                                 /* set the buffer to be flushed with an EOF */
554                                 s->async->events |= COMEDI_CB_EOA;
555                         }
556
557                 }
558                 /* flush the buffer */
559                 comedi_event(dev, s);
560         }
561
562         /* reset the interrupt */
563         outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
564         return IRQ_HANDLED;
565 }
566
567 static int dmm32at_ao_winsn(struct comedi_device *dev,
568                             struct comedi_subdevice *s,
569                             struct comedi_insn *insn, unsigned int *data)
570 {
571         struct dmm32at_private *devpriv = dev->private;
572         int i;
573         int chan = CR_CHAN(insn->chanspec);
574         unsigned char hi, lo, status;
575
576         /* Writing a list of values to an AO channel is probably not
577          * very useful, but that's how the interface is defined. */
578         for (i = 0; i < insn->n; i++) {
579
580                 devpriv->ao_readback[chan] = data[i];
581
582                 /* get the low byte */
583                 lo = data[i] & 0x00ff;
584                 /* high byte also contains channel number */
585                 hi = (data[i] >> 8) + chan * (1 << 6);
586                 /* printk("writing 0x%02x  0x%02x\n",hi,lo); */
587                 /* write the low and high values to the board */
588                 outb(lo, dev->iobase + DMM32AT_DACLSB);
589                 outb(hi, dev->iobase + DMM32AT_DACMSB);
590
591                 /* wait for circuit to settle */
592                 for (i = 0; i < 40000; i++) {
593                         status = inb(dev->iobase + DMM32AT_DACSTAT);
594                         if ((status & DMM32AT_DACBUSY) == 0)
595                                 break;
596                 }
597                 if (i == 40000) {
598                         printk(KERN_WARNING "dmm32at: timeout\n");
599                         return -ETIMEDOUT;
600                 }
601                 /* dummy read to update trigger the output */
602                 status = inb(dev->iobase + DMM32AT_DACMSB);
603
604         }
605
606         /* return the number of samples read/written */
607         return i;
608 }
609
610 static int dmm32at_ao_rinsn(struct comedi_device *dev,
611                             struct comedi_subdevice *s,
612                             struct comedi_insn *insn, unsigned int *data)
613 {
614         struct dmm32at_private *devpriv = dev->private;
615         int i;
616         int chan = CR_CHAN(insn->chanspec);
617
618         for (i = 0; i < insn->n; i++)
619                 data[i] = devpriv->ao_readback[chan];
620
621         return i;
622 }
623
624 static int dmm32at_dio_insn_bits(struct comedi_device *dev,
625                                  struct comedi_subdevice *s,
626                                  struct comedi_insn *insn, unsigned int *data)
627 {
628         struct dmm32at_private *devpriv = dev->private;
629         unsigned char diobits;
630
631         /* The insn data is a mask in data[0] and the new data
632          * in data[1], each channel cooresponding to a bit. */
633         if (data[0]) {
634                 s->state &= ~data[0];
635                 s->state |= data[0] & data[1];
636                 /* Write out the new digital output lines */
637                 /* outw(s->state,dev->iobase + DMM32AT_DIO); */
638         }
639
640         /* get access to the DIO regs */
641         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
642
643         /* if either part of dio is set for output */
644         if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) ||
645             ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) {
646                 diobits = (s->state & 0x00ff0000) >> 16;
647                 outb(diobits, dev->iobase + DMM32AT_DIOC);
648         }
649         if ((devpriv->dio_config & DMM32AT_DIRB) == 0) {
650                 diobits = (s->state & 0x0000ff00) >> 8;
651                 outb(diobits, dev->iobase + DMM32AT_DIOB);
652         }
653         if ((devpriv->dio_config & DMM32AT_DIRA) == 0) {
654                 diobits = (s->state & 0x000000ff);
655                 outb(diobits, dev->iobase + DMM32AT_DIOA);
656         }
657
658         /* now read the state back in */
659         s->state = inb(dev->iobase + DMM32AT_DIOC);
660         s->state <<= 8;
661         s->state |= inb(dev->iobase + DMM32AT_DIOB);
662         s->state <<= 8;
663         s->state |= inb(dev->iobase + DMM32AT_DIOA);
664         data[1] = s->state;
665
666         /* on return, data[1] contains the value of the digital
667          * input and output lines. */
668         /* data[1]=inw(dev->iobase + DMM32AT_DIO); */
669         /* or we could just return the software copy of the output values if
670          * it was a purely digital output subdevice */
671         /* data[1]=s->state; */
672
673         return insn->n;
674 }
675
676 static int dmm32at_dio_insn_config(struct comedi_device *dev,
677                                    struct comedi_subdevice *s,
678                                    struct comedi_insn *insn, unsigned int *data)
679 {
680         struct dmm32at_private *devpriv = dev->private;
681         unsigned char chanbit;
682         int chan = CR_CHAN(insn->chanspec);
683
684         if (insn->n != 1)
685                 return -EINVAL;
686
687         if (chan < 8)
688                 chanbit = DMM32AT_DIRA;
689         else if (chan < 16)
690                 chanbit = DMM32AT_DIRB;
691         else if (chan < 20)
692                 chanbit = DMM32AT_DIRCL;
693         else
694                 chanbit = DMM32AT_DIRCH;
695
696         /* The input or output configuration of each digital line is
697          * configured by a special insn_config instruction.  chanspec
698          * contains the channel to be changed, and data[0] contains the
699          * value COMEDI_INPUT or COMEDI_OUTPUT. */
700
701         /* if output clear the bit, otherwise set it */
702         if (data[0] == COMEDI_OUTPUT)
703                 devpriv->dio_config &= ~chanbit;
704         else
705                 devpriv->dio_config |= chanbit;
706         /* get access to the DIO regs */
707         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
708         /* set the DIO's to the new configuration setting */
709         outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
710
711         return 1;
712 }
713
714 static int dmm32at_attach(struct comedi_device *dev,
715                           struct comedi_devconfig *it)
716 {
717         struct dmm32at_private *devpriv;
718         int ret;
719         struct comedi_subdevice *s;
720         unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
721         unsigned long iobase;
722         unsigned int irq;
723
724         dev->board_name = dev->driver->driver_name;
725
726         iobase = it->options[0];
727         irq = it->options[1];
728
729         printk(KERN_INFO "comedi%d: dmm32at: attaching\n", dev->minor);
730         printk(KERN_DEBUG "dmm32at: probing at address 0x%04lx, irq %u\n",
731                iobase, irq);
732
733         /* register address space */
734         if (!request_region(iobase, DMM32AT_MEMSIZE, dev->board_name)) {
735                 printk(KERN_ERR "comedi%d: dmm32at: I/O port conflict\n",
736                        dev->minor);
737                 return -EIO;
738         }
739         dev->iobase = iobase;
740
741         /* the following just makes sure the board is there and gets
742            it to a known state */
743
744         /* reset the board */
745         outb(DMM32AT_RESET, dev->iobase + DMM32AT_CNTRL);
746
747         /* allow a millisecond to reset */
748         udelay(1000);
749
750         /* zero scan and fifo control */
751         outb(0x0, dev->iobase + DMM32AT_FIFOCNTRL);
752
753         /* zero interrupt and clock control */
754         outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
755
756         /* write a test channel range, the high 3 bits should drop */
757         outb(0x80, dev->iobase + DMM32AT_AILOW);
758         outb(0xff, dev->iobase + DMM32AT_AIHIGH);
759
760         /* set the range at 10v unipolar */
761         outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AICONF);
762
763         /* should take 10 us to settle, here's a hundred */
764         udelay(100);
765
766         /* read back the values */
767         ailo = inb(dev->iobase + DMM32AT_AILOW);
768         aihi = inb(dev->iobase + DMM32AT_AIHIGH);
769         fifostat = inb(dev->iobase + DMM32AT_FIFOSTAT);
770         aistat = inb(dev->iobase + DMM32AT_AISTAT);
771         intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
772         airback = inb(dev->iobase + DMM32AT_AIRBACK);
773
774         printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
775                ailo, aihi, fifostat);
776         printk(KERN_DEBUG
777                "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
778                aistat, intstat, airback);
779
780         if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
781             (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
782                 printk(KERN_ERR "dmmat32: board detection failed\n");
783                 return -EIO;
784         }
785
786         /* board is there, register interrupt */
787         if (irq) {
788                 ret = request_irq(irq, dmm32at_isr, 0, dev->board_name, dev);
789                 if (ret < 0) {
790                         printk(KERN_ERR "dmm32at: irq conflict\n");
791                         return ret;
792                 }
793                 dev->irq = irq;
794         }
795
796         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
797         if (!devpriv)
798                 return -ENOMEM;
799         dev->private = devpriv;
800
801         ret = comedi_alloc_subdevices(dev, 3);
802         if (ret)
803                 return ret;
804
805         s = &dev->subdevices[0];
806         dev->read_subdev = s;
807         /* analog input subdevice */
808         s->type = COMEDI_SUBD_AI;
809         /* we support single-ended (ground) and differential */
810         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
811         s->n_chan = 32;
812         s->maxdata = 0xffff;
813         s->range_table = &dmm32at_airanges;
814         s->len_chanlist = 32;   /* This is the maximum chanlist length that
815                                    the board can handle */
816         s->insn_read = dmm32at_ai_rinsn;
817         s->do_cmd = dmm32at_ai_cmd;
818         s->do_cmdtest = dmm32at_ai_cmdtest;
819         s->cancel = dmm32at_ai_cancel;
820
821         s = &dev->subdevices[1];
822         /* analog output subdevice */
823         s->type = COMEDI_SUBD_AO;
824         s->subdev_flags = SDF_WRITABLE;
825         s->n_chan = 4;
826         s->maxdata = 0x0fff;
827         s->range_table = &dmm32at_aoranges;
828         s->insn_write = dmm32at_ao_winsn;
829         s->insn_read = dmm32at_ao_rinsn;
830
831         s = &dev->subdevices[2];
832         /* digital i/o subdevice */
833
834         /* get access to the DIO regs */
835         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
836         /* set the DIO's to the defualt input setting */
837         devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
838                 DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
839         outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
840
841         /* set up the subdevice */
842         s->type = COMEDI_SUBD_DIO;
843         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
844         s->n_chan = 24;
845         s->maxdata = 1;
846         s->state = 0;
847         s->range_table = &range_digital;
848         s->insn_bits = dmm32at_dio_insn_bits;
849         s->insn_config = dmm32at_dio_insn_config;
850
851         /* success */
852         printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor);
853
854         return 1;
855
856 }
857
858 static void dmm32at_detach(struct comedi_device *dev)
859 {
860         if (dev->irq)
861                 free_irq(dev->irq, dev);
862         if (dev->iobase)
863                 release_region(dev->iobase, DMM32AT_MEMSIZE);
864 }
865
866 static struct comedi_driver dmm32at_driver = {
867         .driver_name    = "dmm32at",
868         .module         = THIS_MODULE,
869         .attach         = dmm32at_attach,
870         .detach         = dmm32at_detach,
871 };
872 module_comedi_driver(dmm32at_driver);
873
874 MODULE_AUTHOR("Comedi http://www.comedi.org");
875 MODULE_DESCRIPTION("Comedi low-level driver");
876 MODULE_LICENSE("GPL");