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 <linux/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 set_current_state(TASK_INTERRUPTIBLE);
198 schedule_timeout(((timeout -
199 elapsed) * HZ) / 10000);
200 }
201 poll_freewait(&table);
202 {
203 unsigned char ch;
204
205 f->f_pos = 0;
206 if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1)
207 result = ch;
208 }
209 } else {
210
211 int retries = 0;
212 while (1) {
213 unsigned char ch;
214
215 retries++;
216 if (retries >= timeout)
217 break;
218
219 f->f_pos = 0;
220 if (f->f_op->read(f, &ch, 1, &f->f_pos) == 1) {
221 result = ch;
222 break;
223 }
224 udelay(100);
225 }
226 }
227 set_fs(oldfs);
228 }
229 return result;
230}
231
232static void tty_setspeed(struct file *f, int speed)
233{
234 mm_segment_t oldfs;
235
236 oldfs = get_fs();
237 set_fs(KERNEL_DS);
238 {
239
240 struct termios settings;
241
242 tty_ioctl(f, TCGETS, (unsigned long)&settings);
243
244 settings.c_iflag = 0;
245 settings.c_oflag = 0;
246 settings.c_lflag = 0;
247 settings.c_cflag = CLOCAL | CS8 | CREAD;
248 settings.c_cc[VMIN] = 0;
249 settings.c_cc[VTIME] = 0;
250 switch (speed) {
251 case 2400:{
252 settings.c_cflag |= B2400;
253 }
254 break;
255 case 4800:{
256 settings.c_cflag |= B4800;
257 }
258 break;
259 case 9600:{
260 settings.c_cflag |= B9600;
261 }
262 break;
263 case 19200:{
264 settings.c_cflag |= B19200;
265 }
266 break;
267 case 38400:{
268 settings.c_cflag |= B38400;
269 }
270 break;
271 case 57600:{
272 settings.c_cflag |= B57600;
273 }
274 break;
275 case 115200:{
276 settings.c_cflag |= B115200;
277 }
278 break;
279 default:{
280 settings.c_cflag |= B9600;
281 }
282 break;
283 }
284 tty_ioctl(f, TCSETS, (unsigned long)&settings);
285
286 }
287 {
288
289 struct serial_struct settings;
290
291 tty_ioctl(f, TIOCGSERIAL, (unsigned long)&settings);
292 settings.flags |= ASYNC_LOW_LATENCY;
293 tty_ioctl(f, TIOCSSERIAL, (unsigned long)&settings);
294 }
295
296 set_fs(oldfs);
297}
298
299static void poll_digital(struct file *f, int channel)
300{
301 char cmd;
302
303 cmd = 0x40 | (channel & 0x1f);
304 tty_write(f, &cmd, 1);
305}
306
307static void poll_channel(struct file *f, int channel)
308{
309 char cmd;
310
311 cmd = 0x60 | (channel & 0x1f);
312 tty_write(f, &cmd, 1);
313}
314
315static struct serial_data serial_read(struct file *f, int timeout)
316{
317 struct serial_data result;
318 int length;
319
320 result.kind = is_invalid;
321 result.index = 0;
322 result.value = 0;
323 length = 0;
324 while (1) {
325 int data = tty_read(f, timeout);
326
327 length++;
328 if (data < 0) {
329 printk(KERN_ERR "serial2002 error\n");
330 break;
331 } else if (data & 0x80) {
332 result.value = (result.value << 7) | (data & 0x7f);
333 } else {
334 if (length == 1) {
335 switch ((data >> 5) & 0x03) {
336 case 0:{
337 result.value = 0;
338 result.kind = is_digital;
339 }
340 break;
341 case 1:{
342 result.value = 1;
343 result.kind = is_digital;
344 }
345 break;
346 }
347 } else {
348 result.value =
349 (result.value << 2) | ((data & 0x60) >> 5);
350 result.kind = is_channel;
351 }
352 result.index = data & 0x1f;
353 break;
354 }
355 }
356 return result;
357
358}
359
360static void serial_write(struct file *f, struct serial_data data)
361{
362 if (data.kind == is_digital) {
363 unsigned char ch =
364 ((data.value << 5) & 0x20) | (data.index & 0x1f);
365 tty_write(f, &ch, 1);
366 } else {
367 unsigned char ch[6];
368 int i = 0;
369 if (data.value >= (1L << 30)) {
370 ch[i] = 0x80 | ((data.value >> 30) & 0x03);
371 i++;
372 }
373 if (data.value >= (1L << 23)) {
374 ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
375 i++;
376 }
377 if (data.value >= (1L << 16)) {
378 ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
379 i++;
380 }
381 if (data.value >= (1L << 9)) {
382 ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
383 i++;
384 }
385 ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
386 i++;
387 ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
388 i++;
389 tty_write(f, ch, i);
390 }
391}
392
393static int serial_2002_open(struct comedi_device *dev)
394{
395 int result;
396 char port[20];
397
398 sprintf(port, "/dev/ttyS%d", devpriv->port);
399 devpriv->tty = filp_open(port, O_RDWR, 0);
400 if (IS_ERR(devpriv->tty)) {
401 result = (int)PTR_ERR(devpriv->tty);
402 printk(KERN_ERR "serial_2002: file open error = %d\n", result);
403 } else {
404 struct config_t {
405
406 short int kind;
407 short int bits;
408 int min;
409 int max;
410 };
411
412 struct config_t *dig_in_config;
413 struct config_t *dig_out_config;
414 struct config_t *chan_in_config;
415 struct config_t *chan_out_config;
416 int i;
417
418 result = 0;
419 dig_in_config = kcalloc(32, sizeof(struct config_t),
420 GFP_KERNEL);
421 dig_out_config = kcalloc(32, sizeof(struct config_t),
422 GFP_KERNEL);
423 chan_in_config = kcalloc(32, sizeof(struct config_t),
424 GFP_KERNEL);
425 chan_out_config = kcalloc(32, sizeof(struct config_t),
426 GFP_KERNEL);
427 if (!dig_in_config || !dig_out_config
428 || !chan_in_config || !chan_out_config) {
429 result = -ENOMEM;
430 goto err_alloc_configs;
431 }
432
433 tty_setspeed(devpriv->tty, devpriv->speed);
434 poll_channel(devpriv->tty, 31);
435 while (1) {
436 struct serial_data data;
437
438 data = serial_read(devpriv->tty, 1000);
439 if (data.kind != is_channel || data.index != 31
440 || !(data.value & 0xe0)) {
441 break;
442 } else {
443 int command, channel, kind;
444 struct config_t *cur_config = NULL;
445
446 channel = data.value & 0x1f;
447 kind = (data.value >> 5) & 0x7;
448 command = (data.value >> 8) & 0x3;
449 switch (kind) {
450 case 1:{
451 cur_config = dig_in_config;
452 }
453 break;
454 case 2:{
455 cur_config = dig_out_config;
456 }
457 break;
458 case 3:{
459 cur_config = chan_in_config;
460 }
461 break;
462 case 4:{
463 cur_config = chan_out_config;
464 }
465 break;
466 case 5:{
467 cur_config = chan_in_config;
468 }
469 break;
470 }
471
472 if (cur_config) {
473 cur_config[channel].kind = kind;
474 switch (command) {
475 case 0:{
476 cur_config[channel].bits
477 =
478 (data.value >> 10) &
479 0x3f;
480 }
481 break;
482 case 1:{
483 int unit, sign, min;
484 unit =
485 (data.value >> 10) &
486 0x7;
487 sign =
488 (data.value >> 13) &
489 0x1;
490 min =
491 (data.value >> 14) &
492 0xfffff;
493
494 switch (unit) {
495 case 0:{
496 min =
497 min
498 *
499 1000000;
500 }
501 break;
502 case 1:{
503 min =
504 min
505 *
506 1000;
507 }
508 break;
509 case 2:{
510 min =
511 min
512 * 1;
513 }
514 break;
515 }
516 if (sign)
517 min = -min;
518 cur_config[channel].min
519 = min;
520 }
521 break;
522 case 2:{
523 int unit, sign, max;
524 unit =
525 (data.value >> 10) &
526 0x7;
527 sign =
528 (data.value >> 13) &
529 0x1;
530 max =
531 (data.value >> 14) &
532 0xfffff;
533
534 switch (unit) {
535 case 0:{
536 max =
537 max
538 *
539 1000000;
540 }
541 break;
542 case 1:{
543 max =
544 max
545 *
546 1000;
547 }
548 break;
549 case 2:{
550 max =
551 max
552 * 1;
553 }
554 break;
555 }
556 if (sign)
557 max = -max;
558 cur_config[channel].max
559 = max;
560 }
561 break;
562 }
563 }
564 }
565 }
566 for (i = 0; i <= 4; i++) {
567
568 struct config_t *c;
569 unsigned char *mapping = NULL;
570 struct serial2002_range_table_t *range = NULL;
571 int kind = 0;
572
573 switch (i) {
574 case 0:{
575 c = dig_in_config;
576 mapping = devpriv->digital_in_mapping;
577 kind = 1;
578 }
579 break;
580 case 1:{
581 c = dig_out_config;
582 mapping = devpriv->digital_out_mapping;
583 kind = 2;
584 }
585 break;
586 case 2:{
587 c = chan_in_config;
588 mapping = devpriv->analog_in_mapping;
589 range = devpriv->in_range;
590 kind = 3;
591 }
592 break;
593 case 3:{
594 c = chan_out_config;
595 mapping = devpriv->analog_out_mapping;
596 range = devpriv->out_range;
597 kind = 4;
598 }
599 break;
600 case 4:{
601 c = chan_in_config;
602 mapping = devpriv->encoder_in_mapping;
603 range = devpriv->in_range;
604 kind = 5;
605 }
606 break;
607 default:{
608 c = NULL;
609 }
610 break;
611 }
612 if (c) {
613 struct comedi_subdevice *s;
614 const struct comedi_lrange **range_table_list =
615 NULL;
616 unsigned int *maxdata_list;
617 int j, chan;
618
619 for (chan = 0, j = 0; j < 32; j++) {
620 if (c[j].kind == kind)
621 chan++;
622 }
623 s = &dev->subdevices[i];
624 s->n_chan = chan;
625 s->maxdata = 0;
626 kfree(s->maxdata_list);
627 s->maxdata_list = maxdata_list =
628 kmalloc(sizeof(unsigned int) * s->n_chan,
629 GFP_KERNEL);
630 if (!s->maxdata_list)
631 break;
632 kfree(s->range_table_list);
633 s->range_table = NULL;
634 s->range_table_list = NULL;
635 if (range) {
636 s->range_table_list = range_table_list =
637 kmalloc(sizeof
638 (struct
639 serial2002_range_table_t) *
640 s->n_chan, GFP_KERNEL);
641 if (!s->range_table_list)
642 break;
643 }
644 for (chan = 0, j = 0; j < 32; j++) {
645 if (c[j].kind == kind) {
646 if (mapping)
647 mapping[chan] = j;
648 if (range) {
649 range[j].length = 1;
650 range[j].range.min =
651 c[j].min;
652 range[j].range.max =
653 c[j].max;
654 range_table_list[chan] =
655 (const struct
656 comedi_lrange *)
657 &range[j];
658 }
659 maxdata_list[chan] =
660 ((long long)1 << c[j].bits)
661 - 1;
662 chan++;
663 }
664 }
665 }
666 }
667 if (i <= 4) {
668
669
670 result = -ENOMEM;
671 for (i = 0; i <= 4; i++) {
672 struct comedi_subdevice *s;
673
674 s = &dev->subdevices[i];
675 kfree(s->maxdata_list);
676 s->maxdata_list = NULL;
677 kfree(s->range_table_list);
678 s->range_table_list = NULL;
679 }
680 }
681
682err_alloc_configs:
683 kfree(dig_in_config);
684 kfree(dig_out_config);
685 kfree(chan_in_config);
686 kfree(chan_out_config);
687
688 if (result) {
689 if (devpriv->tty) {
690 filp_close(devpriv->tty, 0);
691 devpriv->tty = NULL;
692 }
693 }
694 }
695 return result;
696}
697
698static void serial_2002_close(struct comedi_device *dev)
699{
700 if (!IS_ERR(devpriv->tty) && (devpriv->tty != 0))
701 filp_close(devpriv->tty, 0);
702}
703
704static int serial2002_di_rinsn(struct comedi_device *dev,
705 struct comedi_subdevice *s,
706 struct comedi_insn *insn, unsigned int *data)
707{
708 int n;
709 int chan;
710
711 chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
712 for (n = 0; n < insn->n; n++) {
713 struct serial_data read;
714
715 poll_digital(devpriv->tty, chan);
716 while (1) {
717 read = serial_read(devpriv->tty, 1000);
718 if (read.kind != is_digital || read.index == chan)
719 break;
720 }
721 data[n] = read.value;
722 }
723 return n;
724}
725
726static int serial2002_do_winsn(struct comedi_device *dev,
727 struct comedi_subdevice *s,
728 struct comedi_insn *insn, unsigned int *data)
729{
730 int n;
731 int chan;
732
733 chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
734 for (n = 0; n < insn->n; n++) {
735 struct serial_data write;
736
737 write.kind = is_digital;
738 write.index = chan;
739 write.value = data[n];
740 serial_write(devpriv->tty, write);
741 }
742 return n;
743}
744
745static int serial2002_ai_rinsn(struct comedi_device *dev,
746 struct comedi_subdevice *s,
747 struct comedi_insn *insn, unsigned int *data)
748{
749 int n;
750 int chan;
751
752 chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
753 for (n = 0; n < insn->n; n++) {
754 struct serial_data read;
755
756 poll_channel(devpriv->tty, chan);
757 while (1) {
758 read = serial_read(devpriv->tty, 1000);
759 if (read.kind != is_channel || read.index == chan)
760 break;
761 }
762 data[n] = read.value;
763 }
764 return n;
765}
766
767static int serial2002_ao_winsn(struct comedi_device *dev,
768 struct comedi_subdevice *s,
769 struct comedi_insn *insn, unsigned int *data)
770{
771 int n;
772 int chan;
773
774 chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
775 for (n = 0; n < insn->n; n++) {
776 struct serial_data write;
777
778 write.kind = is_channel;
779 write.index = chan;
780 write.value = data[n];
781 serial_write(devpriv->tty, write);
782 devpriv->ao_readback[chan] = data[n];
783 }
784 return n;
785}
786
787static int serial2002_ao_rinsn(struct comedi_device *dev,
788 struct comedi_subdevice *s,
789 struct comedi_insn *insn, unsigned int *data)
790{
791 int n;
792 int chan = CR_CHAN(insn->chanspec);
793
794 for (n = 0; n < insn->n; n++)
795 data[n] = devpriv->ao_readback[chan];
796
797 return n;
798}
799
800static int serial2002_ei_rinsn(struct comedi_device *dev,
801 struct comedi_subdevice *s,
802 struct comedi_insn *insn, unsigned int *data)
803{
804 int n;
805 int chan;
806
807 chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
808 for (n = 0; n < insn->n; n++) {
809 struct serial_data read;
810
811 poll_channel(devpriv->tty, chan);
812 while (1) {
813 read = serial_read(devpriv->tty, 1000);
814 if (read.kind != is_channel || read.index == chan)
815 break;
816 }
817 data[n] = read.value;
818 }
819 return n;
820}
821
822static int serial2002_attach(struct comedi_device *dev,
823 struct comedi_devconfig *it)
824{
825 struct comedi_subdevice *s;
826
827 printk("comedi%d: serial2002: ", dev->minor);
828 dev->board_name = thisboard->name;
829 if (alloc_private(dev, sizeof(struct serial2002_private)) < 0)
830 return -ENOMEM;
831 dev->open = serial_2002_open;
832 dev->close = serial_2002_close;
833 devpriv->port = it->options[0];
834 devpriv->speed = it->options[1];
835 printk("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
836
837 if (alloc_subdevices(dev, 5) < 0)
838 return -ENOMEM;
839
840
841 s = dev->subdevices + 0;
842 s->type = COMEDI_SUBD_DI;
843 s->subdev_flags = SDF_READABLE;
844 s->n_chan = 0;
845 s->maxdata = 1;
846 s->range_table = &range_digital;
847 s->insn_read = &serial2002_di_rinsn;
848
849
850 s = dev->subdevices + 1;
851 s->type = COMEDI_SUBD_DO;
852 s->subdev_flags = SDF_WRITEABLE;
853 s->n_chan = 0;
854 s->maxdata = 1;
855 s->range_table = &range_digital;
856 s->insn_write = &serial2002_do_winsn;
857
858
859 s = dev->subdevices + 2;
860 s->type = COMEDI_SUBD_AI;
861 s->subdev_flags = SDF_READABLE | SDF_GROUND;
862 s->n_chan = 0;
863 s->maxdata = 1;
864 s->range_table = 0;
865 s->insn_read = &serial2002_ai_rinsn;
866
867
868 s = dev->subdevices + 3;
869 s->type = COMEDI_SUBD_AO;
870 s->subdev_flags = SDF_WRITEABLE;
871 s->n_chan = 0;
872 s->maxdata = 1;
873 s->range_table = 0;
874 s->insn_write = &serial2002_ao_winsn;
875 s->insn_read = &serial2002_ao_rinsn;
876
877
878 s = dev->subdevices + 4;
879 s->type = COMEDI_SUBD_COUNTER;
880 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
881 s->n_chan = 0;
882 s->maxdata = 1;
883 s->range_table = 0;
884 s->insn_read = &serial2002_ei_rinsn;
885
886 return 1;
887}
888
889static int serial2002_detach(struct comedi_device *dev)
890{
891 struct comedi_subdevice *s;
892 int i;
893
894 printk("comedi%d: serial2002: remove\n", dev->minor);
895 for (i = 0; i < 5; i++) {
896 s = &dev->subdevices[i];
897 kfree(s->maxdata_list);
898 kfree(s->range_table_list);
899 }
900 return 0;
901}
902
903static int __init driver_serial2002_init_module(void)
904{
905 return comedi_driver_register(&driver_serial2002);
906}
907
908static void __exit driver_serial2002_cleanup_module(void)
909{
910 comedi_driver_unregister(&driver_serial2002);
911}
912
913module_init(driver_serial2002_init_module);
914module_exit(driver_serial2002_cleanup_module);
915
916MODULE_AUTHOR("Comedi http://www.comedi.org");
917MODULE_DESCRIPTION("Comedi low-level driver");
918MODULE_LICENSE("GPL");
919