linux/drivers/staging/comedi/drivers/amplc_pc236.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/amplc_pc236.c
   3    Driver for Amplicon PC36AT and PCI236 DIO boards.
   4
   5    Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
   6
   7    COMEDI - Linux Control and Measurement Device Interface
   8    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   9
  10    This program is free software; you can redistribute it and/or modify
  11    it under the terms of the GNU General Public License as published by
  12    the Free Software Foundation; either version 2 of the License, or
  13    (at your option) any later version.
  14
  15    This program is distributed in the hope that it will be useful,
  16    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18    GNU General Public License for more details.
  19
  20    You should have received a copy of the GNU General Public License
  21    along with this program; if not, write to the Free Software
  22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23
  24*/
  25/*
  26Driver: amplc_pc236
  27Description: Amplicon PC36AT, PCI236
  28Author: Ian Abbott <abbotti@mev.co.uk>
  29Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
  30Updated: Wed, 01 Apr 2009 15:41:25 +0100
  31Status: works
  32
  33Configuration options - PC36AT:
  34  [0] - I/O port base address
  35  [1] - IRQ (optional)
  36
  37Configuration options - PCI236:
  38  [0] - PCI bus of device (optional)
  39  [1] - PCI slot of device (optional)
  40  If bus/slot is not specified, the first available PCI device will be
  41  used.
  42
  43The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
  44as subdevice 0.
  45
  46Subdevice 1 pretends to be a digital input device, but it always returns
  470 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
  48a rising edge on port C bit 3 acts as an external trigger, which can be
  49used to wake up tasks.  This is like the comedi_parport device, but the
  50only way to physically disable the interrupt on the PC36AT is to remove
  51the IRQ jumper.  If no interrupt is connected, then subdevice 1 is
  52unused.
  53*/
  54
  55#include <linux/interrupt.h>
  56
  57#include "../comedidev.h"
  58
  59#include "comedi_pci.h"
  60
  61#include "8255.h"
  62#include "plx9052.h"
  63
  64#define PC236_DRIVER_NAME       "amplc_pc236"
  65
  66/* PCI236 PCI configuration register information */
  67#define PCI_VENDOR_ID_AMPLICON 0x14dc
  68#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
  69#define PCI_DEVICE_ID_INVALID 0xffff
  70
  71/* PC36AT / PCI236 registers */
  72
  73#define PC236_IO_SIZE           4
  74#define PC236_LCR_IO_SIZE       128
  75
  76/*
  77 * INTCSR values for PCI236.
  78 */
  79/* Disable interrupt, also clear any interrupt there */
  80#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
  81        | PLX9052_INTCSR_LI1POL_HIGH \
  82        | PLX9052_INTCSR_LI2POL_HIGH \
  83        | PLX9052_INTCSR_PCIENAB_DISABLED \
  84        | PLX9052_INTCSR_LI1SEL_EDGE \
  85        | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
  86/* Enable interrupt, also clear any interrupt there. */
  87#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
  88        | PLX9052_INTCSR_LI1POL_HIGH \
  89        | PLX9052_INTCSR_LI2POL_HIGH \
  90        | PLX9052_INTCSR_PCIENAB_ENABLED \
  91        | PLX9052_INTCSR_LI1SEL_EDGE \
  92        | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
  93
  94/*
  95 * Board descriptions for Amplicon PC36AT and PCI236.
  96 */
  97
  98enum pc236_bustype { isa_bustype, pci_bustype };
  99enum pc236_model { pc36at_model, pci236_model, anypci_model };
 100
 101struct pc236_board {
 102        const char *name;
 103        const char *fancy_name;
 104        unsigned short devid;
 105        enum pc236_bustype bustype;
 106        enum pc236_model model;
 107};
 108static const struct pc236_board pc236_boards[] = {
 109        {
 110         .name = "pc36at",
 111         .fancy_name = "PC36AT",
 112         .bustype = isa_bustype,
 113         .model = pc36at_model,
 114         },
 115#ifdef CONFIG_COMEDI_PCI
 116        {
 117         .name = "pci236",
 118         .fancy_name = "PCI236",
 119         .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
 120         .bustype = pci_bustype,
 121         .model = pci236_model,
 122         },
 123#endif
 124#ifdef CONFIG_COMEDI_PCI
 125        {
 126         .name = PC236_DRIVER_NAME,
 127         .fancy_name = PC236_DRIVER_NAME,
 128         .devid = PCI_DEVICE_ID_INVALID,
 129         .bustype = pci_bustype,
 130         .model = anypci_model, /* wildcard */
 131         },
 132#endif
 133};
 134
 135#ifdef CONFIG_COMEDI_PCI
 136static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
 137        {
 138        PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236,
 139                    PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
 140        0}
 141};
 142
 143MODULE_DEVICE_TABLE(pci, pc236_pci_table);
 144#endif /* CONFIG_COMEDI_PCI */
 145
 146/*
 147 * Useful for shorthand access to the particular board structure
 148 */
 149#define thisboard ((const struct pc236_board *)dev->board_ptr)
 150
 151/* this structure is for data unique to this hardware driver.  If
 152   several hardware drivers keep similar information in this structure,
 153   feel free to suggest moving the variable to the struct comedi_device struct.
 154 */
 155struct pc236_private {
 156#ifdef CONFIG_COMEDI_PCI
 157        /* PCI device */
 158        struct pci_dev *pci_dev;
 159        unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
 160#endif
 161        int enable_irq;
 162};
 163
 164#define devpriv ((struct pc236_private *)dev->private)
 165
 166/*
 167 * The struct comedi_driver structure tells the Comedi core module
 168 * which functions to call to configure/deconfigure (attach/detach)
 169 * the board, and also about the kernel module that contains
 170 * the device code.
 171 */
 172static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
 173static int pc236_detach(struct comedi_device *dev);
 174static struct comedi_driver driver_amplc_pc236 = {
 175        .driver_name = PC236_DRIVER_NAME,
 176        .module = THIS_MODULE,
 177        .attach = pc236_attach,
 178        .detach = pc236_detach,
 179        .board_name = &pc236_boards[0].name,
 180        .offset = sizeof(struct pc236_board),
 181        .num_names = ARRAY_SIZE(pc236_boards),
 182};
 183
 184#ifdef CONFIG_COMEDI_PCI
 185static int __devinit driver_amplc_pc236_pci_probe(struct pci_dev *dev,
 186                                                  const struct pci_device_id
 187                                                  *ent)
 188{
 189        return comedi_pci_auto_config(dev, driver_amplc_pc236.driver_name);
 190}
 191
 192static void __devexit driver_amplc_pc236_pci_remove(struct pci_dev *dev)
 193{
 194        comedi_pci_auto_unconfig(dev);
 195}
 196
 197static struct pci_driver driver_amplc_pc236_pci_driver = {
 198        .id_table = pc236_pci_table,
 199        .probe = &driver_amplc_pc236_pci_probe,
 200        .remove = __devexit_p(&driver_amplc_pc236_pci_remove)
 201};
 202
 203static int __init driver_amplc_pc236_init_module(void)
 204{
 205        int retval;
 206
 207        retval = comedi_driver_register(&driver_amplc_pc236);
 208        if (retval < 0)
 209                return retval;
 210
 211        driver_amplc_pc236_pci_driver.name =
 212            (char *)driver_amplc_pc236.driver_name;
 213        return pci_register_driver(&driver_amplc_pc236_pci_driver);
 214}
 215
 216static void __exit driver_amplc_pc236_cleanup_module(void)
 217{
 218        pci_unregister_driver(&driver_amplc_pc236_pci_driver);
 219        comedi_driver_unregister(&driver_amplc_pc236);
 220}
 221
 222module_init(driver_amplc_pc236_init_module);
 223module_exit(driver_amplc_pc236_cleanup_module);
 224#else
 225static int __init driver_amplc_pc236_init_module(void)
 226{
 227        return comedi_driver_register(&driver_amplc_pc236);
 228}
 229
 230static void __exit driver_amplc_pc236_cleanup_module(void)
 231{
 232        comedi_driver_unregister(&driver_amplc_pc236);
 233}
 234
 235module_init(driver_amplc_pc236_init_module);
 236module_exit(driver_amplc_pc236_cleanup_module);
 237#endif
 238
 239static int pc236_request_region(unsigned minor, unsigned long from,
 240                                unsigned long extent);
 241static void pc236_intr_disable(struct comedi_device *dev);
 242static void pc236_intr_enable(struct comedi_device *dev);
 243static int pc236_intr_check(struct comedi_device *dev);
 244static int pc236_intr_insn(struct comedi_device *dev,
 245                           struct comedi_subdevice *s, struct comedi_insn *insn,
 246                           unsigned int *data);
 247static int pc236_intr_cmdtest(struct comedi_device *dev,
 248                              struct comedi_subdevice *s,
 249                              struct comedi_cmd *cmd);
 250static int pc236_intr_cmd(struct comedi_device *dev,
 251                          struct comedi_subdevice *s);
 252static int pc236_intr_cancel(struct comedi_device *dev,
 253                             struct comedi_subdevice *s);
 254static irqreturn_t pc236_interrupt(int irq, void *d);
 255
 256/*
 257 * This function looks for a PCI device matching the requested board name,
 258 * bus and slot.
 259 */
 260#ifdef CONFIG_COMEDI_PCI
 261static int
 262pc236_find_pci(struct comedi_device *dev, int bus, int slot,
 263               struct pci_dev **pci_dev_p)
 264{
 265        struct pci_dev *pci_dev = NULL;
 266
 267        *pci_dev_p = NULL;
 268
 269        /* Look for matching PCI device. */
 270        for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
 271             pci_dev != NULL;
 272             pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
 273                                      PCI_ANY_ID, pci_dev)) {
 274                /* If bus/slot specified, check them. */
 275                if (bus || slot) {
 276                        if (bus != pci_dev->bus->number
 277                            || slot != PCI_SLOT(pci_dev->devfn))
 278                                continue;
 279                }
 280                if (thisboard->model == anypci_model) {
 281                        /* Match any supported model. */
 282                        int i;
 283
 284                        for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
 285                                if (pc236_boards[i].bustype != pci_bustype)
 286                                        continue;
 287                                if (pci_dev->device == pc236_boards[i].devid) {
 288                                        /* Change board_ptr to matched board. */
 289                                        dev->board_ptr = &pc236_boards[i];
 290                                        break;
 291                                }
 292                        }
 293                        if (i == ARRAY_SIZE(pc236_boards))
 294                                continue;
 295                } else {
 296                        /* Match specific model name. */
 297                        if (pci_dev->device != thisboard->devid)
 298                                continue;
 299                }
 300
 301                /* Found a match. */
 302                *pci_dev_p = pci_dev;
 303                return 0;
 304        }
 305        /* No match found. */
 306        if (bus || slot) {
 307                printk(KERN_ERR
 308                       "comedi%d: error! no %s found at pci %02x:%02x!\n",
 309                       dev->minor, thisboard->name, bus, slot);
 310        } else {
 311                printk(KERN_ERR "comedi%d: error! no %s found!\n",
 312                       dev->minor, thisboard->name);
 313        }
 314        return -EIO;
 315}
 316#endif
 317
 318/*
 319 * Attach is called by the Comedi core to configure the driver
 320 * for a particular board.  If you specified a board_name array
 321 * in the driver structure, dev->board_ptr contains that
 322 * address.
 323 */
 324static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 325{
 326        struct comedi_subdevice *s;
 327        unsigned long iobase = 0;
 328        unsigned int irq = 0;
 329#ifdef CONFIG_COMEDI_PCI
 330        struct pci_dev *pci_dev = NULL;
 331        int bus = 0, slot = 0;
 332#endif
 333        int share_irq = 0;
 334        int ret;
 335
 336        printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
 337               PC236_DRIVER_NAME);
 338/*
 339 * Allocate the private structure area.  alloc_private() is a
 340 * convenient macro defined in comedidev.h.
 341 */
 342        ret = alloc_private(dev, sizeof(struct pc236_private));
 343        if (ret < 0) {
 344                printk(KERN_ERR "comedi%d: error! out of memory!\n",
 345                       dev->minor);
 346                return ret;
 347        }
 348        /* Process options. */
 349        switch (thisboard->bustype) {
 350        case isa_bustype:
 351                iobase = it->options[0];
 352                irq = it->options[1];
 353                share_irq = 0;
 354                break;
 355#ifdef CONFIG_COMEDI_PCI
 356        case pci_bustype:
 357                bus = it->options[0];
 358                slot = it->options[1];
 359                share_irq = 1;
 360
 361                ret = pc236_find_pci(dev, bus, slot, &pci_dev);
 362                if (ret < 0)
 363                        return ret;
 364                devpriv->pci_dev = pci_dev;
 365                break;
 366#endif /* CONFIG_COMEDI_PCI */
 367        default:
 368                printk(KERN_ERR
 369                       "comedi%d: %s: BUG! cannot determine board type!\n",
 370                       dev->minor, PC236_DRIVER_NAME);
 371                return -EINVAL;
 372                break;
 373        }
 374
 375/*
 376 * Initialize dev->board_name.
 377 */
 378        dev->board_name = thisboard->name;
 379
 380        /* Enable device and reserve I/O spaces. */
 381#ifdef CONFIG_COMEDI_PCI
 382        if (pci_dev) {
 383
 384                ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
 385                if (ret < 0) {
 386                        printk(KERN_ERR
 387                               "comedi%d: error! cannot enable PCI device and request regions!\n",
 388                               dev->minor);
 389                        return ret;
 390                }
 391                devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
 392                iobase = pci_resource_start(pci_dev, 2);
 393                irq = pci_dev->irq;
 394        } else
 395#endif
 396        {
 397                ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
 398                if (ret < 0)
 399                        return ret;
 400        }
 401        dev->iobase = iobase;
 402
 403/*
 404 * Allocate the subdevice structures.  alloc_subdevice() is a
 405 * convenient macro defined in comedidev.h.
 406 */
 407        ret = alloc_subdevices(dev, 2);
 408        if (ret < 0) {
 409                printk(KERN_ERR "comedi%d: error! out of memory!\n",
 410                       dev->minor);
 411                return ret;
 412        }
 413
 414        s = dev->subdevices + 0;
 415        /* digital i/o subdevice (8255) */
 416        ret = subdev_8255_init(dev, s, NULL, iobase);
 417        if (ret < 0) {
 418                printk(KERN_ERR "comedi%d: error! out of memory!\n",
 419                       dev->minor);
 420                return ret;
 421        }
 422        s = dev->subdevices + 1;
 423        dev->read_subdev = s;
 424        s->type = COMEDI_SUBD_UNUSED;
 425        pc236_intr_disable(dev);
 426        if (irq) {
 427                unsigned long flags = share_irq ? IRQF_SHARED : 0;
 428
 429                if (request_irq(irq, pc236_interrupt, flags,
 430                                PC236_DRIVER_NAME, dev) >= 0) {
 431                        dev->irq = irq;
 432                        s->type = COMEDI_SUBD_DI;
 433                        s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 434                        s->n_chan = 1;
 435                        s->maxdata = 1;
 436                        s->range_table = &range_digital;
 437                        s->insn_bits = pc236_intr_insn;
 438                        s->do_cmdtest = pc236_intr_cmdtest;
 439                        s->do_cmd = pc236_intr_cmd;
 440                        s->cancel = pc236_intr_cancel;
 441                }
 442        }
 443        printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
 444        if (thisboard->bustype == isa_bustype) {
 445                printk("(base %#lx) ", iobase);
 446        } else {
 447#ifdef CONFIG_COMEDI_PCI
 448                printk("(pci %s) ", pci_name(pci_dev));
 449#endif
 450        }
 451        if (irq)
 452                printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
 453        else
 454                printk("(no irq) ");
 455
 456        printk("attached\n");
 457
 458        return 1;
 459}
 460
 461/*
 462 * _detach is called to deconfigure a device.  It should deallocate
 463 * resources.
 464 * This function is also called when _attach() fails, so it should be
 465 * careful not to release resources that were not necessarily
 466 * allocated by _attach().  dev->private and dev->subdevices are
 467 * deallocated automatically by the core.
 468 */
 469static int pc236_detach(struct comedi_device *dev)
 470{
 471        printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
 472               PC236_DRIVER_NAME);
 473        if (devpriv)
 474                pc236_intr_disable(dev);
 475
 476        if (dev->irq)
 477                free_irq(dev->irq, dev);
 478        if (dev->subdevices)
 479                subdev_8255_cleanup(dev, dev->subdevices + 0);
 480        if (devpriv) {
 481#ifdef CONFIG_COMEDI_PCI
 482                if (devpriv->pci_dev) {
 483                        if (dev->iobase)
 484                                comedi_pci_disable(devpriv->pci_dev);
 485                        pci_dev_put(devpriv->pci_dev);
 486                } else
 487#endif
 488                {
 489                        if (dev->iobase)
 490                                release_region(dev->iobase, PC236_IO_SIZE);
 491                }
 492        }
 493        if (dev->board_name) {
 494                printk(KERN_INFO "comedi%d: %s removed\n",
 495                       dev->minor, dev->board_name);
 496        }
 497        return 0;
 498}
 499
 500/*
 501 * This function checks and requests an I/O region, reporting an error
 502 * if there is a conflict.
 503 */
 504static int pc236_request_region(unsigned minor, unsigned long from,
 505                                unsigned long extent)
 506{
 507        if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
 508                printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
 509                       minor, from, extent);
 510                return -EIO;
 511        }
 512        return 0;
 513}
 514
 515/*
 516 * This function is called to mark the interrupt as disabled (no command
 517 * configured on subdevice 1) and to physically disable the interrupt
 518 * (not possible on the PC36AT, except by removing the IRQ jumper!).
 519 */
 520static void pc236_intr_disable(struct comedi_device *dev)
 521{
 522        unsigned long flags;
 523
 524        spin_lock_irqsave(&dev->spinlock, flags);
 525        devpriv->enable_irq = 0;
 526#ifdef CONFIG_COMEDI_PCI
 527        if (devpriv->lcr_iobase)
 528                outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
 529#endif
 530        spin_unlock_irqrestore(&dev->spinlock, flags);
 531}
 532
 533/*
 534 * This function is called to mark the interrupt as enabled (a command
 535 * configured on subdevice 1) and to physically enable the interrupt
 536 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
 537 */
 538static void pc236_intr_enable(struct comedi_device *dev)
 539{
 540        unsigned long flags;
 541
 542        spin_lock_irqsave(&dev->spinlock, flags);
 543        devpriv->enable_irq = 1;
 544#ifdef CONFIG_COMEDI_PCI
 545        if (devpriv->lcr_iobase)
 546                outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
 547#endif
 548        spin_unlock_irqrestore(&dev->spinlock, flags);
 549}
 550
 551/*
 552 * This function is called when an interrupt occurs to check whether
 553 * the interrupt has been marked as enabled and was generated by the
 554 * board.  If so, the function prepares the hardware for the next
 555 * interrupt.
 556 * Returns 0 if the interrupt should be ignored.
 557 */
 558static int pc236_intr_check(struct comedi_device *dev)
 559{
 560        int retval = 0;
 561        unsigned long flags;
 562
 563        spin_lock_irqsave(&dev->spinlock, flags);
 564        if (devpriv->enable_irq) {
 565                retval = 1;
 566#ifdef CONFIG_COMEDI_PCI
 567                if (devpriv->lcr_iobase) {
 568                        if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
 569                             & PLX9052_INTCSR_LI1STAT_MASK)
 570                            == PLX9052_INTCSR_LI1STAT_INACTIVE) {
 571                                retval = 0;
 572                        } else {
 573                                /* Clear interrupt and keep it enabled. */
 574                                outl(PCI236_INTR_ENABLE,
 575                                     devpriv->lcr_iobase + PLX9052_INTCSR);
 576                        }
 577                }
 578#endif
 579        }
 580        spin_unlock_irqrestore(&dev->spinlock, flags);
 581
 582        return retval;
 583}
 584
 585/*
 586 * Input from subdevice 1.
 587 * Copied from the comedi_parport driver.
 588 */
 589static int pc236_intr_insn(struct comedi_device *dev,
 590                           struct comedi_subdevice *s, struct comedi_insn *insn,
 591                           unsigned int *data)
 592{
 593        data[1] = 0;
 594        return 2;
 595}
 596
 597/*
 598 * Subdevice 1 command test.
 599 * Copied from the comedi_parport driver.
 600 */
 601static int pc236_intr_cmdtest(struct comedi_device *dev,
 602                              struct comedi_subdevice *s,
 603                              struct comedi_cmd *cmd)
 604{
 605        int err = 0;
 606        int tmp;
 607
 608        /* step 1 */
 609
 610        tmp = cmd->start_src;
 611        cmd->start_src &= TRIG_NOW;
 612        if (!cmd->start_src || tmp != cmd->start_src)
 613                err++;
 614
 615        tmp = cmd->scan_begin_src;
 616        cmd->scan_begin_src &= TRIG_EXT;
 617        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 618                err++;
 619
 620        tmp = cmd->convert_src;
 621        cmd->convert_src &= TRIG_FOLLOW;
 622        if (!cmd->convert_src || tmp != cmd->convert_src)
 623                err++;
 624
 625        tmp = cmd->scan_end_src;
 626        cmd->scan_end_src &= TRIG_COUNT;
 627        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 628                err++;
 629
 630        tmp = cmd->stop_src;
 631        cmd->stop_src &= TRIG_NONE;
 632        if (!cmd->stop_src || tmp != cmd->stop_src)
 633                err++;
 634
 635        if (err)
 636                return 1;
 637
 638        /* step 2: ignored */
 639
 640        if (err)
 641                return 2;
 642
 643        /* step 3: */
 644
 645        if (cmd->start_arg != 0) {
 646                cmd->start_arg = 0;
 647                err++;
 648        }
 649        if (cmd->scan_begin_arg != 0) {
 650                cmd->scan_begin_arg = 0;
 651                err++;
 652        }
 653        if (cmd->convert_arg != 0) {
 654                cmd->convert_arg = 0;
 655                err++;
 656        }
 657        if (cmd->scan_end_arg != 1) {
 658                cmd->scan_end_arg = 1;
 659                err++;
 660        }
 661        if (cmd->stop_arg != 0) {
 662                cmd->stop_arg = 0;
 663                err++;
 664        }
 665
 666        if (err)
 667                return 3;
 668
 669        /* step 4: ignored */
 670
 671        if (err)
 672                return 4;
 673
 674        return 0;
 675}
 676
 677/*
 678 * Subdevice 1 command.
 679 */
 680static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 681{
 682        pc236_intr_enable(dev);
 683
 684        return 0;
 685}
 686
 687/*
 688 * Subdevice 1 cancel command.
 689 */
 690static int pc236_intr_cancel(struct comedi_device *dev,
 691                             struct comedi_subdevice *s)
 692{
 693        pc236_intr_disable(dev);
 694
 695        return 0;
 696}
 697
 698/*
 699 * Interrupt service routine.
 700 * Based on the comedi_parport driver.
 701 */
 702static irqreturn_t pc236_interrupt(int irq, void *d)
 703{
 704        struct comedi_device *dev = d;
 705        struct comedi_subdevice *s = dev->subdevices + 1;
 706        int handled;
 707
 708        handled = pc236_intr_check(dev);
 709        if (dev->attached && handled) {
 710                comedi_buf_put(s->async, 0);
 711                s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
 712                comedi_event(dev, s);
 713        }
 714        return IRQ_RETVAL(handled);
 715}
 716
 717MODULE_AUTHOR("Comedi http://www.comedi.org");
 718MODULE_DESCRIPTION("Comedi low-level driver");
 719MODULE_LICENSE("GPL");
 720
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.