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