linux/drivers/staging/comedi/drivers/serial2002.c
<<
>>
Prefs
   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                                                        cur_config[channel].min
 519                                                            = min;
 520                                                }
 521                                                break;
 522                                        case 2:{
 523                                                        int unit, sign, max;
 524                                                        unit =
 525                                                            (data.value >> 10) &
 526                                                            0x7;
 527                                                        sign =
 528                                                            (data.value >> 13) &
 529                                                            0x1;
 530                                                        max =
 531                                                            (data.value >> 14) &
 532                                                            0xfffff;
 533
 534                                                        switch (unit) {
 535                                                        case 0:{
 536                                                                        max =
 537                                                                            max
 538                                                                            *
 539                                                                            1000000;
 540                                                                }
 541                                                                break;
 542                                                        case 1:{
 543                                                                        max =
 544                                                                            max
 545                                                                            *
 546                                                                            1000;
 547                                                                }
 548                                                                break;
 549                                                        case 2:{
 550                                                                        max =
 551                                                                            max
 552                                                                            * 1;
 553                                                                }
 554                                                                break;
 555                                                        }
 556                                                        if (sign)
 557                                                                max = -max;
 558                                                        cur_config[channel].max
 559                                                            = max;
 560                                                }
 561                                                break;
 562                                        }
 563                                }
 564                        }
 565                }
 566                for (i = 0; i <= 4; i++) {
 567                        /*  Fill in subdev data */
 568                        struct config_t *c;
 569                        unsigned char *mapping = NULL;
 570                        struct serial2002_range_table_t *range = NULL;
 571                        int kind = 0;
 572
 573                        switch (i) {
 574                        case 0:{
 575                                        c = dig_in_config;
 576                                        mapping = devpriv->digital_in_mapping;
 577                                        kind = 1;
 578                                }
 579                                break;
 580                        case 1:{
 581                                        c = dig_out_config;
 582                                        mapping = devpriv->digital_out_mapping;
 583                                        kind = 2;
 584                                }
 585                                break;
 586                        case 2:{
 587                                        c = chan_in_config;
 588                                        mapping = devpriv->analog_in_mapping;
 589                                        range = devpriv->in_range;
 590                                        kind = 3;
 591                                }
 592                                break;
 593                        case 3:{
 594                                        c = chan_out_config;
 595                                        mapping = devpriv->analog_out_mapping;
 596                                        range = devpriv->out_range;
 597                                        kind = 4;
 598                                }
 599                                break;
 600                        case 4:{
 601                                        c = chan_in_config;
 602                                        mapping = devpriv->encoder_in_mapping;
 603                                        range = devpriv->in_range;
 604                                        kind = 5;
 605                                }
 606                                break;
 607                        default:{
 608                                        c = NULL;
 609                                }
 610                                break;
 611                        }
 612                        if (c) {
 613                                struct comedi_subdevice *s;
 614                                const struct comedi_lrange **range_table_list =
 615                                    NULL;
 616                                unsigned int *maxdata_list;
 617                                int j, chan;
 618
 619                                for (chan = 0, j = 0; j < 32; j++) {
 620                                        if (c[j].kind == kind)
 621                                                chan++;
 622                                }
 623                                s = &dev->subdevices[i];
 624                                s->n_chan = chan;
 625                                s->maxdata = 0;
 626                                kfree(s->maxdata_list);
 627                                s->maxdata_list = maxdata_list =
 628                                    kmalloc(sizeof(unsigned int) * s->n_chan,
 629                                            GFP_KERNEL);
 630                                if (!s->maxdata_list)
 631                                        break;  /* error handled below */
 632                                kfree(s->range_table_list);
 633                                s->range_table = NULL;
 634                                s->range_table_list = NULL;
 635                                if (range) {
 636                                        s->range_table_list = range_table_list =
 637                                            kmalloc(sizeof
 638                                                    (struct
 639                                                     serial2002_range_table_t) *
 640                                                    s->n_chan, GFP_KERNEL);
 641                                        if (!s->range_table_list)
 642                                                break;  /* err handled below */
 643                                }
 644                                for (chan = 0, j = 0; j < 32; j++) {
 645                                        if (c[j].kind == kind) {
 646                                                if (mapping)
 647                                                        mapping[chan] = j;
 648                                                if (range) {
 649                                                        range[j].length = 1;
 650                                                        range[j].range.min =
 651                                                            c[j].min;
 652                                                        range[j].range.max =
 653                                                            c[j].max;
 654                                                        range_table_list[chan] =
 655                                                            (const struct
 656                                                             comedi_lrange *)
 657                                                            &range[j];
 658                                                }
 659                                                maxdata_list[chan] =
 660                                                    ((long long)1 << c[j].bits)
 661                                                    - 1;
 662                                                chan++;
 663                                        }
 664                                }
 665                        }
 666                }
 667                if (i <= 4) {
 668                        /* Failed to allocate maxdata_list or range_table_list
 669                         * for a subdevice that needed it.  */
 670                        result = -ENOMEM;
 671                        for (i = 0; i <= 4; i++) {
 672                                struct comedi_subdevice *s;
 673
 674                                s = &dev->subdevices[i];
 675                                kfree(s->maxdata_list);
 676                                s->maxdata_list = NULL;
 677                                kfree(s->range_table_list);
 678                                s->range_table_list = NULL;
 679                        }
 680                }
 681
 682err_alloc_configs:
 683                kfree(dig_in_config);
 684                kfree(dig_out_config);
 685                kfree(chan_in_config);
 686                kfree(chan_out_config);
 687
 688                if (result) {
 689                        if (devpriv->tty) {
 690                                filp_close(devpriv->tty, 0);
 691                                devpriv->tty = NULL;
 692                        }
 693                }
 694        }
 695        return result;
 696}
 697
 698static void serial_2002_close(struct comedi_device *dev)
 699{
 700        if (!IS_ERR(devpriv->tty) && (devpriv->tty != 0))
 701                filp_close(devpriv->tty, 0);
 702}
 703
 704static int serial2002_di_rinsn(struct comedi_device *dev,
 705                               struct comedi_subdevice *s,
 706                               struct comedi_insn *insn, unsigned int *data)
 707{
 708        int n;
 709        int chan;
 710
 711        chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
 712        for (n = 0; n < insn->n; n++) {
 713                struct serial_data read;
 714
 715                poll_digital(devpriv->tty, chan);
 716                while (1) {
 717                        read = serial_read(devpriv->tty, 1000);
 718                        if (read.kind != is_digital || read.index == chan)
 719                                break;
 720                }
 721                data[n] = read.value;
 722        }
 723        return n;
 724}
 725
 726static int serial2002_do_winsn(struct comedi_device *dev,
 727                               struct comedi_subdevice *s,
 728                               struct comedi_insn *insn, unsigned int *data)
 729{
 730        int n;
 731        int chan;
 732
 733        chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
 734        for (n = 0; n < insn->n; n++) {
 735                struct serial_data write;
 736
 737                write.kind = is_digital;
 738                write.index = chan;
 739                write.value = data[n];
 740                serial_write(devpriv->tty, write);
 741        }
 742        return n;
 743}
 744
 745static int serial2002_ai_rinsn(struct comedi_device *dev,
 746                               struct comedi_subdevice *s,
 747                               struct comedi_insn *insn, unsigned int *data)
 748{
 749        int n;
 750        int chan;
 751
 752        chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
 753        for (n = 0; n < insn->n; n++) {
 754                struct serial_data read;
 755
 756                poll_channel(devpriv->tty, chan);
 757                while (1) {
 758                        read = serial_read(devpriv->tty, 1000);
 759                        if (read.kind != is_channel || read.index == chan)
 760                                break;
 761                }
 762                data[n] = read.value;
 763        }
 764        return n;
 765}
 766
 767static int serial2002_ao_winsn(struct comedi_device *dev,
 768                               struct comedi_subdevice *s,
 769                               struct comedi_insn *insn, unsigned int *data)
 770{
 771        int n;
 772        int chan;
 773
 774        chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
 775        for (n = 0; n < insn->n; n++) {
 776                struct serial_data write;
 777
 778                write.kind = is_channel;
 779                write.index = chan;
 780                write.value = data[n];
 781                serial_write(devpriv->tty, write);
 782                devpriv->ao_readback[chan] = data[n];
 783        }
 784        return n;
 785}
 786
 787static int serial2002_ao_rinsn(struct comedi_device *dev,
 788                               struct comedi_subdevice *s,
 789                               struct comedi_insn *insn, unsigned int *data)
 790{
 791        int n;
 792        int chan = CR_CHAN(insn->chanspec);
 793
 794        for (n = 0; n < insn->n; n++)
 795                data[n] = devpriv->ao_readback[chan];
 796
 797        return n;
 798}
 799
 800static int serial2002_ei_rinsn(struct comedi_device *dev,
 801                               struct comedi_subdevice *s,
 802                               struct comedi_insn *insn, unsigned int *data)
 803{
 804        int n;
 805        int chan;
 806
 807        chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
 808        for (n = 0; n < insn->n; n++) {
 809                struct serial_data read;
 810
 811                poll_channel(devpriv->tty, chan);
 812                while (1) {
 813                        read = serial_read(devpriv->tty, 1000);
 814                        if (read.kind != is_channel || read.index == chan)
 815                                break;
 816                }
 817                data[n] = read.value;
 818        }
 819        return n;
 820}
 821
 822static int serial2002_attach(struct comedi_device *dev,
 823                             struct comedi_devconfig *it)
 824{
 825        struct comedi_subdevice *s;
 826
 827        printk("comedi%d: serial2002: ", dev->minor);
 828        dev->board_name = thisboard->name;
 829        if (alloc_private(dev, sizeof(struct serial2002_private)) < 0)
 830                return -ENOMEM;
 831        dev->open = serial_2002_open;
 832        dev->close = serial_2002_close;
 833        devpriv->port = it->options[0];
 834        devpriv->speed = it->options[1];
 835        printk("/dev/ttyS%d @ %d\n", devpriv->port, devpriv->speed);
 836
 837        if (alloc_subdevices(dev, 5) < 0)
 838                return -ENOMEM;
 839
 840        /* digital input subdevice */
 841        s = dev->subdevices + 0;
 842        s->type = COMEDI_SUBD_DI;
 843        s->subdev_flags = SDF_READABLE;
 844        s->n_chan = 0;
 845        s->maxdata = 1;
 846        s->range_table = &range_digital;
 847        s->insn_read = &serial2002_di_rinsn;
 848
 849        /* digital output subdevice */
 850        s = dev->subdevices + 1;
 851        s->type = COMEDI_SUBD_DO;
 852        s->subdev_flags = SDF_WRITEABLE;
 853        s->n_chan = 0;
 854        s->maxdata = 1;
 855        s->range_table = &range_digital;
 856        s->insn_write = &serial2002_do_winsn;
 857
 858        /* analog input subdevice */
 859        s = dev->subdevices + 2;
 860        s->type = COMEDI_SUBD_AI;
 861        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 862        s->n_chan = 0;
 863        s->maxdata = 1;
 864        s->range_table = 0;
 865        s->insn_read = &serial2002_ai_rinsn;
 866
 867        /* analog output subdevice */
 868        s = dev->subdevices + 3;
 869        s->type = COMEDI_SUBD_AO;
 870        s->subdev_flags = SDF_WRITEABLE;
 871        s->n_chan = 0;
 872        s->maxdata = 1;
 873        s->range_table = 0;
 874        s->insn_write = &serial2002_ao_winsn;
 875        s->insn_read = &serial2002_ao_rinsn;
 876
 877        /* encoder input subdevice */
 878        s = dev->subdevices + 4;
 879        s->type = COMEDI_SUBD_COUNTER;
 880        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 881        s->n_chan = 0;
 882        s->maxdata = 1;
 883        s->range_table = 0;
 884        s->insn_read = &serial2002_ei_rinsn;
 885
 886        return 1;
 887}
 888
 889static int serial2002_detach(struct comedi_device *dev)
 890{
 891        struct comedi_subdevice *s;
 892        int i;
 893
 894        printk("comedi%d: serial2002: remove\n", dev->minor);
 895        for (i = 0; i < 5; i++) {
 896                s = &dev->subdevices[i];
 897                kfree(s->maxdata_list);
 898                kfree(s->range_table_list);
 899        }
 900        return 0;
 901}
 902
 903static int __init driver_serial2002_init_module(void)
 904{
 905        return comedi_driver_register(&driver_serial2002);
 906}
 907
 908static void __exit driver_serial2002_cleanup_module(void)
 909{
 910        comedi_driver_unregister(&driver_serial2002);
 911}
 912
 913module_init(driver_serial2002_init_module);
 914module_exit(driver_serial2002_cleanup_module);
 915
 916MODULE_AUTHOR("Comedi http://www.comedi.org");
 917MODULE_DESCRIPTION("Comedi low-level driver");
 918MODULE_LICENSE("GPL");
 919