staging: comedi: drivers: replace SDF_WRITEABLE with SDF_WRITABLE
[deliverable/linux.git] / 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
19 /*
20 Driver: serial2002
21 Description: Driver for serial connected hardware
22 Devices:
23 Author: Anders Blomdell
24 Updated: Fri, 7 Jun 2002 12:56:45 -0700
25 Status: 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
41 struct serial2002_range_table_t {
42
43 /* HACK... */
44 int length;
45 struct comedi_krange range;
46 };
47
48 struct 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
62 struct 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
100 static 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
109 static 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
123 static 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
131 static 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
158 static 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
195 static 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
248 static 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
256 static 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
264 static 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
306 static 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
340 struct config_t {
341 short int kind;
342 short int bits;
343 int min;
344 int max;
345 };
346
347 static 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
399 static 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
544 err_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
560 static 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
577 static 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
585 static 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
609 static 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
630 static 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
654 static 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
676 static 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
691 static 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
715 static 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_WRITABLE;
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_WRITABLE;
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
785 static 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
797 static struct comedi_driver serial2002_driver = {
798 .driver_name = "serial2002",
799 .module = THIS_MODULE,
800 .attach = serial2002_attach,
801 .detach = serial2002_detach,
802 };
803 module_comedi_driver(serial2002_driver);
804
805 MODULE_AUTHOR("Comedi http://www.comedi.org");
806 MODULE_DESCRIPTION("Comedi low-level driver");
807 MODULE_LICENSE("GPL");
This page took 0.077015 seconds and 5 git commands to generate.