serial2002.c revision e4e1f289be88a75dc8b63d50ade1f9a2e6168021
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 kfree(s->maxdata_list); 911 kfree(s->range_table_list); 912 } 913 return 0; 914} 915 916static int __init driver_serial2002_init_module(void) 917{ 918 return comedi_driver_register(&driver_serial2002); 919} 920 921static void __exit driver_serial2002_cleanup_module(void) 922{ 923 comedi_driver_unregister(&driver_serial2002); 924} 925 926module_init(driver_serial2002_init_module); 927module_exit(driver_serial2002_cleanup_module); 928 929MODULE_AUTHOR("Comedi http://www.comedi.org"); 930MODULE_DESCRIPTION("Comedi low-level driver"); 931MODULE_LICENSE("GPL"); 932