Staging: comedi: Remove COMEDI_INITCLEANUP macro
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / serial2002.c
1 /*
2 comedi/drivers/serial2002.c
3 Skeleton code for a Comedi driver
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2002 Anders Blomdell <anders.blomdell@control.lth.se>
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 /*
25 Driver: serial2002
26 Description: Driver for serial connected hardware
27 Devices:
28 Author: Anders Blomdell
29 Updated: Fri, 7 Jun 2002 12:56:45 -0700
30 Status: in development
31
32 */
33
34 #include "../comedidev.h"
35
36 #include <linux/delay.h>
37 #include <linux/ioport.h>
38 #include <linux/sched.h>
39 #include <linux/slab.h>
40
41 #include <asm/termios.h>
42 #include <asm/ioctls.h>
43 #include <linux/serial.h>
44 #include <linux/poll.h>
45
46 /*
47 * Board descriptions for two imaginary boards. Describing the
48 * boards in this way is optional, and completely driver-dependent.
49 * Some drivers use arrays such as this, other do not.
50 */
51 struct serial2002_board {
52 const char *name;
53 };
54
55 static const struct serial2002_board serial2002_boards[] = {
56 {
57 .name = "serial2002"}
58 };
59
60 /*
61 * Useful for shorthand access to the particular board structure
62 */
63 #define thisboard ((const struct serial2002_board *)dev->board_ptr)
64
65 struct serial2002_range_table_t {
66
67 /* HACK... */
68 int length;
69 struct comedi_krange range;
70 };
71
72 struct serial2002_private {
73
74 int port; /* /dev/ttyS<port> */
75 int speed; /* baudrate */
76 struct file *tty;
77 unsigned int ao_readback[32];
78 unsigned char digital_in_mapping[32];
79 unsigned char digital_out_mapping[32];
80 unsigned char analog_in_mapping[32];
81 unsigned char analog_out_mapping[32];
82 unsigned char encoder_in_mapping[32];
83 struct serial2002_range_table_t in_range[32], out_range[32];
84 };
85
86 /*
87 * most drivers define the following macro to make it easy to
88 * access the private structure.
89 */
90 #define devpriv ((struct serial2002_private *)dev->private)
91
92 static int serial2002_attach(struct comedi_device *dev,
93 struct comedi_devconfig *it);
94 static int serial2002_detach(struct comedi_device *dev);
95 struct comedi_driver driver_serial2002 = {
96 .driver_name = "serial2002",
97 .module = THIS_MODULE,
98 .attach = serial2002_attach,
99 .detach = serial2002_detach,
100 .board_name = &serial2002_boards[0].name,
101 .offset = sizeof(struct serial2002_board),
102 .num_names = ARRAY_SIZE(serial2002_boards),
103 };
104
105 static int serial2002_di_rinsn(struct comedi_device *dev,
106 struct comedi_subdevice *s,
107 struct comedi_insn *insn, unsigned int *data);
108 static int serial2002_do_winsn(struct comedi_device *dev,
109 struct comedi_subdevice *s,
110 struct comedi_insn *insn, unsigned int *data);
111 static int serial2002_ai_rinsn(struct comedi_device *dev,
112 struct comedi_subdevice *s,
113 struct comedi_insn *insn, unsigned int *data);
114 static int serial2002_ao_winsn(struct comedi_device *dev,
115 struct comedi_subdevice *s,
116 struct comedi_insn *insn, unsigned int *data);
117 static int serial2002_ao_rinsn(struct comedi_device *dev,
118 struct comedi_subdevice *s,
119 struct comedi_insn *insn, unsigned int *data);
120
121 struct serial_data {
122 enum { is_invalid, is_digital, is_channel } kind;
123 int index;
124 unsigned long value;
125 };
126
127 static long tty_ioctl(struct file *f, unsigned op, unsigned long param)
128 {
129 if (f->f_op->unlocked_ioctl)
130 return f->f_op->unlocked_ioctl(f, op, param);
131
132 return -ENOSYS;
133 }
134
135 static int tty_write(struct file *f, unsigned char *buf, int count)
136 {
137 int result;
138 mm_segment_t oldfs;
139
140 oldfs = get_fs();
141 set_fs(KERNEL_DS);
142 f->f_pos = 0;
143 result = f->f_op->write(f, buf, count, &f->f_pos);
144 set_fs(oldfs);
145 return result;
146 }
147
148 #if 0
149 /*
150 * On 2.6.26.3 this occaisonally gave me page faults, worked around by
151 * settings.c_cc[VMIN] = 0; settings.c_cc[VTIME] = 0
152 */
153 static int tty_available(struct file *f)
154 {
155 long result = 0;
156 mm_segment_t oldfs;
157
158 oldfs = get_fs();
159 set_fs(KERNEL_DS);
160 tty_ioctl(f, FIONREAD, (unsigned long)&result);
161 set_fs(oldfs);
162 return result;
163 }
164 #endif
165
166 static int tty_read(struct file *f, int timeout)
167 {
168 int result;
169
170 result = -1;
171 if (!IS_ERR(f)) {
172 mm_segment_t oldfs;
173
174 oldfs = get_fs();
175 set_fs(KERNEL_DS);
176 if (f->f_op->poll) {
177 struct poll_wqueues table;
178 struct timeval start, now;
179
180 do_gettimeofday(&start);
181 poll_initwait(&table);
182 while (1) {
183 long elapsed;
184 int mask;
185
186 mask = f->f_op->poll(f, &table.pt);
187 if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
188 POLLHUP | POLLERR)) {
189 break;
190 }
191 do_gettimeofday(&now);
192 elapsed =
193 (1000000 * (now.tv_sec - start.tv_sec) +
194 now.tv_usec - start.tv_usec);
195 if (elapsed > timeout) {
196 break;
197 }
198 set_current_state(TASK_INTERRUPTIBLE);
199 schedule_timeout(((timeout -
200 elapsed) * HZ) / 10000);
201 }
202 poll_freewait(&table);
203 {
204 unsigned char ch;
205
206 f->f_pos = 0;
207 if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
208 result = ch;
209 }
210 }
211 } else {
212 /* Device does not support poll, busy wait */
213 int retries = 0;
214 while (1) {
215 unsigned char ch;
216
217 retries++;
218 if (retries >= timeout) {
219 break;
220 }
221
222 f->f_pos = 0;
223 if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
224 result = ch;
225 break;
226 }
227 udelay(100);
228 }
229 }
230 set_fs(oldfs);
231 }
232 return result;
233 }
234
235 static void tty_setspeed(struct file *f, int speed)
236 {
237 mm_segment_t oldfs;
238
239 oldfs = get_fs();
240 set_fs(KERNEL_DS);
241 {
242 /* Set speed */
243 struct termios settings;
244
245 tty_ioctl(f, TCGETS, (unsigned long)&settings);
246 /* printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX)); */
247 settings.c_iflag = 0;
248 settings.c_oflag = 0;
249 settings.c_lflag = 0;
250 settings.c_cflag = CLOCAL | CS8 | CREAD;
251 settings.c_cc[VMIN] = 0;
252 settings.c_cc[VTIME] = 0;
253 switch (speed) {
254 case 2400:{
255 settings.c_cflag |= B2400;
256 }
257 break;
258 case 4800:{
259 settings.c_cflag |= B4800;
260 }
261 break;
262 case 9600:{
263 settings.c_cflag |= B9600;
264 }
265 break;
266 case 19200:{
267 settings.c_cflag |= B19200;
268 }
269 break;
270 case 38400:{
271 settings.c_cflag |= B38400;
272 }
273 break;
274 case 57600:{
275 settings.c_cflag |= B57600;
276 }
277 break;
278 case 115200:{
279 settings.c_cflag |= B115200;
280 }
281 break;
282 default:{
283 settings.c_cflag |= B9600;
284 }
285 break;
286 }
287 tty_ioctl(f, TCSETS, (unsigned long)&settings);
288 /* printk("Speed: %d\n", settings.c_cflag & (CBAUD | CBAUDEX)); */
289 }
290 {
291 /* Set low latency */
292 struct serial_struct settings;
293
294 tty_ioctl(f, TIOCGSERIAL, (unsigned long)&settings);
295 settings.flags |= ASYNC_LOW_LATENCY;
296 tty_ioctl(f, TIOCSSERIAL, (unsigned long)&settings);
297 }
298
299 set_fs(oldfs);
300 }
301
302 static void poll_digital(struct file *f, int channel)
303 {
304 char cmd;
305
306 cmd = 0x40 | (channel & 0x1f);
307 tty_write(f, &cmd, 1);
308 }
309
310 static void poll_channel(struct file *f, int channel)
311 {
312 char cmd;
313
314 cmd = 0x60 | (channel & 0x1f);
315 tty_write(f, &cmd, 1);
316 }
317
318 static struct serial_data serial_read(struct file *f, int timeout)
319 {
320 struct serial_data result;
321 int length;
322
323 result.kind = is_invalid;
324 result.index = 0;
325 result.value = 0;
326 length = 0;
327 while (1) {
328 int data = tty_read(f, timeout);
329
330 length++;
331 if (data < 0) {
332 printk("serial2002 error\n");
333 break;
334 } else if (data & 0x80) {
335 result.value = (result.value << 7) | (data & 0x7f);
336 } else {
337 if (length == 1) {
338 switch ((data >> 5) & 0x03) {
339 case 0:{
340 result.value = 0;
341 result.kind = is_digital;
342 }
343 break;
344 case 1:{
345 result.value = 1;
346 result.kind = is_digital;
347 }
348 break;
349 }
350 } else {
351 result.value =
352 (result.value << 2) | ((data & 0x60) >> 5);
353 result.kind = is_channel;
354 }
355 result.index = data & 0x1f;
356 break;
357 }
358 }
359 return result;
360
361 }
362
363 static void serial_write(struct file *f, struct serial_data data)
364 {
365 if (data.kind == is_digital) {
366 unsigned char ch =
367 ((data.value << 5) & 0x20) | (data.index & 0x1f);
368 tty_write(f, &ch, 1);
369 } else {
370 unsigned char ch[6];
371 int i = 0;
372 if (data.value >= (1L << 30)) {
373 ch[i] = 0x80 | ((data.value >> 30) & 0x03);
374 i++;
375 }
376 if (data.value >= (1L << 23)) {
377 ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
378 i++;
379 }
380 if (data.value >= (1L << 16)) {
381 ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
382 i++;
383 }
384 if (data.value >= (1L << 9)) {
385 ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
386 i++;
387 }
388 ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
389 i++;
390 ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
391 i++;
392 tty_write(f, ch, i);
393 }
394 }
395
396 static int serial_2002_open(struct comedi_device *dev)
397 {
398 int result;
399 char port[20];
400
401 sprintf(port, "/dev/ttyS%d", devpriv->port);
402 devpriv->tty = filp_open(port, O_RDWR, 0);
403 if (IS_ERR(devpriv->tty)) {
404 result = (int)PTR_ERR(devpriv->tty);
405 printk("serial_2002: file open error = %d\n", result);
406 } else {
407 struct config_t {
408
409 short int kind;
410 short int bits;
411 int min;
412 int max;
413 };
414
415 struct config_t *dig_in_config;
416 struct config_t *dig_out_config;
417 struct config_t *chan_in_config;
418 struct config_t *chan_out_config;
419 int i;
420
421 result = 0;
422 dig_in_config = kcalloc(32, sizeof(struct config_t),
423 GFP_KERNEL);
424 dig_out_config = kcalloc(32, sizeof(struct config_t),
425 GFP_KERNEL);
426 chan_in_config = kcalloc(32, sizeof(struct config_t),
427 GFP_KERNEL);
428 chan_out_config = kcalloc(32, sizeof(struct config_t),
429 GFP_KERNEL);
430 if (!dig_in_config || !dig_out_config
431 || !chan_in_config || !chan_out_config) {
432 result = -ENOMEM;
433 goto err_alloc_configs;
434 }
435
436 tty_setspeed(devpriv->tty, devpriv->speed);
437 poll_channel(devpriv->tty, 31); /* Start reading configuration */
438 while (1) {
439 struct serial_data data;
440
441 data = serial_read(devpriv->tty, 1000);
442 if (data.kind != is_channel || data.index != 31
443 || !(data.value & 0xe0)) {
444 break;
445 } else {
446 int command, channel, kind;
447 struct config_t *cur_config = NULL;
448
449 channel = data.value & 0x1f;
450 kind = (data.value >> 5) & 0x7;
451 command = (data.value >> 8) & 0x3;
452 switch (kind) {
453 case 1:{
454 cur_config = dig_in_config;
455 }
456 break;
457 case 2:{
458 cur_config = dig_out_config;
459 }
460 break;
461 case 3:{
462 cur_config = chan_in_config;
463 }
464 break;
465 case 4:{
466 cur_config = chan_out_config;
467 }
468 break;
469 case 5:{
470 cur_config = chan_in_config;
471 }
472 break;
473 }
474
475 if (cur_config) {
476 cur_config[channel].kind = kind;
477 switch (command) {
478 case 0:{
479 cur_config[channel].bits
480 =
481 (data.value >> 10) &
482 0x3f;
483 }
484 break;
485 case 1:{
486 int unit, sign, min;
487 unit =
488 (data.value >> 10) &
489 0x7;
490 sign =
491 (data.value >> 13) &
492 0x1;
493 min =
494 (data.value >> 14) &
495 0xfffff;
496
497 switch (unit) {
498 case 0:{
499 min =
500 min
501 *
502 1000000;
503 }
504 break;
505 case 1:{
506 min =
507 min
508 *
509 1000;
510 }
511 break;
512 case 2:{
513 min =
514 min
515 * 1;
516 }
517 break;
518 }
519 if (sign) {
520 min = -min;
521 }
522 cur_config[channel].min
523 = min;
524 }
525 break;
526 case 2:{
527 int unit, sign, max;
528 unit =
529 (data.value >> 10) &
530 0x7;
531 sign =
532 (data.value >> 13) &
533 0x1;
534 max =
535 (data.value >> 14) &
536 0xfffff;
537
538 switch (unit) {
539 case 0:{
540 max =
541 max
542 *
543 1000000;
544 }
545 break;
546 case 1:{
547 max =
548 max
549 *
550 1000;
551 }
552 break;
553 case 2:{
554 max =
555 max
556 * 1;
557 }
558 break;
559 }
560 if (sign) {
561 max = -max;
562 }
563 cur_config[channel].max
564 = max;
565 }
566 break;
567 }
568 }
569 }
570 }
571 for (i = 0; i <= 4; i++) {
572 /* Fill in subdev data */
573 struct config_t *c;
574 unsigned char *mapping = NULL;
575 struct serial2002_range_table_t *range = NULL;
576 int kind = 0;
577
578 switch (i) {
579 case 0:{
580 c = dig_in_config;
581 mapping = devpriv->digital_in_mapping;
582 kind = 1;
583 }
584 break;
585 case 1:{
586 c = dig_out_config;
587 mapping = devpriv->digital_out_mapping;
588 kind = 2;
589 }
590 break;
591 case 2:{
592 c = chan_in_config;
593 mapping = devpriv->analog_in_mapping;
594 range = devpriv->in_range;
595 kind = 3;
596 }
597 break;
598 case 3:{
599 c = chan_out_config;
600 mapping = devpriv->analog_out_mapping;
601 range = devpriv->out_range;
602 kind = 4;
603 }
604 break;
605 case 4:{
606 c = chan_in_config;
607 mapping = devpriv->encoder_in_mapping;
608 range = devpriv->in_range;
609 kind = 5;
610 }
611 break;
612 default:{
613 c = NULL;
614 }
615 break;
616 }
617 if (c) {
618 struct comedi_subdevice *s;
619 const struct comedi_lrange **range_table_list =
620 NULL;
621 unsigned int *maxdata_list;
622 int j, chan;
623
624 for (chan = 0, j = 0; j < 32; j++) {
625 if (c[j].kind == kind) {
626 chan++;
627 }
628 }
629 s = &dev->subdevices[i];
630 s->n_chan = chan;
631 s->maxdata = 0;
632 kfree(s->maxdata_list);
633 s->maxdata_list = maxdata_list =
634 kmalloc(sizeof(unsigned int) * s->n_chan,
635 GFP_KERNEL);
636 if (!s->maxdata_list)
637 break; /* error handled below */
638 kfree(s->range_table_list);
639 s->range_table = NULL;
640 s->range_table_list = NULL;
641 if (range) {
642 s->range_table_list = range_table_list =
643 kmalloc(sizeof
644 (struct
645 serial2002_range_table_t) *
646 s->n_chan, GFP_KERNEL);
647 if (!s->range_table_list)
648 break; /* err handled below */
649 }
650 for (chan = 0, j = 0; j < 32; j++) {
651 if (c[j].kind == kind) {
652 if (mapping) {
653 mapping[chan] = j;
654 }
655 if (range) {
656 range[j].length = 1;
657 range[j].range.min =
658 c[j].min;
659 range[j].range.max =
660 c[j].max;
661 range_table_list[chan] =
662 (const struct
663 comedi_lrange *)
664 &range[j];
665 }
666 maxdata_list[chan] =
667 ((long long)1 << c[j].bits)
668 - 1;
669 chan++;
670 }
671 }
672 }
673 }
674 if (i <= 4) {
675 /* Failed to allocate maxdata_list or range_table_list
676 * for a subdevice that needed it. */
677 result = -ENOMEM;
678 for (i = 0; i <= 4; i++) {
679 struct comedi_subdevice *s;
680
681 s = &dev->subdevices[i];
682 kfree(s->maxdata_list);
683 s->maxdata_list = NULL;
684 kfree(s->range_table_list);
685 s->range_table_list = NULL;
686 }
687 }
688
689 err_alloc_configs:
690 kfree(dig_in_config);
691 kfree(dig_out_config);
692 kfree(chan_in_config);
693 kfree(chan_out_config);
694
695 if (result) {
696 if (devpriv->tty) {
697 filp_close(devpriv->tty, 0);
698 devpriv->tty = NULL;
699 }
700 }
701 }
702 return result;
703 }
704
705 static void serial_2002_close(struct comedi_device *dev)
706 {
707 if (!IS_ERR(devpriv->tty) && (devpriv->tty != 0)) {
708 filp_close(devpriv->tty, 0);
709 }
710 }
711
712 static int serial2002_di_rinsn(struct comedi_device *dev,
713 struct comedi_subdevice *s,
714 struct comedi_insn *insn, unsigned int *data)
715 {
716 int n;
717 int chan;
718
719 chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
720 for (n = 0; n < insn->n; n++) {
721 struct serial_data read;
722
723 poll_digital(devpriv->tty, chan);
724 while (1) {
725 read = serial_read(devpriv->tty, 1000);
726 if (read.kind != is_digital || read.index == chan) {
727 break;
728 }
729 }
730 data[n] = read.value;
731 }
732 return n;
733 }
734
735 static int serial2002_do_winsn(struct comedi_device *dev,
736 struct comedi_subdevice *s,
737 struct comedi_insn *insn, unsigned int *data)
738 {
739 int n;
740 int chan;
741
742 chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
743 for (n = 0; n < insn->n; n++) {
744 struct serial_data write;
745
746 write.kind = is_digital;
747 write.index = chan;
748 write.value = data[n];
749 serial_write(devpriv->tty, write);
750 }
751 return n;
752 }
753
754 static int serial2002_ai_rinsn(struct comedi_device *dev,
755 struct comedi_subdevice *s,
756 struct comedi_insn *insn, unsigned int *data)
757 {
758 int n;
759 int chan;
760
761 chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
762 for (n = 0; n < insn->n; n++) {
763 struct serial_data read;
764
765 poll_channel(devpriv->tty, chan);
766 while (1) {
767 read = serial_read(devpriv->tty, 1000);
768 if (read.kind != is_channel || read.index == chan) {
769 break;
770 }
771 }
772 data[n] = read.value;
773 }
774 return n;
775 }
776
777 static int serial2002_ao_winsn(struct comedi_device *dev,
778 struct comedi_subdevice *s,
779 struct comedi_insn *insn, unsigned int *data)
780 {
781 int n;
782 int chan;
783
784 chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
785 for (n = 0; n < insn->n; n++) {
786 struct serial_data write;
787
788 write.kind = is_channel;
789 write.index = chan;
790 write.value = data[n];
791 serial_write(devpriv->tty, write);
792 devpriv->ao_readback[chan] = data[n];
793 }
794 return n;
795 }
796
797 static int serial2002_ao_rinsn(struct comedi_device *dev,
798 struct comedi_subdevice *s,
799 struct comedi_insn *insn, unsigned int *data)
800 {
801 int n;
802 int chan = CR_CHAN(insn->chanspec);
803
804 for (n = 0; n < insn->n; n++) {
805 data[n] = devpriv->ao_readback[chan];
806 }
807
808 return n;
809 }
810
811 static int serial2002_ei_rinsn(struct comedi_device *dev,
812 struct comedi_subdevice *s,
813 struct comedi_insn *insn, unsigned int *data)
814 {
815 int n;
816 int chan;
817
818 chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
819 for (n = 0; n < insn->n; n++) {
820 struct serial_data read;
821
822 poll_channel(devpriv->tty, chan);
823 while (1) {
824 read = serial_read(devpriv->tty, 1000);
825 if (read.kind != is_channel || read.index == chan) {
826 break;
827 }
828 }
829 data[n] = read.value;
830 }
831 return n;
832 }
833
834 static int serial2002_attach(struct comedi_device *dev,
835 struct comedi_devconfig *it)
836 {
837 struct comedi_subdevice *s;
838
839 printk("comedi%d: serial2002: ", dev->minor);
840 dev->board_name = thisboard->name;
841 if (alloc_private(dev, sizeof(struct serial2002_private)) < 0) {
842 return -ENOMEM;
843 }
844 dev->open = serial_2002_open;
845 dev->close = serial_2002_close;
846 devpriv->port = it->options[0];
847 devpriv->speed = it->options[1];
848 printk("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
849
850 if (alloc_subdevices(dev, 5) < 0)
851 return -ENOMEM;
852
853 /* digital input subdevice */
854 s = dev->subdevices + 0;
855 s->type = COMEDI_SUBD_DI;
856 s->subdev_flags = SDF_READABLE;
857 s->n_chan = 0;
858 s->maxdata = 1;
859 s->range_table = &range_digital;
860 s->insn_read = &serial2002_di_rinsn;
861
862 /* digital output subdevice */
863 s = dev->subdevices + 1;
864 s->type = COMEDI_SUBD_DO;
865 s->subdev_flags = SDF_WRITEABLE;
866 s->n_chan = 0;
867 s->maxdata = 1;
868 s->range_table = &range_digital;
869 s->insn_write = &serial2002_do_winsn;
870
871 /* analog input subdevice */
872 s = dev->subdevices + 2;
873 s->type = COMEDI_SUBD_AI;
874 s->subdev_flags = SDF_READABLE | SDF_GROUND;
875 s->n_chan = 0;
876 s->maxdata = 1;
877 s->range_table = 0;
878 s->insn_read = &serial2002_ai_rinsn;
879
880 /* analog output subdevice */
881 s = dev->subdevices + 3;
882 s->type = COMEDI_SUBD_AO;
883 s->subdev_flags = SDF_WRITEABLE;
884 s->n_chan = 0;
885 s->maxdata = 1;
886 s->range_table = 0;
887 s->insn_write = &serial2002_ao_winsn;
888 s->insn_read = &serial2002_ao_rinsn;
889
890 /* encoder input subdevice */
891 s = dev->subdevices + 4;
892 s->type = COMEDI_SUBD_COUNTER;
893 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
894 s->n_chan = 0;
895 s->maxdata = 1;
896 s->range_table = 0;
897 s->insn_read = &serial2002_ei_rinsn;
898
899 return 1;
900 }
901
902 static int serial2002_detach(struct comedi_device *dev)
903 {
904 struct comedi_subdevice *s;
905 int i;
906
907 printk("comedi%d: serial2002: remove\n", dev->minor);
908 for (i = 0; i < 5; i++) {
909 s = &dev->subdevices[i];
910 if (s->maxdata_list) {
911 kfree(s->maxdata_list);
912 }
913 if (s->range_table_list) {
914 kfree(s->range_table_list);
915 }
916 }
917 return 0;
918 }
919
920 static int __init driver_serial2002_init_module(void)
921 {
922 return comedi_driver_register(&driver_serial2002);
923 }
924
925 static void __exit driver_serial2002_cleanup_module(void)
926 {
927 comedi_driver_unregister(&driver_serial2002);
928 }
929
930 module_init(driver_serial2002_init_module);
931 module_exit(driver_serial2002_cleanup_module);
932
933 MODULE_AUTHOR("Comedi http://www.comedi.org");
934 MODULE_DESCRIPTION("Comedi low-level driver");
935 MODULE_LICENSE("GPL");