1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#include <linux/module.h>
36#include "../comedidev.h"
37
38#include <linux/gfp.h>
39#include <linux/delay.h>
40#include <linux/io.h>
41#include <linux/interrupt.h>
42#include <asm/dma.h>
43
44#include "comedi_fc.h"
45#include "8253.h"
46
47
48
49
50#define PCL816_DO_DI_LSB_REG 0x00
51#define PCL816_DO_DI_MSB_REG 0x01
52#define PCL816_TIMER_BASE 0x04
53#define PCL816_AI_LSB_REG 0x08
54#define PCL816_AI_MSB_REG 0x09
55#define PCL816_RANGE_REG 0x09
56#define PCL816_CLRINT_REG 0x0a
57#define PCL816_MUX_REG 0x0b
58#define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
59#define PCL816_CTRL_REG 0x0c
60#define PCL816_CTRL_DISABLE_TRIG (0 << 0)
61#define PCL816_CTRL_SOFT_TRIG (1 << 0)
62#define PCL816_CTRL_PACER_TRIG (1 << 1)
63#define PCL816_CTRL_EXT_TRIG (1 << 2)
64#define PCL816_CTRL_POE (1 << 3)
65#define PCL816_CTRL_DMAEN (1 << 4)
66#define PCL816_CTRL_INTEN (1 << 5)
67#define PCL816_CTRL_DMASRC_SLOT0 (0 << 6)
68#define PCL816_CTRL_DMASRC_SLOT1 (1 << 6)
69#define PCL816_CTRL_DMASRC_SLOT2 (2 << 6)
70#define PCL816_STATUS_REG 0x0d
71#define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
72#define PCL816_STATUS_INTSRC_MASK (3 << 4)
73#define PCL816_STATUS_INTSRC_SLOT0 (0 << 4)
74#define PCL816_STATUS_INTSRC_SLOT1 (1 << 4)
75#define PCL816_STATUS_INTSRC_SLOT2 (2 << 4)
76#define PCL816_STATUS_INTSRC_DMA (3 << 4)
77#define PCL816_STATUS_INTACT (1 << 6)
78#define PCL816_STATUS_DRDY (1 << 7)
79
80#define MAGIC_DMA_WORD 0x5a5a
81
82static const struct comedi_lrange range_pcl816 = {
83 8, {
84 BIP_RANGE(10),
85 BIP_RANGE(5),
86 BIP_RANGE(2.5),
87 BIP_RANGE(1.25),
88 UNI_RANGE(10),
89 UNI_RANGE(5),
90 UNI_RANGE(2.5),
91 UNI_RANGE(1.25)
92 }
93};
94
95struct pcl816_board {
96 const char *name;
97 int ai_maxdata;
98 int ao_maxdata;
99 int ai_chanlist;
100};
101
102static const struct pcl816_board boardtypes[] = {
103 {
104 .name = "pcl816",
105 .ai_maxdata = 0xffff,
106 .ao_maxdata = 0xffff,
107 .ai_chanlist = 1024,
108 }, {
109 .name = "pcl814b",
110 .ai_maxdata = 0x3fff,
111 .ao_maxdata = 0x3fff,
112 .ai_chanlist = 1024,
113 },
114};
115
116struct pcl816_private {
117 unsigned int dma;
118 unsigned int dmapages;
119 unsigned int hwdmasize;
120 unsigned long dmabuf[2];
121 unsigned int hwdmaptr[2];
122 int next_dma_buf;
123 long dma_runs_to_end;
124 unsigned long last_dma_run;
125 int ai_act_scan;
126 unsigned int ai_poll_ptr;
127 unsigned int divisor1;
128 unsigned int divisor2;
129 unsigned int ai_cmd_running:1;
130 unsigned int ai_cmd_canceled:1;
131};
132
133static int check_channel_list(struct comedi_device *dev,
134 struct comedi_subdevice *s,
135 unsigned int *chanlist, unsigned int chanlen);
136
137static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
138{
139 struct pcl816_private *devpriv = dev->private;
140 unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
141
142 i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
143 i8254_write(timer_base, 0, 0, 0x00ff);
144 udelay(1);
145
146 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
147 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
148 udelay(1);
149
150 if (load_counters) {
151 i8254_write(timer_base, 0, 2, devpriv->divisor2);
152 i8254_write(timer_base, 0, 1, devpriv->divisor1);
153 }
154}
155
156static void pcl816_ai_setup_dma(struct comedi_device *dev,
157 struct comedi_subdevice *s)
158{
159 struct pcl816_private *devpriv = dev->private;
160 struct comedi_cmd *cmd = &s->async->cmd;
161 unsigned int dma_flags;
162 unsigned int bytes;
163
164 bytes = devpriv->hwdmasize;
165 if (cmd->stop_src == TRIG_COUNT) {
166
167 bytes = s->async->cmd.chanlist_len *
168 s->async->cmd.chanlist_len *
169 sizeof(short);
170
171
172 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
173
174
175 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
176 devpriv->dma_runs_to_end--;
177 if (devpriv->dma_runs_to_end >= 0)
178 bytes = devpriv->hwdmasize;
179 } else
180 devpriv->dma_runs_to_end = -1;
181
182 devpriv->next_dma_buf = 0;
183 set_dma_mode(devpriv->dma, DMA_MODE_READ);
184 dma_flags = claim_dma_lock();
185 clear_dma_ff(devpriv->dma);
186 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
187 set_dma_count(devpriv->dma, bytes);
188 release_dma_lock(dma_flags);
189 enable_dma(devpriv->dma);
190}
191
192static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
193 struct comedi_subdevice *s)
194{
195 struct pcl816_private *devpriv = dev->private;
196 struct comedi_cmd *cmd = &s->async->cmd;
197 unsigned long dma_flags;
198
199 disable_dma(devpriv->dma);
200 if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
201
202 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
203 set_dma_mode(devpriv->dma, DMA_MODE_READ);
204 dma_flags = claim_dma_lock();
205 set_dma_addr(devpriv->dma,
206 devpriv->hwdmaptr[devpriv->next_dma_buf]);
207 if (devpriv->dma_runs_to_end)
208 set_dma_count(devpriv->dma, devpriv->hwdmasize);
209 else
210 set_dma_count(devpriv->dma, devpriv->last_dma_run);
211 release_dma_lock(dma_flags);
212 enable_dma(devpriv->dma);
213 }
214
215 devpriv->dma_runs_to_end--;
216}
217
218static void pcl816_ai_set_chan_range(struct comedi_device *dev,
219 unsigned int chan,
220 unsigned int range)
221{
222 outb(chan, dev->iobase + PCL816_MUX_REG);
223 outb(range, dev->iobase + PCL816_RANGE_REG);
224}
225
226static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
227 unsigned int first_chan,
228 unsigned int last_chan)
229{
230 outb(PCL816_MUX_SCAN(first_chan, last_chan),
231 dev->iobase + PCL816_MUX_REG);
232}
233
234static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
235 unsigned int *chanlist,
236 unsigned int seglen)
237{
238 unsigned int first_chan = CR_CHAN(chanlist[0]);
239 unsigned int last_chan;
240 unsigned int range;
241 unsigned int i;
242
243
244 for (i = 0; i < seglen; i++) {
245 last_chan = CR_CHAN(chanlist[i]);
246 range = CR_RANGE(chanlist[i]);
247
248 pcl816_ai_set_chan_range(dev, last_chan, range);
249 }
250
251 udelay(1);
252
253 pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
254}
255
256static void pcl816_ai_clear_eoc(struct comedi_device *dev)
257{
258
259 outb(0, dev->iobase + PCL816_CLRINT_REG);
260}
261
262static void pcl816_ai_soft_trig(struct comedi_device *dev)
263{
264
265 outb(0, dev->iobase + PCL816_AI_LSB_REG);
266}
267
268static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
269 struct comedi_subdevice *s)
270{
271 unsigned int val;
272
273 val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
274 val |= inb(dev->iobase + PCL816_AI_LSB_REG);
275
276 return val & s->maxdata;
277}
278
279static int pcl816_ai_eoc(struct comedi_device *dev,
280 struct comedi_subdevice *s,
281 struct comedi_insn *insn,
282 unsigned long context)
283{
284 unsigned int status;
285
286 status = inb(dev->iobase + PCL816_STATUS_REG);
287 if ((status & PCL816_STATUS_DRDY) == 0)
288 return 0;
289 return -EBUSY;
290}
291
292static bool pcl816_ai_next_chan(struct comedi_device *dev,
293 struct comedi_subdevice *s)
294{
295 struct pcl816_private *devpriv = dev->private;
296 struct comedi_cmd *cmd = &s->async->cmd;
297
298 s->async->events |= COMEDI_CB_BLOCK;
299
300 s->async->cur_chan++;
301 if (s->async->cur_chan >= cmd->chanlist_len) {
302 s->async->cur_chan = 0;
303 devpriv->ai_act_scan++;
304 s->async->events |= COMEDI_CB_EOS;
305 }
306
307 if (cmd->stop_src == TRIG_COUNT &&
308 devpriv->ai_act_scan >= cmd->stop_arg) {
309
310 s->async->events |= COMEDI_CB_EOA;
311 return false;
312 }
313
314 return true;
315}
316
317static void transfer_from_dma_buf(struct comedi_device *dev,
318 struct comedi_subdevice *s,
319 unsigned short *ptr,
320 unsigned int bufptr, unsigned int len)
321{
322 int i;
323
324 for (i = 0; i < len; i++) {
325 comedi_buf_put(s->async, ptr[bufptr++]);
326
327 if (!pcl816_ai_next_chan(dev, s))
328 return;
329 }
330}
331
332static irqreturn_t pcl816_interrupt(int irq, void *d)
333{
334 struct comedi_device *dev = d;
335 struct comedi_subdevice *s = dev->read_subdev;
336 struct pcl816_private *devpriv = dev->private;
337 unsigned short *ptr;
338 unsigned int bufptr;
339 unsigned int len;
340
341 if (!dev->attached || !devpriv->ai_cmd_running) {
342 pcl816_ai_clear_eoc(dev);
343 return IRQ_HANDLED;
344 }
345
346 if (devpriv->ai_cmd_canceled) {
347 devpriv->ai_cmd_canceled = 0;
348 pcl816_ai_clear_eoc(dev);
349 return IRQ_HANDLED;
350 }
351
352 ptr = (unsigned short *)devpriv->dmabuf[devpriv->next_dma_buf];
353
354 pcl816_ai_setup_next_dma(dev, s);
355
356 len = (devpriv->hwdmasize >> 1) - devpriv->ai_poll_ptr;
357 bufptr = devpriv->ai_poll_ptr;
358 devpriv->ai_poll_ptr = 0;
359
360 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
361
362 pcl816_ai_clear_eoc(dev);
363
364 cfc_handle_events(dev, s);
365 return IRQ_HANDLED;
366}
367
368static int pcl816_ai_cmdtest(struct comedi_device *dev,
369 struct comedi_subdevice *s, struct comedi_cmd *cmd)
370{
371 struct pcl816_private *devpriv = dev->private;
372 int err = 0;
373 int tmp;
374
375
376
377 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
378 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
379 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
380 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
381 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
382
383 if (err)
384 return 1;
385
386
387
388 err |= cfc_check_trigger_is_unique(cmd->convert_src);
389 err |= cfc_check_trigger_is_unique(cmd->stop_src);
390
391
392
393 if (err)
394 return 2;
395
396
397
398
399 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
400 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
401
402 if (cmd->convert_src == TRIG_TIMER)
403 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
404 else
405 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
406
407 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
408
409 if (cmd->stop_src == TRIG_COUNT)
410 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
411 else
412 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
413
414 if (err)
415 return 3;
416
417
418
419 if (cmd->convert_src == TRIG_TIMER) {
420 tmp = cmd->convert_arg;
421 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
422 &devpriv->divisor1,
423 &devpriv->divisor2,
424 &cmd->convert_arg, cmd->flags);
425 if (cmd->convert_arg < 10000)
426 cmd->convert_arg = 10000;
427 if (tmp != cmd->convert_arg)
428 err++;
429 }
430
431 if (err)
432 return 4;
433
434
435
436
437 if (cmd->chanlist) {
438 if (!check_channel_list(dev, s, cmd->chanlist,
439 cmd->chanlist_len))
440 return 5;
441 }
442
443 return 0;
444}
445
446static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
447{
448 struct pcl816_private *devpriv = dev->private;
449 struct comedi_cmd *cmd = &s->async->cmd;
450 unsigned int ctrl;
451 unsigned int seglen;
452
453 if (devpriv->ai_cmd_running)
454 return -EBUSY;
455
456 pcl816_start_pacer(dev, false);
457
458 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
459 if (seglen < 1)
460 return -EINVAL;
461 pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
462 udelay(1);
463
464 devpriv->ai_act_scan = 0;
465 s->async->cur_chan = 0;
466 devpriv->ai_cmd_running = 1;
467 devpriv->ai_poll_ptr = 0;
468 devpriv->ai_cmd_canceled = 0;
469
470 pcl816_ai_setup_dma(dev, s);
471
472 pcl816_start_pacer(dev, true);
473
474 ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
475 if (cmd->convert_src == TRIG_TIMER)
476 ctrl |= PCL816_CTRL_PACER_TRIG;
477 else
478 ctrl |= PCL816_CTRL_EXT_TRIG;
479
480 outb(ctrl, dev->iobase + PCL816_CTRL_REG);
481 outb((devpriv->dma << 4) | dev->irq, dev->iobase + PCL816_STATUS_REG);
482
483 return 0;
484}
485
486static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
487{
488 struct pcl816_private *devpriv = dev->private;
489 unsigned long flags;
490 unsigned int top1, top2, i;
491
492 spin_lock_irqsave(&dev->spinlock, flags);
493
494 for (i = 0; i < 20; i++) {
495 top1 = get_dma_residue(devpriv->dma);
496 top2 = get_dma_residue(devpriv->dma);
497 if (top1 == top2)
498 break;
499 }
500 if (top1 != top2) {
501 spin_unlock_irqrestore(&dev->spinlock, flags);
502 return 0;
503 }
504
505
506 top1 = devpriv->hwdmasize - top1;
507 top1 >>= 1;
508 top2 = top1 - devpriv->ai_poll_ptr;
509 if (top2 < 1) {
510 spin_unlock_irqrestore(&dev->spinlock, flags);
511 return 0;
512 }
513
514 transfer_from_dma_buf(dev, s,
515 (unsigned short *)devpriv->dmabuf[devpriv->
516 next_dma_buf],
517 devpriv->ai_poll_ptr, top2);
518
519 devpriv->ai_poll_ptr = top1;
520 spin_unlock_irqrestore(&dev->spinlock, flags);
521
522 cfc_handle_events(dev, s);
523
524 return s->async->buf_write_count - s->async->buf_read_count;
525}
526
527static int pcl816_ai_cancel(struct comedi_device *dev,
528 struct comedi_subdevice *s)
529{
530 struct pcl816_private *devpriv = dev->private;
531
532 if (!devpriv->ai_cmd_running)
533 return 0;
534
535 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
536 pcl816_ai_clear_eoc(dev);
537
538
539 i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
540 2, I8254_MODE0 | I8254_BINARY);
541 i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
542 1, I8254_MODE0 | I8254_BINARY);
543
544 devpriv->ai_cmd_running = 0;
545 devpriv->ai_cmd_canceled = 1;
546
547 return 0;
548}
549
550static int
551check_channel_list(struct comedi_device *dev,
552 struct comedi_subdevice *s, unsigned int *chanlist,
553 unsigned int chanlen)
554{
555 unsigned int chansegment[16];
556 unsigned int i, nowmustbechan, seglen, segpos;
557
558
559 if (chanlen < 1) {
560 comedi_error(dev, "range/channel list is empty!");
561 return 0;
562 }
563
564 if (chanlen > 1) {
565
566 chansegment[0] = chanlist[0];
567 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
568
569 if (chanlist[0] == chanlist[i])
570 break;
571 nowmustbechan =
572 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
573 if (nowmustbechan != CR_CHAN(chanlist[i])) {
574
575 dev_dbg(dev->class_dev,
576 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
577 i, CR_CHAN(chanlist[i]), nowmustbechan,
578 CR_CHAN(chanlist[0]));
579 return 0;
580 }
581
582 chansegment[i] = chanlist[i];
583 }
584
585
586 for (i = 0, segpos = 0; i < chanlen; i++) {
587 if (chanlist[i] != chansegment[i % seglen]) {
588 dev_dbg(dev->class_dev,
589 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
590 i, CR_CHAN(chansegment[i]),
591 CR_RANGE(chansegment[i]),
592 CR_AREF(chansegment[i]),
593 CR_CHAN(chanlist[i % seglen]),
594 CR_RANGE(chanlist[i % seglen]),
595 CR_AREF(chansegment[i % seglen]));
596 return 0;
597 }
598 }
599 } else {
600 seglen = 1;
601 }
602
603 return seglen;
604}
605
606static int pcl816_ai_insn_read(struct comedi_device *dev,
607 struct comedi_subdevice *s,
608 struct comedi_insn *insn,
609 unsigned int *data)
610{
611 unsigned int chan = CR_CHAN(insn->chanspec);
612 unsigned int range = CR_RANGE(insn->chanspec);
613 int ret = 0;
614 int i;
615
616 outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
617
618 pcl816_ai_set_chan_range(dev, chan, range);
619 pcl816_ai_set_chan_scan(dev, chan, chan);
620
621 for (i = 0; i < insn->n; i++) {
622 pcl816_ai_clear_eoc(dev);
623 pcl816_ai_soft_trig(dev);
624
625 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
626 if (ret)
627 break;
628
629 data[i] = pcl816_ai_get_sample(dev, s);
630 }
631 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
632 pcl816_ai_clear_eoc(dev);
633
634 return ret ? ret : insn->n;
635}
636
637static int pcl816_di_insn_bits(struct comedi_device *dev,
638 struct comedi_subdevice *s,
639 struct comedi_insn *insn,
640 unsigned int *data)
641{
642 data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
643 (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
644
645 return insn->n;
646}
647
648static int pcl816_do_insn_bits(struct comedi_device *dev,
649 struct comedi_subdevice *s,
650 struct comedi_insn *insn,
651 unsigned int *data)
652{
653 if (comedi_dio_update_state(s, data)) {
654 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
655 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
656 }
657
658 data[1] = s->state;
659
660 return insn->n;
661}
662
663static void pcl816_reset(struct comedi_device *dev)
664{
665 unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
666
667 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
668 pcl816_ai_set_chan_range(dev, 0, 0);
669 pcl816_ai_clear_eoc(dev);
670
671
672 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
673 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
674 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
675
676
677 outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
678 outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
679}
680
681static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
682{
683 const struct pcl816_board *board = comedi_board(dev);
684 struct pcl816_private *devpriv;
685 struct comedi_subdevice *s;
686 int ret;
687 int i;
688
689 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
690 if (!devpriv)
691 return -ENOMEM;
692
693 ret = comedi_request_region(dev, it->options[0], 0x10);
694 if (ret)
695 return ret;
696
697
698 if (it->options[1] >= 2 && it->options[1] <= 7) {
699 ret = request_irq(it->options[1], pcl816_interrupt, 0,
700 dev->board_name, dev);
701 if (ret == 0)
702 dev->irq = it->options[1];
703 }
704
705
706 if (dev->irq && (it->options[2] == 3 || it->options[2] == 1)) {
707 ret = request_dma(it->options[2], dev->board_name);
708 if (ret) {
709 dev_err(dev->class_dev,
710 "unable to request DMA channel %d\n",
711 it->options[2]);
712 return -EBUSY;
713 }
714 devpriv->dma = it->options[2];
715
716 devpriv->dmapages = 2;
717 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
718
719 for (i = 0; i < 2; i++) {
720 unsigned long dmabuf;
721
722 dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
723 if (!dmabuf)
724 return -ENOMEM;
725
726 devpriv->dmabuf[i] = dmabuf;
727 devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
728 }
729 }
730
731 ret = comedi_alloc_subdevices(dev, 4);
732 if (ret)
733 return ret;
734
735 s = &dev->subdevices[0];
736 s->type = COMEDI_SUBD_AI;
737 s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
738 s->n_chan = 16;
739 s->maxdata = board->ai_maxdata;
740 s->range_table = &range_pcl816;
741 s->insn_read = pcl816_ai_insn_read;
742 if (devpriv->dma) {
743 dev->read_subdev = s;
744 s->subdev_flags |= SDF_CMD_READ;
745 s->len_chanlist = board->ai_chanlist;
746 s->do_cmdtest = pcl816_ai_cmdtest;
747 s->do_cmd = pcl816_ai_cmd;
748 s->poll = pcl816_ai_poll;
749 s->cancel = pcl816_ai_cancel;
750 }
751
752
753 s = &dev->subdevices[2];
754 s->type = COMEDI_SUBD_UNUSED;
755#if 0
756 subdevs[1] = COMEDI_SUBD_AO;
757 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
758 s->n_chan = 1;
759 s->maxdata = board->ao_maxdata;
760 s->range_table = &range_pcl816;
761#endif
762
763
764 s = &dev->subdevices[2];
765 s->type = COMEDI_SUBD_DI;
766 s->subdev_flags = SDF_READABLE;
767 s->n_chan = 16;
768 s->maxdata = 1;
769 s->range_table = &range_digital;
770 s->insn_bits = pcl816_di_insn_bits;
771
772
773 s = &dev->subdevices[3];
774 s->type = COMEDI_SUBD_DO;
775 s->subdev_flags = SDF_WRITABLE;
776 s->n_chan = 16;
777 s->maxdata = 1;
778 s->range_table = &range_digital;
779 s->insn_bits = pcl816_do_insn_bits;
780
781 pcl816_reset(dev);
782
783 return 0;
784}
785
786static void pcl816_detach(struct comedi_device *dev)
787{
788 struct pcl816_private *devpriv = dev->private;
789
790 if (dev->private) {
791 pcl816_ai_cancel(dev, dev->read_subdev);
792 pcl816_reset(dev);
793 if (devpriv->dma)
794 free_dma(devpriv->dma);
795 if (devpriv->dmabuf[0])
796 free_pages(devpriv->dmabuf[0], devpriv->dmapages);
797 if (devpriv->dmabuf[1])
798 free_pages(devpriv->dmabuf[1], devpriv->dmapages);
799 }
800 comedi_legacy_detach(dev);
801}
802
803static struct comedi_driver pcl816_driver = {
804 .driver_name = "pcl816",
805 .module = THIS_MODULE,
806 .attach = pcl816_attach,
807 .detach = pcl816_detach,
808 .board_name = &boardtypes[0].name,
809 .num_names = ARRAY_SIZE(boardtypes),
810 .offset = sizeof(struct pcl816_board),
811};
812module_comedi_driver(pcl816_driver);
813
814MODULE_AUTHOR("Comedi http://www.comedi.org");
815MODULE_DESCRIPTION("Comedi low-level driver");
816MODULE_LICENSE("GPL");
817