pcmuio.c revision 741ba8cdf0e938d131b49fda37f80057286e459b
1/* 2 * pcmuio.c 3 * Comedi driver for Winsystems PC-104 based 48/96-channel DIO boards. 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19/* 20 * Driver: pcmuio 21 * Description: Winsystems PC-104 based 48/96-channel DIO boards. 22 * Devices: (Winsystems) PCM-UIO48A [pcmuio48] 23 * (Winsystems) PCM-UIO96A [pcmuio96] 24 * Author: Calin Culianu <calin@ajvar.org> 25 * Updated: Fri, 13 Jan 2006 12:01:01 -0500 26 * Status: works 27 * 28 * A driver for the relatively straightforward-to-program PCM-UIO48A and 29 * PCM-UIO96A boards from Winsystems. These boards use either one or two 30 * (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO). This 31 * chip is interesting in that each I/O line is individually programmable 32 * for INPUT or OUTPUT (thus comedi_dio_config can be done on a per-channel 33 * basis). Also, each chip supports edge-triggered interrupts for the first 34 * 24 I/O lines. Of course, since the 96-channel version of the board has 35 * two ASICs, it can detect polarity changes on up to 48 I/O lines. Since 36 * this is essentially an (non-PnP) ISA board, I/O Address and IRQ selection 37 * are done through jumpers on the board. You need to pass that information 38 * to this driver as the first and second comedi_config option, respectively. 39 * Note that the 48-channel version uses 16 bytes of IO memory and the 96- 40 * channel version uses 32-bytes (in case you are worried about conflicts). 41 * The 48-channel board is split into two 24-channel comedi subdevices. The 42 * 96-channel board is split into 4 24-channel DIO subdevices. 43 * 44 * Note that IRQ support has been added, but it is untested. 45 * 46 * To use edge-detection IRQ support, pass the IRQs of both ASICS (for the 47 * 96 channel version) or just 1 ASIC (for 48-channel version). Then, use 48 * comedi_commands with TRIG_NOW. Your callback will be called each time an 49 * edge is triggered, and the data values will be two sample_t's, which 50 * should be concatenated to form one 32-bit unsigned int. This value is 51 * the mask of channels that had edges detected from your channel list. Note 52 * that the bits positions in the mask correspond to positions in your 53 * chanlist when you specified the command and *not* channel id's! 54 * 55 * To set the polarity of the edge-detection interrupts pass a nonzero value 56 * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for 57 * both CR_RANGE and CR_AREF if you want edge-down polarity. 58 * 59 * In the 48-channel version: 60 * 61 * On subdev 0, the first 24 channels channels are edge-detect channels. 62 * 63 * In the 96-channel board you have the following channels that can do edge 64 * detection: 65 * 66 * subdev 0, channels 0-24 (first 24 channels of 1st ASIC) 67 * subdev 2, channels 0-24 (first 24 channels of 2nd ASIC) 68 * 69 * Configuration Options: 70 * [0] - I/O port base address 71 * [1] - IRQ (for first ASIC, or first 24 channels) 72 * [2] - IRQ (for second ASIC, pcmuio96 only - IRQ for chans 48-72 73 * can be the same as first irq!) 74 */ 75 76#include <linux/module.h> 77#include <linux/interrupt.h> 78#include <linux/slab.h> 79 80#include "../comedidev.h" 81 82#include "comedi_fc.h" 83 84/* 85 * Register I/O map 86 * 87 * Offset Page 0 Page 1 Page 2 Page 3 88 * ------ ----------- ----------- ----------- ----------- 89 * 0x00 Port 0 I/O Port 0 I/O Port 0 I/O Port 0 I/O 90 * 0x01 Port 1 I/O Port 1 I/O Port 1 I/O Port 1 I/O 91 * 0x02 Port 2 I/O Port 2 I/O Port 2 I/O Port 2 I/O 92 * 0x03 Port 3 I/O Port 3 I/O Port 3 I/O Port 3 I/O 93 * 0x04 Port 4 I/O Port 4 I/O Port 4 I/O Port 4 I/O 94 * 0x05 Port 5 I/O Port 5 I/O Port 5 I/O Port 5 I/O 95 * 0x06 INT_PENDING INT_PENDING INT_PENDING INT_PENDING 96 * 0x07 Page/Lock Page/Lock Page/Lock Page/Lock 97 * 0x08 N/A POL_0 ENAB_0 INT_ID0 98 * 0x09 N/A POL_1 ENAB_1 INT_ID1 99 * 0x0a N/A POL_2 ENAB_2 INT_ID2 100 */ 101#define PCMUIO_PORT_REG(x) (0x00 + (x)) 102#define PCMUIO_INT_PENDING_REG 0x06 103#define PCMUIO_PAGE_LOCK_REG 0x07 104#define PCMUIO_LOCK_PORT(x) ((1 << (x)) & 0x3f) 105#define PCMUIO_PAGE(x) (((x) & 0x3) << 6) 106#define PCMUIO_PAGE_MASK PCMUIO_PAGE(3) 107#define PCMUIO_PAGE_POL 1 108#define PCMUIO_PAGE_ENAB 2 109#define PCMUIO_PAGE_INT_ID 3 110#define PCMUIO_PAGE_REG(x) (0x08 + (x)) 111 112#define PCMUIO_ASIC_IOSIZE 0x10 113#define PCMUIO_MAX_ASICS 2 114 115struct pcmuio_board { 116 const char *name; 117 const int num_asics; 118}; 119 120static const struct pcmuio_board pcmuio_boards[] = { 121 { 122 .name = "pcmuio48", 123 .num_asics = 1, 124 }, { 125 .name = "pcmuio96", 126 .num_asics = 2, 127 }, 128}; 129 130struct pcmuio_subdev_private { 131 /* The below is only used for intr subdevices */ 132 struct { 133 /* if non-negative, this subdev has an interrupt asic */ 134 int asic; 135 /* 136 * subdev-relative channel mask for channels 137 * we are interested in 138 */ 139 int enabled_mask; 140 int active; 141 int stop_count; 142 int continuous; 143 spinlock_t spinlock; 144 } intr; 145}; 146 147struct pcmuio_private { 148 struct { 149 spinlock_t pagelock; 150 } asics[PCMUIO_MAX_ASICS]; 151 struct pcmuio_subdev_private *sprivs; 152 unsigned int irq2; 153}; 154 155static void pcmuio_write(struct comedi_device *dev, unsigned int val, 156 int asic, int page, int port) 157{ 158 struct pcmuio_private *devpriv = dev->private; 159 unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE); 160 unsigned long flags; 161 162 spin_lock_irqsave(&devpriv->asics[asic].pagelock, flags); 163 if (page == 0) { 164 /* Port registers are valid for any page */ 165 outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0)); 166 outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1)); 167 outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2)); 168 } else { 169 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG); 170 outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0)); 171 outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1)); 172 outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2)); 173 } 174 spin_unlock_irqrestore(&devpriv->asics[asic].pagelock, flags); 175} 176 177static unsigned int pcmuio_read(struct comedi_device *dev, 178 int asic, int page, int port) 179{ 180 struct pcmuio_private *devpriv = dev->private; 181 unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE); 182 unsigned long flags; 183 unsigned int val; 184 185 spin_lock_irqsave(&devpriv->asics[asic].pagelock, flags); 186 if (page == 0) { 187 /* Port registers are valid for any page */ 188 val = inb(iobase + PCMUIO_PORT_REG(port + 0)); 189 val |= (inb(iobase + PCMUIO_PORT_REG(port + 1)) << 8); 190 val |= (inb(iobase + PCMUIO_PORT_REG(port + 2)) << 16); 191 } else { 192 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG); 193 val = inb(iobase + PCMUIO_PAGE_REG(0)); 194 val |= (inb(iobase + PCMUIO_PAGE_REG(1)) << 8); 195 val |= (inb(iobase + PCMUIO_PAGE_REG(2)) << 16); 196 } 197 spin_unlock_irqrestore(&devpriv->asics[asic].pagelock, flags); 198 199 return val; 200} 201 202/* 203 * Each channel can be individually programmed for input or output. 204 * Writing a '0' to a channel causes the corresponding output pin 205 * to go to a high-z state (pulled high by an external 10K resistor). 206 * This allows it to be used as an input. When used in the input mode, 207 * a read reflects the inverted state of the I/O pin, such that a 208 * high on the pin will read as a '0' in the register. Writing a '1' 209 * to a bit position causes the pin to sink current (up to 12mA), 210 * effectively pulling it low. 211 */ 212static int pcmuio_dio_insn_bits(struct comedi_device *dev, 213 struct comedi_subdevice *s, 214 struct comedi_insn *insn, unsigned int *data) 215{ 216 unsigned int mask = data[0] & s->io_bits; /* outputs only */ 217 unsigned int bits = data[1]; 218 int asic = s->index / 2; 219 int port = (s->index % 2) ? 3 : 0; 220 unsigned int val; 221 222 /* get inverted state of the channels from the port */ 223 val = pcmuio_read(dev, asic, 0, port); 224 225 /* get the true state of the channels */ 226 s->state = val ^ ((0x1 << s->n_chan) - 1); 227 228 if (mask) { 229 s->state &= ~mask; 230 s->state |= (mask & bits); 231 232 /* invert the state and update the channels */ 233 val = s->state ^ ((0x1 << s->n_chan) - 1); 234 pcmuio_write(dev, val, asic, 0, port); 235 } 236 237 data[1] = s->state; 238 239 return insn->n; 240} 241 242static int pcmuio_dio_insn_config(struct comedi_device *dev, 243 struct comedi_subdevice *s, 244 struct comedi_insn *insn, 245 unsigned int *data) 246{ 247 int asic = s->index / 2; 248 int port = (s->index % 2) ? 3 : 0; 249 int ret; 250 251 ret = comedi_dio_insn_config(dev, s, insn, data, 0); 252 if (ret) 253 return ret; 254 255 if (data[0] == INSN_CONFIG_DIO_INPUT) 256 pcmuio_write(dev, s->io_bits, asic, 0, port); 257 258 return insn->n; 259} 260 261static void pcmuio_reset(struct comedi_device *dev) 262{ 263 const struct pcmuio_board *board = comedi_board(dev); 264 int asic; 265 266 for (asic = 0; asic < board->num_asics; ++asic) { 267 /* first, clear all the DIO port bits */ 268 pcmuio_write(dev, 0, asic, 0, 0); 269 pcmuio_write(dev, 0, asic, 0, 3); 270 271 /* Next, clear all the paged registers for each page */ 272 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0); 273 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0); 274 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0); 275 } 276} 277 278static void pcmuio_stop_intr(struct comedi_device *dev, 279 struct comedi_subdevice *s) 280{ 281 struct pcmuio_subdev_private *subpriv = s->private; 282 int asic; 283 284 asic = subpriv->intr.asic; 285 if (asic < 0) 286 return; /* not an interrupt subdev */ 287 288 subpriv->intr.enabled_mask = 0; 289 subpriv->intr.active = 0; 290 s->async->inttrig = NULL; 291 292 /* disable all intrs for this subdev.. */ 293 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0); 294} 295 296static void pcmuio_handle_intr_subdev(struct comedi_device *dev, 297 struct comedi_subdevice *s, 298 unsigned triggered) 299{ 300 struct pcmuio_subdev_private *subpriv = s->private; 301 unsigned int len = s->async->cmd.chanlist_len; 302 unsigned oldevents = s->async->events; 303 unsigned int val = 0; 304 unsigned long flags; 305 unsigned mytrig; 306 unsigned int i; 307 308 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 309 310 if (!subpriv->intr.active) 311 goto done; 312 313 mytrig = triggered; 314 mytrig &= ((0x1 << s->n_chan) - 1); 315 316 if (!(mytrig & subpriv->intr.enabled_mask)) 317 goto done; 318 319 for (i = 0; i < len; i++) { 320 unsigned int chan = CR_CHAN(s->async->cmd.chanlist[i]); 321 if (mytrig & (1U << chan)) 322 val |= (1U << i); 323 } 324 325 /* Write the scan to the buffer. */ 326 if (comedi_buf_put(s->async, val) && 327 comedi_buf_put(s->async, val >> 16)) { 328 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); 329 } else { 330 /* Overflow! Stop acquisition!! */ 331 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 332 pcmuio_stop_intr(dev, s); 333 } 334 335 /* Check for end of acquisition. */ 336 if (!subpriv->intr.continuous) { 337 /* stop_src == TRIG_COUNT */ 338 if (subpriv->intr.stop_count > 0) { 339 subpriv->intr.stop_count--; 340 if (subpriv->intr.stop_count == 0) { 341 s->async->events |= COMEDI_CB_EOA; 342 /* TODO: STOP_ACQUISITION_CALL_HERE!! */ 343 pcmuio_stop_intr(dev, s); 344 } 345 } 346 } 347 348done: 349 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 350 351 if (oldevents != s->async->events) 352 comedi_event(dev, s); 353} 354 355static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic) 356{ 357 /* there are could be two asics so we can't use dev->read_subdev */ 358 struct comedi_subdevice *s = &dev->subdevices[asic * 2]; 359 unsigned long iobase = dev->iobase + (asic * PCMUIO_ASIC_IOSIZE); 360 unsigned int val; 361 362 /* are there any interrupts pending */ 363 val = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07; 364 if (!val) 365 return 0; 366 367 /* get, and clear, the pending interrupts */ 368 val = pcmuio_read(dev, asic, PCMUIO_PAGE_INT_ID, 0); 369 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0); 370 371 /* handle the pending interrupts */ 372 pcmuio_handle_intr_subdev(dev, s, val); 373 374 return 1; 375} 376 377static irqreturn_t pcmuio_interrupt(int irq, void *d) 378{ 379 struct comedi_device *dev = d; 380 struct pcmuio_private *devpriv = dev->private; 381 int handled = 0; 382 383 if (irq == dev->irq) 384 handled += pcmuio_handle_asic_interrupt(dev, 0); 385 if (irq == devpriv->irq2) 386 handled += pcmuio_handle_asic_interrupt(dev, 1); 387 388 return handled ? IRQ_HANDLED : IRQ_NONE; 389} 390 391static int pcmuio_start_intr(struct comedi_device *dev, 392 struct comedi_subdevice *s) 393{ 394 struct pcmuio_subdev_private *subpriv = s->private; 395 396 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) { 397 /* An empty acquisition! */ 398 s->async->events |= COMEDI_CB_EOA; 399 subpriv->intr.active = 0; 400 return 1; 401 } else { 402 unsigned bits = 0, pol_bits = 0, n; 403 int asic; 404 struct comedi_cmd *cmd = &s->async->cmd; 405 406 asic = subpriv->intr.asic; 407 if (asic < 0) 408 return 1; /* not an interrupt 409 subdev */ 410 subpriv->intr.enabled_mask = 0; 411 subpriv->intr.active = 1; 412 if (cmd->chanlist) { 413 for (n = 0; n < cmd->chanlist_len; n++) { 414 bits |= (1U << CR_CHAN(cmd->chanlist[n])); 415 pol_bits |= (CR_AREF(cmd->chanlist[n]) 416 || CR_RANGE(cmd-> 417 chanlist[n]) ? 1U : 0U) 418 << CR_CHAN(cmd->chanlist[n]); 419 } 420 } 421 bits &= ((0x1 << s->n_chan) - 1); 422 subpriv->intr.enabled_mask = bits; 423 424 /* set pol and enab intrs for this subdev.. */ 425 pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0); 426 pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0); 427 } 428 return 0; 429} 430 431static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 432{ 433 struct pcmuio_subdev_private *subpriv = s->private; 434 unsigned long flags; 435 436 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 437 if (subpriv->intr.active) 438 pcmuio_stop_intr(dev, s); 439 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 440 441 return 0; 442} 443 444/* 445 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice. 446 */ 447static int 448pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s, 449 unsigned int trignum) 450{ 451 struct pcmuio_subdev_private *subpriv = s->private; 452 unsigned long flags; 453 int event = 0; 454 455 if (trignum != 0) 456 return -EINVAL; 457 458 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 459 s->async->inttrig = NULL; 460 if (subpriv->intr.active) 461 event = pcmuio_start_intr(dev, s); 462 463 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 464 465 if (event) 466 comedi_event(dev, s); 467 468 return 1; 469} 470 471/* 472 * 'do_cmd' function for an 'INTERRUPT' subdevice. 473 */ 474static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 475{ 476 struct pcmuio_subdev_private *subpriv = s->private; 477 struct comedi_cmd *cmd = &s->async->cmd; 478 unsigned long flags; 479 int event = 0; 480 481 spin_lock_irqsave(&subpriv->intr.spinlock, flags); 482 subpriv->intr.active = 1; 483 484 /* Set up end of acquisition. */ 485 switch (cmd->stop_src) { 486 case TRIG_COUNT: 487 subpriv->intr.continuous = 0; 488 subpriv->intr.stop_count = cmd->stop_arg; 489 break; 490 default: 491 /* TRIG_NONE */ 492 subpriv->intr.continuous = 1; 493 subpriv->intr.stop_count = 0; 494 break; 495 } 496 497 /* Set up start of acquisition. */ 498 switch (cmd->start_src) { 499 case TRIG_INT: 500 s->async->inttrig = pcmuio_inttrig_start_intr; 501 break; 502 default: 503 /* TRIG_NOW */ 504 event = pcmuio_start_intr(dev, s); 505 break; 506 } 507 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags); 508 509 if (event) 510 comedi_event(dev, s); 511 512 return 0; 513} 514 515static int pcmuio_cmdtest(struct comedi_device *dev, 516 struct comedi_subdevice *s, 517 struct comedi_cmd *cmd) 518{ 519 int err = 0; 520 521 /* Step 1 : check if triggers are trivially valid */ 522 523 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); 524 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 525 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); 526 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 527 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 528 529 if (err) 530 return 1; 531 532 /* Step 2a : make sure trigger sources are unique */ 533 534 err |= cfc_check_trigger_is_unique(cmd->start_src); 535 err |= cfc_check_trigger_is_unique(cmd->stop_src); 536 537 /* Step 2b : and mutually compatible */ 538 539 if (err) 540 return 2; 541 542 /* Step 3: check if arguments are trivially valid */ 543 544 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 545 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 546 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 547 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 548 549 switch (cmd->stop_src) { 550 case TRIG_COUNT: 551 /* any count allowed */ 552 break; 553 case TRIG_NONE: 554 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 555 break; 556 default: 557 break; 558 } 559 560 if (err) 561 return 3; 562 563 /* step 4: fix up any arguments */ 564 565 /* if (err) return 4; */ 566 567 return 0; 568} 569 570static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it) 571{ 572 const struct pcmuio_board *board = comedi_board(dev); 573 struct comedi_subdevice *s; 574 struct pcmuio_private *devpriv; 575 struct pcmuio_subdev_private *subpriv; 576 int sdev_no, n_subdevs, asic; 577 int ret; 578 579 ret = comedi_request_region(dev, it->options[0], 580 board->num_asics * PCMUIO_ASIC_IOSIZE); 581 if (ret) 582 return ret; 583 584 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 585 if (!devpriv) 586 return -ENOMEM; 587 588 for (asic = 0; asic < PCMUIO_MAX_ASICS; ++asic) 589 spin_lock_init(&devpriv->asics[asic].pagelock); 590 591 pcmuio_reset(dev); 592 593 if (it->options[1]) { 594 /* request the irq for the 1st asic */ 595 ret = request_irq(it->options[1], pcmuio_interrupt, 0, 596 dev->board_name, dev); 597 if (ret == 0) 598 dev->irq = it->options[1]; 599 } 600 601 if (board->num_asics == 2) { 602 if (it->options[2] == dev->irq) { 603 /* the same irq (or none) is used by both asics */ 604 devpriv->irq2 = it->options[2]; 605 } else if (it->options[2]) { 606 /* request the irq for the 2nd asic */ 607 ret = request_irq(it->options[2], pcmuio_interrupt, 0, 608 dev->board_name, dev); 609 if (ret == 0) 610 devpriv->irq2 = it->options[2]; 611 } 612 } 613 614 n_subdevs = board->num_asics * 2; 615 devpriv->sprivs = kcalloc(n_subdevs, sizeof(*subpriv), GFP_KERNEL); 616 if (!devpriv->sprivs) 617 return -ENOMEM; 618 619 ret = comedi_alloc_subdevices(dev, n_subdevs); 620 if (ret) 621 return ret; 622 623 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) { 624 s = &dev->subdevices[sdev_no]; 625 subpriv = &devpriv->sprivs[sdev_no]; 626 s->private = subpriv; 627 s->maxdata = 1; 628 s->range_table = &range_digital; 629 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 630 s->type = COMEDI_SUBD_DIO; 631 s->insn_bits = pcmuio_dio_insn_bits; 632 s->insn_config = pcmuio_dio_insn_config; 633 s->n_chan = 24; 634 635 /* subdevices 0 and 2 can suppport interrupts */ 636 if ((sdev_no == 0 && dev->irq) || 637 (sdev_no == 2 && devpriv->irq2)) { 638 /* setup the interrupt subdevice */ 639 subpriv->intr.asic = sdev_no / 2; 640 dev->read_subdev = s; 641 s->subdev_flags |= SDF_CMD_READ; 642 s->cancel = pcmuio_cancel; 643 s->do_cmd = pcmuio_cmd; 644 s->do_cmdtest = pcmuio_cmdtest; 645 s->len_chanlist = s->n_chan; 646 } else { 647 subpriv->intr.asic = -1; 648 s->len_chanlist = 1; 649 } 650 spin_lock_init(&subpriv->intr.spinlock); 651 } 652 653 return 0; 654} 655 656static void pcmuio_detach(struct comedi_device *dev) 657{ 658 struct pcmuio_private *devpriv = dev->private; 659 660 if (devpriv) { 661 pcmuio_reset(dev); 662 663 /* free the 2nd irq if used, the core will free the 1st one */ 664 if (devpriv->irq2 && devpriv->irq2 != dev->irq) 665 free_irq(devpriv->irq2, dev); 666 667 kfree(devpriv->sprivs); 668 } 669 comedi_legacy_detach(dev); 670} 671 672static struct comedi_driver pcmuio_driver = { 673 .driver_name = "pcmuio", 674 .module = THIS_MODULE, 675 .attach = pcmuio_attach, 676 .detach = pcmuio_detach, 677 .board_name = &pcmuio_boards[0].name, 678 .offset = sizeof(struct pcmuio_board), 679 .num_names = ARRAY_SIZE(pcmuio_boards), 680}; 681module_comedi_driver(pcmuio_driver); 682 683MODULE_AUTHOR("Comedi http://www.comedi.org"); 684MODULE_DESCRIPTION("Comedi low-level driver"); 685MODULE_LICENSE("GPL"); 686