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