linux/drivers/staging/comedi/drivers/serial2002.c
<<
>>
Prefs
   1/*
   2 * serial2002.c
   3 * Comedi driver for serial connected hardware
   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#include <linux/module.h>
  29#include "../comedidev.h"
  30
  31#include <linux/delay.h>
  32#include <linux/sched.h>
  33#include <linux/slab.h>
  34#include <linux/ktime.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        /*  HACK... */
  43        int length;
  44        struct comedi_krange range;
  45};
  46
  47struct serial2002_private {
  48        int port;               /*  /dev/ttyS<port> */
  49        int speed;              /*  baudrate */
  50        struct file *tty;
  51        unsigned int ao_readback[32];
  52        unsigned char digital_in_mapping[32];
  53        unsigned char digital_out_mapping[32];
  54        unsigned char analog_in_mapping[32];
  55        unsigned char analog_out_mapping[32];
  56        unsigned char encoder_in_mapping[32];
  57        struct serial2002_range_table_t in_range[32], out_range[32];
  58};
  59
  60struct serial_data {
  61        enum { is_invalid, is_digital, is_channel } kind;
  62        int index;
  63        unsigned long value;
  64};
  65
  66/*
  67 * The configuration serial_data.value read from the device is
  68 * a bitmask that defines specific options of a channel:
  69 *
  70 * 4:0 - the channel to configure
  71 * 7:5 - the kind of channel
  72 * 9:8 - the command used to configure the channel
  73 *
  74 * The remaining bits vary in use depending on the command:
  75 *
  76 * BITS     15:10 - the channel bits (maxdata)
  77 * MIN/MAX  12:10 - the units multiplier for the scale
  78 *          13    - the sign of the scale
  79 *          33:14 - the base value for the range
  80 */
  81#define S2002_CFG_CHAN(x)               ((x) & 0x1f)
  82#define S2002_CFG_KIND(x)               (((x) >> 5) & 0x7)
  83#define S2002_CFG_KIND_INVALID          0
  84#define S2002_CFG_KIND_DIGITAL_IN       1
  85#define S2002_CFG_KIND_DIGITAL_OUT      2
  86#define S2002_CFG_KIND_ANALOG_IN        3
  87#define S2002_CFG_KIND_ANALOG_OUT       4
  88#define S2002_CFG_KIND_ENCODER_IN       5
  89#define S2002_CFG_CMD(x)                (((x) >> 8) & 0x3)
  90#define S2002_CFG_CMD_BITS              0
  91#define S2002_CFG_CMD_MIN               1
  92#define S2002_CFG_CMD_MAX               2
  93#define S2002_CFG_BITS(x)               (((x) >> 10) & 0x3f)
  94#define S2002_CFG_UNITS(x)              (((x) >> 10) & 0x7)
  95#define S2002_CFG_SIGN(x)               (((x) >> 13) & 0x1)
  96#define S2002_CFG_BASE(x)               (((x) >> 14) & 0xfffff)
  97
  98static long serial2002_tty_ioctl(struct file *f, unsigned int op,
  99                                 unsigned long param)
 100{
 101        if (f->f_op->unlocked_ioctl)
 102                return f->f_op->unlocked_ioctl(f, op, param);
 103
 104        return -ENOTTY;
 105}
 106
 107static int serial2002_tty_write(struct file *f, unsigned char *buf, int count)
 108{
 109        const char __user *p = (__force const char __user *)buf;
 110        int result;
 111        loff_t offset = 0;
 112        mm_segment_t oldfs;
 113
 114        oldfs = get_fs();
 115        set_fs(KERNEL_DS);
 116        result = __vfs_write(f, p, count, &offset);
 117        set_fs(oldfs);
 118        return result;
 119}
 120
 121static void serial2002_tty_read_poll_wait(struct file *f, int timeout)
 122{
 123        struct poll_wqueues table;
 124        ktime_t start, now;
 125
 126        start = ktime_get();
 127        poll_initwait(&table);
 128        while (1) {
 129                long elapsed;
 130                int mask;
 131
 132                mask = f->f_op->poll(f, &table.pt);
 133                if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
 134                            POLLHUP | POLLERR)) {
 135                        break;
 136                }
 137                now = ktime_get();
 138                elapsed = ktime_us_delta(now, start);
 139                if (elapsed > timeout)
 140                        break;
 141                set_current_state(TASK_INTERRUPTIBLE);
 142                schedule_timeout(((timeout - elapsed) * HZ) / 10000);
 143        }
 144        poll_freewait(&table);
 145}
 146
 147static int serial2002_tty_read(struct file *f, int timeout)
 148{
 149        unsigned char ch;
 150        int result;
 151
 152        result = -1;
 153        if (!IS_ERR(f)) {
 154                mm_segment_t oldfs;
 155                char __user *p = (__force char __user *)&ch;
 156                loff_t offset = 0;
 157
 158                oldfs = get_fs();
 159                set_fs(KERNEL_DS);
 160                if (f->f_op->poll) {
 161                        serial2002_tty_read_poll_wait(f, timeout);
 162
 163                        if (__vfs_read(f, p, 1, &offset) == 1)
 164                                result = ch;
 165                } else {
 166                        /* Device does not support poll, busy wait */
 167                        int retries = 0;
 168
 169                        while (1) {
 170                                retries++;
 171                                if (retries >= timeout)
 172                                        break;
 173
 174                                if (__vfs_read(f, p, 1, &offset) == 1) {
 175                                        result = ch;
 176                                        break;
 177                                }
 178                                usleep_range(100, 1000);
 179                        }
 180                }
 181                set_fs(oldfs);
 182        }
 183        return result;
 184}
 185
 186static void serial2002_tty_setspeed(struct file *f, int speed)
 187{
 188        struct termios termios;
 189        struct serial_struct serial;
 190        mm_segment_t oldfs;
 191
 192        oldfs = get_fs();
 193        set_fs(KERNEL_DS);
 194
 195        /* Set speed */
 196        serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios);
 197        termios.c_iflag = 0;
 198        termios.c_oflag = 0;
 199        termios.c_lflag = 0;
 200        termios.c_cflag = CLOCAL | CS8 | CREAD;
 201        termios.c_cc[VMIN] = 0;
 202        termios.c_cc[VTIME] = 0;
 203        switch (speed) {
 204        case 2400:
 205                termios.c_cflag |= B2400;
 206                break;
 207        case 4800:
 208                termios.c_cflag |= B4800;
 209                break;
 210        case 9600:
 211                termios.c_cflag |= B9600;
 212                break;
 213        case 19200:
 214                termios.c_cflag |= B19200;
 215                break;
 216        case 38400:
 217                termios.c_cflag |= B38400;
 218                break;
 219        case 57600:
 220                termios.c_cflag |= B57600;
 221                break;
 222        case 115200:
 223                termios.c_cflag |= B115200;
 224                break;
 225        default:
 226                termios.c_cflag |= B9600;
 227                break;
 228        }
 229        serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios);
 230
 231        /* Set low latency */
 232        serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial);
 233        serial.flags |= ASYNC_LOW_LATENCY;
 234        serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial);
 235
 236        set_fs(oldfs);
 237}
 238
 239static void serial2002_poll_digital(struct file *f, int channel)
 240{
 241        char cmd;
 242
 243        cmd = 0x40 | (channel & 0x1f);
 244        serial2002_tty_write(f, &cmd, 1);
 245}
 246
 247static void serial2002_poll_channel(struct file *f, int channel)
 248{
 249        char cmd;
 250
 251        cmd = 0x60 | (channel & 0x1f);
 252        serial2002_tty_write(f, &cmd, 1);
 253}
 254
 255static struct serial_data serial2002_read(struct file *f, int timeout)
 256{
 257        struct serial_data result;
 258        int length;
 259
 260        result.kind = is_invalid;
 261        result.index = 0;
 262        result.value = 0;
 263        length = 0;
 264        while (1) {
 265                int data = serial2002_tty_read(f, timeout);
 266
 267                length++;
 268                if (data < 0) {
 269                        break;
 270                } else if (data & 0x80) {
 271                        result.value = (result.value << 7) | (data & 0x7f);
 272                } else {
 273                        if (length == 1) {
 274                                switch ((data >> 5) & 0x03) {
 275                                case 0:
 276                                        result.value = 0;
 277                                        result.kind = is_digital;
 278                                        break;
 279                                case 1:
 280                                        result.value = 1;
 281                                        result.kind = is_digital;
 282                                        break;
 283                                }
 284                        } else {
 285                                result.value =
 286                                    (result.value << 2) | ((data & 0x60) >> 5);
 287                                result.kind = is_channel;
 288                        }
 289                        result.index = data & 0x1f;
 290                        break;
 291                }
 292        }
 293        return result;
 294}
 295
 296static void serial2002_write(struct file *f, struct serial_data data)
 297{
 298        if (data.kind == is_digital) {
 299                unsigned char ch =
 300                    ((data.value << 5) & 0x20) | (data.index & 0x1f);
 301                serial2002_tty_write(f, &ch, 1);
 302        } else {
 303                unsigned char ch[6];
 304                int i = 0;
 305
 306                if (data.value >= (1L << 30)) {
 307                        ch[i] = 0x80 | ((data.value >> 30) & 0x03);
 308                        i++;
 309                }
 310                if (data.value >= (1L << 23)) {
 311                        ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
 312                        i++;
 313                }
 314                if (data.value >= (1L << 16)) {
 315                        ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
 316                        i++;
 317                }
 318                if (data.value >= (1L << 9)) {
 319                        ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
 320                        i++;
 321                }
 322                ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
 323                i++;
 324                ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
 325                i++;
 326                serial2002_tty_write(f, ch, i);
 327        }
 328}
 329
 330struct config_t {
 331        short int kind;
 332        short int bits;
 333        int min;
 334        int max;
 335};
 336
 337static int serial2002_setup_subdevice(struct comedi_subdevice *s,
 338                                      struct config_t *cfg,
 339                                      struct serial2002_range_table_t *range,
 340                                      unsigned char *mapping,
 341                                      int kind)
 342{
 343        const struct comedi_lrange **range_table_list = NULL;
 344        unsigned int *maxdata_list;
 345        int j, chan;
 346
 347        for (chan = 0, j = 0; j < 32; j++) {
 348                if (cfg[j].kind == kind)
 349                        chan++;
 350        }
 351        s->n_chan = chan;
 352        s->maxdata = 0;
 353        kfree(s->maxdata_list);
 354        maxdata_list = kmalloc_array(s->n_chan, sizeof(unsigned int),
 355                                     GFP_KERNEL);
 356        if (!maxdata_list)
 357                return -ENOMEM;
 358        s->maxdata_list = maxdata_list;
 359        kfree(s->range_table_list);
 360        s->range_table = NULL;
 361        s->range_table_list = NULL;
 362        if (kind == 1 || kind == 2) {
 363                s->range_table = &range_digital;
 364        } else if (range) {
 365                range_table_list = kmalloc_array(s->n_chan, sizeof(*range),
 366                                                 GFP_KERNEL);
 367                if (!range_table_list)
 368                        return -ENOMEM;
 369                s->range_table_list = range_table_list;
 370        }
 371        for (chan = 0, j = 0; j < 32; j++) {
 372                if (cfg[j].kind == kind) {
 373                        if (mapping)
 374                                mapping[chan] = j;
 375                        if (range && range_table_list) {
 376                                range[j].length = 1;
 377                                range[j].range.min = cfg[j].min;
 378                                range[j].range.max = cfg[j].max;
 379                                range_table_list[chan] =
 380                                    (const struct comedi_lrange *)&range[j];
 381                        }
 382                        if (cfg[j].bits < 32)
 383                                maxdata_list[chan] = (1u << cfg[j].bits) - 1;
 384                        else
 385                                maxdata_list[chan] = 0xffffffff;
 386                        chan++;
 387                }
 388        }
 389        return 0;
 390}
 391
 392static int serial2002_setup_subdevs(struct comedi_device *dev)
 393{
 394        struct serial2002_private *devpriv = dev->private;
 395        struct config_t *di_cfg;
 396        struct config_t *do_cfg;
 397        struct config_t *ai_cfg;
 398        struct config_t *ao_cfg;
 399        struct config_t *cfg;
 400        struct comedi_subdevice *s;
 401        int result = 0;
 402        int i;
 403
 404        /* Allocate the temporary structs to hold the configuration data */
 405        di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
 406        do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
 407        ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
 408        ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
 409        if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) {
 410                result = -ENOMEM;
 411                goto err_alloc_configs;
 412        }
 413
 414        /* Read the configuration from the connected device */
 415        serial2002_tty_setspeed(devpriv->tty, devpriv->speed);
 416        serial2002_poll_channel(devpriv->tty, 31);
 417        while (1) {
 418                struct serial_data data = serial2002_read(devpriv->tty, 1000);
 419                int kind = S2002_CFG_KIND(data.value);
 420                int channel = S2002_CFG_CHAN(data.value);
 421                int range = S2002_CFG_BASE(data.value);
 422                int cmd = S2002_CFG_CMD(data.value);
 423
 424                if (data.kind != is_channel || data.index != 31 ||
 425                    kind == S2002_CFG_KIND_INVALID)
 426                        break;
 427
 428                switch (kind) {
 429                case S2002_CFG_KIND_DIGITAL_IN:
 430                        cfg = di_cfg;
 431                        break;
 432                case S2002_CFG_KIND_DIGITAL_OUT:
 433                        cfg = do_cfg;
 434                        break;
 435                case S2002_CFG_KIND_ANALOG_IN:
 436                        cfg = ai_cfg;
 437                        break;
 438                case S2002_CFG_KIND_ANALOG_OUT:
 439                        cfg = ao_cfg;
 440                        break;
 441                case S2002_CFG_KIND_ENCODER_IN:
 442                        cfg = ai_cfg;
 443                        break;
 444                default:
 445                        cfg = NULL;
 446                        break;
 447                }
 448                if (!cfg)
 449                        continue;       /* unknown kind, skip it */
 450
 451                cfg[channel].kind = kind;
 452
 453                switch (cmd) {
 454                case S2002_CFG_CMD_BITS:
 455                        cfg[channel].bits = S2002_CFG_BITS(data.value);
 456                        break;
 457                case S2002_CFG_CMD_MIN:
 458                case S2002_CFG_CMD_MAX:
 459                        switch (S2002_CFG_UNITS(data.value)) {
 460                        case 0:
 461                                range *= 1000000;
 462                                break;
 463                        case 1:
 464                                range *= 1000;
 465                                break;
 466                        case 2:
 467                                range *= 1;
 468                                break;
 469                        }
 470                        if (S2002_CFG_SIGN(data.value))
 471                                range = -range;
 472                        if (cmd == S2002_CFG_CMD_MIN)
 473                                cfg[channel].min = range;
 474                        else
 475                                cfg[channel].max = range;
 476                        break;
 477                }
 478        }
 479
 480        /* Fill in subdevice data */
 481        for (i = 0; i <= 4; i++) {
 482                unsigned char *mapping = NULL;
 483                struct serial2002_range_table_t *range = NULL;
 484                int kind = 0;
 485
 486                s = &dev->subdevices[i];
 487
 488                switch (i) {
 489                case 0:
 490                        cfg = di_cfg;
 491                        mapping = devpriv->digital_in_mapping;
 492                        kind = S2002_CFG_KIND_DIGITAL_IN;
 493                        break;
 494                case 1:
 495                        cfg = do_cfg;
 496                        mapping = devpriv->digital_out_mapping;
 497                        kind = S2002_CFG_KIND_DIGITAL_OUT;
 498                        break;
 499                case 2:
 500                        cfg = ai_cfg;
 501                        mapping = devpriv->analog_in_mapping;
 502                        range = devpriv->in_range;
 503                        kind = S2002_CFG_KIND_ANALOG_IN;
 504                        break;
 505                case 3:
 506                        cfg = ao_cfg;
 507                        mapping = devpriv->analog_out_mapping;
 508                        range = devpriv->out_range;
 509                        kind = S2002_CFG_KIND_ANALOG_OUT;
 510                        break;
 511                case 4:
 512                        cfg = ai_cfg;
 513                        mapping = devpriv->encoder_in_mapping;
 514                        range = devpriv->in_range;
 515                        kind = S2002_CFG_KIND_ENCODER_IN;
 516                        break;
 517                }
 518
 519                if (serial2002_setup_subdevice(s, cfg, range, mapping, kind))
 520                        break;  /* err handled below */
 521        }
 522        if (i <= 4) {
 523                /*
 524                 * Failed to allocate maxdata_list or range_table_list
 525                 * for a subdevice that needed it.
 526                 */
 527                result = -ENOMEM;
 528                for (i = 0; i <= 4; i++) {
 529                        s = &dev->subdevices[i];
 530                        kfree(s->maxdata_list);
 531                        s->maxdata_list = NULL;
 532                        kfree(s->range_table_list);
 533                        s->range_table_list = NULL;
 534                }
 535        }
 536
 537err_alloc_configs:
 538        kfree(di_cfg);
 539        kfree(do_cfg);
 540        kfree(ai_cfg);
 541        kfree(ao_cfg);
 542
 543        if (result) {
 544                if (devpriv->tty) {
 545                        filp_close(devpriv->tty, NULL);
 546                        devpriv->tty = NULL;
 547                }
 548        }
 549
 550        return result;
 551}
 552
 553static int serial2002_open(struct comedi_device *dev)
 554{
 555        struct serial2002_private *devpriv = dev->private;
 556        int result;
 557        char port[20];
 558
 559        sprintf(port, "/dev/ttyS%d", devpriv->port);
 560        devpriv->tty = filp_open(port, O_RDWR, 0);
 561        if (IS_ERR(devpriv->tty)) {
 562                result = (int)PTR_ERR(devpriv->tty);
 563                dev_err(dev->class_dev, "file open error = %d\n", result);
 564        } else {
 565                result = serial2002_setup_subdevs(dev);
 566        }
 567        return result;
 568}
 569
 570static void serial2002_close(struct comedi_device *dev)
 571{
 572        struct serial2002_private *devpriv = dev->private;
 573
 574        if (!IS_ERR(devpriv->tty) && devpriv->tty)
 575                filp_close(devpriv->tty, NULL);
 576}
 577
 578static int serial2002_di_insn_read(struct comedi_device *dev,
 579                                   struct comedi_subdevice *s,
 580                                   struct comedi_insn *insn,
 581                                   unsigned int *data)
 582{
 583        struct serial2002_private *devpriv = dev->private;
 584        int n;
 585        int chan;
 586
 587        chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
 588        for (n = 0; n < insn->n; n++) {
 589                struct serial_data read;
 590
 591                serial2002_poll_digital(devpriv->tty, chan);
 592                while (1) {
 593                        read = serial2002_read(devpriv->tty, 1000);
 594                        if (read.kind != is_digital || read.index == chan)
 595                                break;
 596                }
 597                data[n] = read.value;
 598        }
 599        return n;
 600}
 601
 602static int serial2002_do_insn_write(struct comedi_device *dev,
 603                                    struct comedi_subdevice *s,
 604                                    struct comedi_insn *insn,
 605                                    unsigned int *data)
 606{
 607        struct serial2002_private *devpriv = dev->private;
 608        int n;
 609        int chan;
 610
 611        chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
 612        for (n = 0; n < insn->n; n++) {
 613                struct serial_data write;
 614
 615                write.kind = is_digital;
 616                write.index = chan;
 617                write.value = data[n];
 618                serial2002_write(devpriv->tty, write);
 619        }
 620        return n;
 621}
 622
 623static int serial2002_ai_insn_read(struct comedi_device *dev,
 624                                   struct comedi_subdevice *s,
 625                                   struct comedi_insn *insn,
 626                                   unsigned int *data)
 627{
 628        struct serial2002_private *devpriv = dev->private;
 629        int n;
 630        int chan;
 631
 632        chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
 633        for (n = 0; n < insn->n; n++) {
 634                struct serial_data read;
 635
 636                serial2002_poll_channel(devpriv->tty, chan);
 637                while (1) {
 638                        read = serial2002_read(devpriv->tty, 1000);
 639                        if (read.kind != is_channel || read.index == chan)
 640                                break;
 641                }
 642                data[n] = read.value;
 643        }
 644        return n;
 645}
 646
 647static int serial2002_ao_insn_write(struct comedi_device *dev,
 648                                    struct comedi_subdevice *s,
 649                                    struct comedi_insn *insn,
 650                                    unsigned int *data)
 651{
 652        struct serial2002_private *devpriv = dev->private;
 653        int n;
 654        int chan;
 655
 656        chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
 657        for (n = 0; n < insn->n; n++) {
 658                struct serial_data write;
 659
 660                write.kind = is_channel;
 661                write.index = chan;
 662                write.value = data[n];
 663                serial2002_write(devpriv->tty, write);
 664                devpriv->ao_readback[chan] = data[n];
 665        }
 666        return n;
 667}
 668
 669static int serial2002_ao_insn_read(struct comedi_device *dev,
 670                                   struct comedi_subdevice *s,
 671                                   struct comedi_insn *insn,
 672                                   unsigned int *data)
 673{
 674        struct serial2002_private *devpriv = dev->private;
 675        int n;
 676        int chan = CR_CHAN(insn->chanspec);
 677
 678        for (n = 0; n < insn->n; n++)
 679                data[n] = devpriv->ao_readback[chan];
 680
 681        return n;
 682}
 683
 684static int serial2002_encoder_insn_read(struct comedi_device *dev,
 685                                        struct comedi_subdevice *s,
 686                                        struct comedi_insn *insn,
 687                                        unsigned int *data)
 688{
 689        struct serial2002_private *devpriv = dev->private;
 690        int n;
 691        int chan;
 692
 693        chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
 694        for (n = 0; n < insn->n; n++) {
 695                struct serial_data read;
 696
 697                serial2002_poll_channel(devpriv->tty, chan);
 698                while (1) {
 699                        read = serial2002_read(devpriv->tty, 1000);
 700                        if (read.kind != is_channel || read.index == chan)
 701                                break;
 702                }
 703                data[n] = read.value;
 704        }
 705        return n;
 706}
 707
 708static int serial2002_attach(struct comedi_device *dev,
 709                             struct comedi_devconfig *it)
 710{
 711        struct serial2002_private *devpriv;
 712        struct comedi_subdevice *s;
 713        int ret;
 714
 715        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 716        if (!devpriv)
 717                return -ENOMEM;
 718
 719        devpriv->port = it->options[0];
 720        devpriv->speed = it->options[1];
 721
 722        ret = comedi_alloc_subdevices(dev, 5);
 723        if (ret)
 724                return ret;
 725
 726        /* digital input subdevice */
 727        s = &dev->subdevices[0];
 728        s->type         = COMEDI_SUBD_DI;
 729        s->subdev_flags = SDF_READABLE;
 730        s->n_chan       = 0;
 731        s->maxdata      = 1;
 732        s->range_table  = &range_digital;
 733        s->insn_read    = serial2002_di_insn_read;
 734
 735        /* digital output subdevice */
 736        s = &dev->subdevices[1];
 737        s->type         = COMEDI_SUBD_DO;
 738        s->subdev_flags = SDF_WRITABLE;
 739        s->n_chan       = 0;
 740        s->maxdata      = 1;
 741        s->range_table  = &range_digital;
 742        s->insn_write   = serial2002_do_insn_write;
 743
 744        /* analog input subdevice */
 745        s = &dev->subdevices[2];
 746        s->type         = COMEDI_SUBD_AI;
 747        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 748        s->n_chan       = 0;
 749        s->maxdata      = 1;
 750        s->range_table  = NULL;
 751        s->insn_read    = serial2002_ai_insn_read;
 752
 753        /* analog output subdevice */
 754        s = &dev->subdevices[3];
 755        s->type         = COMEDI_SUBD_AO;
 756        s->subdev_flags = SDF_WRITABLE;
 757        s->n_chan       = 0;
 758        s->maxdata      = 1;
 759        s->range_table  = NULL;
 760        s->insn_write   = serial2002_ao_insn_write;
 761        s->insn_read    = serial2002_ao_insn_read;
 762
 763        /* encoder input subdevice */
 764        s = &dev->subdevices[4];
 765        s->type         = COMEDI_SUBD_COUNTER;
 766        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 767        s->n_chan       = 0;
 768        s->maxdata      = 1;
 769        s->range_table  = NULL;
 770        s->insn_read    = serial2002_encoder_insn_read;
 771
 772        dev->open       = serial2002_open;
 773        dev->close      = serial2002_close;
 774
 775        return 0;
 776}
 777
 778static void serial2002_detach(struct comedi_device *dev)
 779{
 780        struct comedi_subdevice *s;
 781        int i;
 782
 783        for (i = 0; i < dev->n_subdevices; i++) {
 784                s = &dev->subdevices[i];
 785                kfree(s->maxdata_list);
 786                kfree(s->range_table_list);
 787        }
 788}
 789
 790static struct comedi_driver serial2002_driver = {
 791        .driver_name    = "serial2002",
 792        .module         = THIS_MODULE,
 793        .attach         = serial2002_attach,
 794        .detach         = serial2002_detach,
 795};
 796module_comedi_driver(serial2002_driver);
 797
 798MODULE_AUTHOR("Comedi http://www.comedi.org");
 799MODULE_DESCRIPTION("Comedi low-level driver");
 800MODULE_LICENSE("GPL");
 801