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#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
48
49
50
51struct serial2002_board {
52 const char *name;
53};
54
55static const struct serial2002_board serial2002_boards[] = {
56 {
57 .name = "serial2002"}
58};
59
60
61
62
63#define thisboard ((const struct serial2002_board *)dev->board_ptr)
64
65struct serial2002_range_table_t {
66
67
68 int length;
69 struct comedi_krange range;
70};
71
72struct serial2002_private {
73
74 int port;
75 int speed;
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
88
89
90#define devpriv ((struct serial2002_private *)dev->private)
91
92static int serial2002_attach(struct comedi_device *dev,
93 struct comedi_devconfig *it);
94static int serial2002_detach(struct comedi_device *dev);
95struct 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
105static int serial2002_di_rinsn(struct comedi_device *dev,
106 struct comedi_subdevice *s,
107 struct comedi_insn *insn, unsigned int *data);
108static int serial2002_do_winsn(struct comedi_device *dev,
109 struct comedi_subdevice *s,
110 struct comedi_insn *insn, unsigned int *data);
111static int serial2002_ai_rinsn(struct comedi_device *dev,
112 struct comedi_subdevice *s,
113 struct comedi_insn *insn, unsigned int *data);
114static int serial2002_ao_winsn(struct comedi_device *dev,
115 struct comedi_subdevice *s,
116 struct comedi_insn *insn, unsigned int *data);
117static int serial2002_ao_rinsn(struct comedi_device *dev,
118 struct comedi_subdevice *s,
119 struct comedi_insn *insn, unsigned int *data);
120
121struct serial_data {
122 enum { is_invalid, is_digital, is_channel } kind;
123 int index;
124 unsigned long value;
125};
126
127static 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
135static 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
151
152
153static 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
166static 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
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
235static 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
243 struct termios settings;
244
245 tty_ioctl(f, TCGETS, (unsigned long)&settings);
246
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
289 }
290 {
291
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
302static 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
310static 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
318static 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
363static 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
396static 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);
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
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;
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;
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
676
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
689err_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
705static 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
712static 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
735static 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
754static 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
777static 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
797static 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
811static 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
834static 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
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
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
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
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
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
902static 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 kfree(s->maxdata_list);
911 kfree(s->range_table_list);
912 }
913 return 0;
914}
915
916static int __init driver_serial2002_init_module(void)
917{
918 return comedi_driver_register(&driver_serial2002);
919}
920
921static void __exit driver_serial2002_cleanup_module(void)
922{
923 comedi_driver_unregister(&driver_serial2002);
924}
925
926module_init(driver_serial2002_init_module);
927module_exit(driver_serial2002_cleanup_module);
928
929MODULE_AUTHOR("Comedi http://www.comedi.org");
930MODULE_DESCRIPTION("Comedi low-level driver");
931MODULE_LICENSE("GPL");
932