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