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