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