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