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
19/*
20Driver: serial2002
21Description: Driver for serial connected hardware
22Devices:
23Author: Anders Blomdell
24Updated: Fri,  7 Jun 2002 12:56:45 -0700
25Status: in development
26
27*/
28
29#include <linux/module.h>
30#include "../comedidev.h"
31
32#include <linux/delay.h>
33#include <linux/sched.h>
34#include <linux/slab.h>
35
36#include <linux/termios.h>
37#include <asm/ioctls.h>
38#include <linux/serial.h>
39#include <linux/poll.h>
40
41struct serial2002_range_table_t {
42
43	/*  HACK... */
44	int length;
45	struct comedi_krange range;
46};
47
48struct serial2002_private {
49
50	int port;		/*  /dev/ttyS<port> */
51	int speed;		/*  baudrate */
52	struct file *tty;
53	unsigned int ao_readback[32];
54	unsigned char digital_in_mapping[32];
55	unsigned char digital_out_mapping[32];
56	unsigned char analog_in_mapping[32];
57	unsigned char analog_out_mapping[32];
58	unsigned char encoder_in_mapping[32];
59	struct serial2002_range_table_t in_range[32], out_range[32];
60};
61
62struct serial_data {
63	enum { is_invalid, is_digital, is_channel } kind;
64	int index;
65	unsigned long value;
66};
67
68/*
69 * The configuration serial_data.value read from the device is
70 * a bitmask that defines specific options of a channel:
71 *
72 * 4:0 - the channel to configure
73 * 7:5 - the kind of channel
74 * 9:8 - the command used to configure the channel
75 *
76 * The remaining bits vary in use depending on the command:
77 *
78 * BITS     15:10 - the channel bits (maxdata)
79 * MIN/MAX  12:10 - the units multiplier for the scale
80 *          13    - the sign of the scale
81 *          33:14 - the base value for the range
82 */
83#define S2002_CFG_CHAN(x)		((x) & 0x1f)
84#define S2002_CFG_KIND(x)		(((x) >> 5) & 0x7)
85#define S2002_CFG_KIND_INVALID		0
86#define S2002_CFG_KIND_DIGITAL_IN	1
87#define S2002_CFG_KIND_DIGITAL_OUT	2
88#define S2002_CFG_KIND_ANALOG_IN	3
89#define S2002_CFG_KIND_ANALOG_OUT	4
90#define S2002_CFG_KIND_ENCODER_IN	5
91#define S2002_CFG_CMD(x)		(((x) >> 8) & 0x3)
92#define S2002_CFG_CMD_BITS		0
93#define S2002_CFG_CMD_MIN		1
94#define S2002_CFG_CMD_MAX		2
95#define S2002_CFG_BITS(x)		(((x) >> 10) & 0x3f)
96#define S2002_CFG_UNITS(x)		(((x) >> 10) & 0x7)
97#define S2002_CFG_SIGN(x)		(((x) >> 13) & 0x1)
98#define S2002_CFG_BASE(x)		(((x) >> 14) & 0xfffff)
99
100static long serial2002_tty_ioctl(struct file *f, unsigned op,
101				 unsigned long param)
102{
103	if (f->f_op->unlocked_ioctl)
104		return f->f_op->unlocked_ioctl(f, op, param);
105
106	return -ENOSYS;
107}
108
109static int serial2002_tty_write(struct file *f, unsigned char *buf, int count)
110{
111	const char __user *p = (__force const char __user *)buf;
112	int result;
113	mm_segment_t oldfs;
114
115	oldfs = get_fs();
116	set_fs(KERNEL_DS);
117	f->f_pos = 0;
118	result = f->f_op->write(f, p, count, &f->f_pos);
119	set_fs(oldfs);
120	return result;
121}
122
123static int serial2002_tty_readb(struct file *f, unsigned char *buf)
124{
125	char __user *p = (__force char __user *)buf;
126
127	f->f_pos = 0;
128	return f->f_op->read(f, p, 1, &f->f_pos);
129}
130
131static void serial2002_tty_read_poll_wait(struct file *f, int timeout)
132{
133	struct poll_wqueues table;
134	struct timeval start, now;
135
136	do_gettimeofday(&start);
137	poll_initwait(&table);
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 = (1000000 * (now.tv_sec - start.tv_sec) +
149			  now.tv_usec - start.tv_usec);
150		if (elapsed > timeout)
151			break;
152		set_current_state(TASK_INTERRUPTIBLE);
153		schedule_timeout(((timeout - elapsed) * HZ) / 10000);
154	}
155	poll_freewait(&table);
156}
157
158static int serial2002_tty_read(struct file *f, int timeout)
159{
160	unsigned char ch;
161	int result;
162
163	result = -1;
164	if (!IS_ERR(f)) {
165		mm_segment_t oldfs;
166
167		oldfs = get_fs();
168		set_fs(KERNEL_DS);
169		if (f->f_op->poll) {
170			serial2002_tty_read_poll_wait(f, timeout);
171
172			if (serial2002_tty_readb(f, &ch) == 1)
173				result = ch;
174		} else {
175			/* Device does not support poll, busy wait */
176			int retries = 0;
177
178			while (1) {
179				retries++;
180				if (retries >= timeout)
181					break;
182
183				if (serial2002_tty_readb(f, &ch) == 1) {
184					result = ch;
185					break;
186				}
187				udelay(100);
188			}
189		}
190		set_fs(oldfs);
191	}
192	return result;
193}
194
195static void serial2002_tty_setspeed(struct file *f, int speed)
196{
197	struct termios termios;
198	struct serial_struct serial;
199	mm_segment_t oldfs;
200
201	oldfs = get_fs();
202	set_fs(KERNEL_DS);
203
204	/* Set speed */
205	serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios);
206	termios.c_iflag = 0;
207	termios.c_oflag = 0;
208	termios.c_lflag = 0;
209	termios.c_cflag = CLOCAL | CS8 | CREAD;
210	termios.c_cc[VMIN] = 0;
211	termios.c_cc[VTIME] = 0;
212	switch (speed) {
213	case 2400:
214		termios.c_cflag |= B2400;
215		break;
216	case 4800:
217		termios.c_cflag |= B4800;
218		break;
219	case 9600:
220		termios.c_cflag |= B9600;
221		break;
222	case 19200:
223		termios.c_cflag |= B19200;
224		break;
225	case 38400:
226		termios.c_cflag |= B38400;
227		break;
228	case 57600:
229		termios.c_cflag |= B57600;
230		break;
231	case 115200:
232		termios.c_cflag |= B115200;
233		break;
234	default:
235		termios.c_cflag |= B9600;
236		break;
237	}
238	serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios);
239
240	/* Set low latency */
241	serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial);
242	serial.flags |= ASYNC_LOW_LATENCY;
243	serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial);
244
245	set_fs(oldfs);
246}
247
248static void serial2002_poll_digital(struct file *f, int channel)
249{
250	char cmd;
251
252	cmd = 0x40 | (channel & 0x1f);
253	serial2002_tty_write(f, &cmd, 1);
254}
255
256static void serial2002_poll_channel(struct file *f, int channel)
257{
258	char cmd;
259
260	cmd = 0x60 | (channel & 0x1f);
261	serial2002_tty_write(f, &cmd, 1);
262}
263
264static struct serial_data serial2002_read(struct file *f, int timeout)
265{
266	struct serial_data result;
267	int length;
268
269	result.kind = is_invalid;
270	result.index = 0;
271	result.value = 0;
272	length = 0;
273	while (1) {
274		int data = serial2002_tty_read(f, timeout);
275
276		length++;
277		if (data < 0) {
278			break;
279		} else if (data & 0x80) {
280			result.value = (result.value << 7) | (data & 0x7f);
281		} else {
282			if (length == 1) {
283				switch ((data >> 5) & 0x03) {
284				case 0:
285					result.value = 0;
286					result.kind = is_digital;
287					break;
288				case 1:
289					result.value = 1;
290					result.kind = is_digital;
291					break;
292				}
293			} else {
294				result.value =
295				    (result.value << 2) | ((data & 0x60) >> 5);
296				result.kind = is_channel;
297			}
298			result.index = data & 0x1f;
299			break;
300		}
301	}
302	return result;
303
304}
305
306static void serial2002_write(struct file *f, struct serial_data data)
307{
308	if (data.kind == is_digital) {
309		unsigned char ch =
310		    ((data.value << 5) & 0x20) | (data.index & 0x1f);
311		serial2002_tty_write(f, &ch, 1);
312	} else {
313		unsigned char ch[6];
314		int i = 0;
315
316		if (data.value >= (1L << 30)) {
317			ch[i] = 0x80 | ((data.value >> 30) & 0x03);
318			i++;
319		}
320		if (data.value >= (1L << 23)) {
321			ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
322			i++;
323		}
324		if (data.value >= (1L << 16)) {
325			ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
326			i++;
327		}
328		if (data.value >= (1L << 9)) {
329			ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
330			i++;
331		}
332		ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
333		i++;
334		ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
335		i++;
336		serial2002_tty_write(f, ch, i);
337	}
338}
339
340struct config_t {
341	short int kind;
342	short int bits;
343	int min;
344	int max;
345};
346
347static int serial2002_setup_subdevice(struct comedi_subdevice *s,
348				      struct config_t *cfg,
349				      struct serial2002_range_table_t *range,
350				      unsigned char *mapping,
351				      int kind)
352{
353	const struct comedi_lrange **range_table_list = NULL;
354	unsigned int *maxdata_list;
355	int j, chan;
356
357	for (chan = 0, j = 0; j < 32; j++) {
358		if (cfg[j].kind == kind)
359			chan++;
360	}
361	s->n_chan = chan;
362	s->maxdata = 0;
363	kfree(s->maxdata_list);
364	maxdata_list = kmalloc_array(s->n_chan, sizeof(unsigned int),
365				     GFP_KERNEL);
366	if (!maxdata_list)
367		return -ENOMEM;
368	s->maxdata_list = maxdata_list;
369	kfree(s->range_table_list);
370	s->range_table = NULL;
371	s->range_table_list = NULL;
372	if (kind == 1 || kind == 2) {
373		s->range_table = &range_digital;
374	} else if (range) {
375		range_table_list = kmalloc_array(s->n_chan, sizeof(*range),
376						 GFP_KERNEL);
377		if (!range_table_list)
378			return -ENOMEM;
379		s->range_table_list = range_table_list;
380	}
381	for (chan = 0, j = 0; j < 32; j++) {
382		if (cfg[j].kind == kind) {
383			if (mapping)
384				mapping[chan] = j;
385			if (range) {
386				range[j].length = 1;
387				range[j].range.min = cfg[j].min;
388				range[j].range.max = cfg[j].max;
389				range_table_list[chan] =
390				    (const struct comedi_lrange *)&range[j];
391			}
392			maxdata_list[chan] = ((long long)1 << cfg[j].bits) - 1;
393			chan++;
394		}
395	}
396	return 0;
397}
398
399static int serial2002_setup_subdevs(struct comedi_device *dev)
400{
401	struct serial2002_private *devpriv = dev->private;
402	struct config_t *di_cfg;
403	struct config_t *do_cfg;
404	struct config_t *ai_cfg;
405	struct config_t *ao_cfg;
406	struct config_t *cfg;
407	struct comedi_subdevice *s;
408	int result = 0;
409	int i;
410
411	/* Allocate the temporary structs to hold the configuration data */
412	di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
413	do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
414	ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
415	ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
416	if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) {
417		result = -ENOMEM;
418		goto err_alloc_configs;
419	}
420
421	/* Read the configuration from the connected device */
422	serial2002_tty_setspeed(devpriv->tty, devpriv->speed);
423	serial2002_poll_channel(devpriv->tty, 31);
424	while (1) {
425		struct serial_data data = serial2002_read(devpriv->tty, 1000);
426		int kind = S2002_CFG_KIND(data.value);
427		int channel = S2002_CFG_CHAN(data.value);
428		int range = S2002_CFG_BASE(data.value);
429		int cmd = S2002_CFG_CMD(data.value);
430
431		if (data.kind != is_channel || data.index != 31 ||
432		    kind == S2002_CFG_KIND_INVALID)
433			break;
434
435		switch (kind) {
436		case S2002_CFG_KIND_DIGITAL_IN:
437			cfg = di_cfg;
438			break;
439		case S2002_CFG_KIND_DIGITAL_OUT:
440			cfg = do_cfg;
441			break;
442		case S2002_CFG_KIND_ANALOG_IN:
443			cfg = ai_cfg;
444			break;
445		case S2002_CFG_KIND_ANALOG_OUT:
446			cfg = ao_cfg;
447			break;
448		case S2002_CFG_KIND_ENCODER_IN:
449			cfg = ai_cfg;
450			break;
451		default:
452			cfg = NULL;
453			break;
454		}
455		if (!cfg)
456			continue;	/* unknown kind, skip it */
457
458		cfg[channel].kind = kind;
459
460		switch (cmd) {
461		case S2002_CFG_CMD_BITS:
462			cfg[channel].bits = S2002_CFG_BITS(data.value);
463			break;
464		case S2002_CFG_CMD_MIN:
465		case S2002_CFG_CMD_MAX:
466			switch (S2002_CFG_UNITS(data.value)) {
467			case 0:
468				range *= 1000000;
469				break;
470			case 1:
471				range *= 1000;
472				break;
473			case 2:
474				range *= 1;
475				break;
476			}
477			if (S2002_CFG_SIGN(data.value))
478				range = -range;
479			if (cmd == S2002_CFG_CMD_MIN)
480				cfg[channel].min = range;
481			else
482				cfg[channel].max = range;
483			break;
484		}
485	}
486
487	/* Fill in subdevice data */
488	for (i = 0; i <= 4; i++) {
489		unsigned char *mapping = NULL;
490		struct serial2002_range_table_t *range = NULL;
491		int kind = 0;
492
493		s = &dev->subdevices[i];
494
495		switch (i) {
496		case 0:
497			cfg = di_cfg;
498			mapping = devpriv->digital_in_mapping;
499			kind = S2002_CFG_KIND_DIGITAL_IN;
500			break;
501		case 1:
502			cfg = do_cfg;
503			mapping = devpriv->digital_out_mapping;
504			kind = S2002_CFG_KIND_DIGITAL_OUT;
505			break;
506		case 2:
507			cfg = ai_cfg;
508			mapping = devpriv->analog_in_mapping;
509			range = devpriv->in_range;
510			kind = S2002_CFG_KIND_ANALOG_IN;
511			break;
512		case 3:
513			cfg = ao_cfg;
514			mapping = devpriv->analog_out_mapping;
515			range = devpriv->out_range;
516			kind = S2002_CFG_KIND_ANALOG_OUT;
517			break;
518		case 4:
519			cfg = ai_cfg;
520			mapping = devpriv->encoder_in_mapping;
521			range = devpriv->in_range;
522			kind = S2002_CFG_KIND_ENCODER_IN;
523			break;
524		}
525
526		if (serial2002_setup_subdevice(s, cfg, range, mapping, kind))
527			break;	/* err handled below */
528	}
529	if (i <= 4) {
530		/*
531		 * Failed to allocate maxdata_list or range_table_list
532		 * for a subdevice that needed it.
533		 */
534		result = -ENOMEM;
535		for (i = 0; i <= 4; i++) {
536			s = &dev->subdevices[i];
537			kfree(s->maxdata_list);
538			s->maxdata_list = NULL;
539			kfree(s->range_table_list);
540			s->range_table_list = NULL;
541		}
542	}
543
544err_alloc_configs:
545	kfree(di_cfg);
546	kfree(do_cfg);
547	kfree(ai_cfg);
548	kfree(ao_cfg);
549
550	if (result) {
551		if (devpriv->tty) {
552			filp_close(devpriv->tty, NULL);
553			devpriv->tty = NULL;
554		}
555	}
556
557	return result;
558}
559
560static int serial2002_open(struct comedi_device *dev)
561{
562	struct serial2002_private *devpriv = dev->private;
563	int result;
564	char port[20];
565
566	sprintf(port, "/dev/ttyS%d", devpriv->port);
567	devpriv->tty = filp_open(port, O_RDWR, 0);
568	if (IS_ERR(devpriv->tty)) {
569		result = (int)PTR_ERR(devpriv->tty);
570		dev_err(dev->class_dev, "file open error = %d\n", result);
571	} else {
572		result = serial2002_setup_subdevs(dev);
573	}
574	return result;
575}
576
577static void serial2002_close(struct comedi_device *dev)
578{
579	struct serial2002_private *devpriv = dev->private;
580
581	if (!IS_ERR(devpriv->tty) && devpriv->tty)
582		filp_close(devpriv->tty, NULL);
583}
584
585static int serial2002_di_insn_read(struct comedi_device *dev,
586				   struct comedi_subdevice *s,
587				   struct comedi_insn *insn,
588				   unsigned int *data)
589{
590	struct serial2002_private *devpriv = dev->private;
591	int n;
592	int chan;
593
594	chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
595	for (n = 0; n < insn->n; n++) {
596		struct serial_data read;
597
598		serial2002_poll_digital(devpriv->tty, chan);
599		while (1) {
600			read = serial2002_read(devpriv->tty, 1000);
601			if (read.kind != is_digital || read.index == chan)
602				break;
603		}
604		data[n] = read.value;
605	}
606	return n;
607}
608
609static int serial2002_do_insn_write(struct comedi_device *dev,
610				    struct comedi_subdevice *s,
611				    struct comedi_insn *insn,
612				    unsigned int *data)
613{
614	struct serial2002_private *devpriv = dev->private;
615	int n;
616	int chan;
617
618	chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
619	for (n = 0; n < insn->n; n++) {
620		struct serial_data write;
621
622		write.kind = is_digital;
623		write.index = chan;
624		write.value = data[n];
625		serial2002_write(devpriv->tty, write);
626	}
627	return n;
628}
629
630static int serial2002_ai_insn_read(struct comedi_device *dev,
631				   struct comedi_subdevice *s,
632				   struct comedi_insn *insn,
633				   unsigned int *data)
634{
635	struct serial2002_private *devpriv = dev->private;
636	int n;
637	int chan;
638
639	chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
640	for (n = 0; n < insn->n; n++) {
641		struct serial_data read;
642
643		serial2002_poll_channel(devpriv->tty, chan);
644		while (1) {
645			read = serial2002_read(devpriv->tty, 1000);
646			if (read.kind != is_channel || read.index == chan)
647				break;
648		}
649		data[n] = read.value;
650	}
651	return n;
652}
653
654static int serial2002_ao_insn_write(struct comedi_device *dev,
655				    struct comedi_subdevice *s,
656				    struct comedi_insn *insn,
657				    unsigned int *data)
658{
659	struct serial2002_private *devpriv = dev->private;
660	int n;
661	int chan;
662
663	chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
664	for (n = 0; n < insn->n; n++) {
665		struct serial_data write;
666
667		write.kind = is_channel;
668		write.index = chan;
669		write.value = data[n];
670		serial2002_write(devpriv->tty, write);
671		devpriv->ao_readback[chan] = data[n];
672	}
673	return n;
674}
675
676static int serial2002_ao_insn_read(struct comedi_device *dev,
677				   struct comedi_subdevice *s,
678				   struct comedi_insn *insn,
679				   unsigned int *data)
680{
681	struct serial2002_private *devpriv = dev->private;
682	int n;
683	int chan = CR_CHAN(insn->chanspec);
684
685	for (n = 0; n < insn->n; n++)
686		data[n] = devpriv->ao_readback[chan];
687
688	return n;
689}
690
691static int serial2002_encoder_insn_read(struct comedi_device *dev,
692					struct comedi_subdevice *s,
693					struct comedi_insn *insn,
694					unsigned int *data)
695{
696	struct serial2002_private *devpriv = dev->private;
697	int n;
698	int chan;
699
700	chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
701	for (n = 0; n < insn->n; n++) {
702		struct serial_data read;
703
704		serial2002_poll_channel(devpriv->tty, chan);
705		while (1) {
706			read = serial2002_read(devpriv->tty, 1000);
707			if (read.kind != is_channel || read.index == chan)
708				break;
709		}
710		data[n] = read.value;
711	}
712	return n;
713}
714
715static int serial2002_attach(struct comedi_device *dev,
716			     struct comedi_devconfig *it)
717{
718	struct serial2002_private *devpriv;
719	struct comedi_subdevice *s;
720	int ret;
721
722	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
723	if (!devpriv)
724		return -ENOMEM;
725
726	devpriv->port = it->options[0];
727	devpriv->speed = it->options[1];
728
729	ret = comedi_alloc_subdevices(dev, 5);
730	if (ret)
731		return ret;
732
733	/* digital input subdevice */
734	s = &dev->subdevices[0];
735	s->type		= COMEDI_SUBD_DI;
736	s->subdev_flags	= SDF_READABLE;
737	s->n_chan	= 0;
738	s->maxdata	= 1;
739	s->range_table	= &range_digital;
740	s->insn_read	= serial2002_di_insn_read;
741
742	/* digital output subdevice */
743	s = &dev->subdevices[1];
744	s->type		= COMEDI_SUBD_DO;
745	s->subdev_flags	= SDF_WRITEABLE;
746	s->n_chan	= 0;
747	s->maxdata	= 1;
748	s->range_table	= &range_digital;
749	s->insn_write	= serial2002_do_insn_write;
750
751	/* analog input subdevice */
752	s = &dev->subdevices[2];
753	s->type		= COMEDI_SUBD_AI;
754	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
755	s->n_chan	= 0;
756	s->maxdata	= 1;
757	s->range_table	= NULL;
758	s->insn_read	= serial2002_ai_insn_read;
759
760	/* analog output subdevice */
761	s = &dev->subdevices[3];
762	s->type		= COMEDI_SUBD_AO;
763	s->subdev_flags	= SDF_WRITEABLE;
764	s->n_chan	= 0;
765	s->maxdata	= 1;
766	s->range_table	= NULL;
767	s->insn_write	= serial2002_ao_insn_write;
768	s->insn_read	= serial2002_ao_insn_read;
769
770	/* encoder input subdevice */
771	s = &dev->subdevices[4];
772	s->type		= COMEDI_SUBD_COUNTER;
773	s->subdev_flags	= SDF_READABLE | SDF_LSAMPL;
774	s->n_chan	= 0;
775	s->maxdata	= 1;
776	s->range_table	= NULL;
777	s->insn_read	= serial2002_encoder_insn_read;
778
779	dev->open	= serial2002_open;
780	dev->close	= serial2002_close;
781
782	return 0;
783}
784
785static void serial2002_detach(struct comedi_device *dev)
786{
787	struct comedi_subdevice *s;
788	int i;
789
790	for (i = 0; i < dev->n_subdevices; i++) {
791		s = &dev->subdevices[i];
792		kfree(s->maxdata_list);
793		kfree(s->range_table_list);
794	}
795}
796
797static struct comedi_driver serial2002_driver = {
798	.driver_name	= "serial2002",
799	.module		= THIS_MODULE,
800	.attach		= serial2002_attach,
801	.detach		= serial2002_detach,
802};
803module_comedi_driver(serial2002_driver);
804
805MODULE_AUTHOR("Comedi http://www.comedi.org");
806MODULE_DESCRIPTION("Comedi low-level driver");
807MODULE_LICENSE("GPL");
808