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