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