RTXI 1.3
comedi/comedi/drivers/pcmmio.c
Go to the documentation of this file.
00001 /*
00002     comedi/drivers/pcmmio.c
00003     Driver for Winsystems PC-104 based multifunction IO board.
00004 
00005     COMEDI - Linux Control and Measurement Device Interface
00006     Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 */
00022 /*
00023 Driver: pcmmio
00024 Description: A driver for the PCM-MIO multifunction board
00025 Devices: [Winsystems] PCM-MIO (pcmmio)
00026 Author: Calin Culianu <calin@ajvar.org>
00027 Updated: Wed, May 16 2007 16:21:10 -0500
00028 Status: works
00029 
00030 A driver for the relatively new PCM-MIO multifunction board from
00031 Winsystems.  This board is a PC-104 based I/O board.  It contains
00032 four subdevices:
00033   subdevice 0 - 16 channels of 16-bit AI
00034   subdevice 1 - 8 channels of 16-bit AO
00035   subdevice 2 - first 24 channels of the 48 channel of DIO (with edge-triggered interrupt support)
00036   subdevice 3 - last 24 channels of the 48 channel DIO (no interrupt support for this bank of channels)
00037 
00038   Some notes:
00039 
00040   Synchronous reads and writes are the only things implemented for AI and AO,
00041   even though the hardware itself can do streaming acquisition, etc.  Anyone
00042   want to add asynchronous I/O for AI/AO as a feature?  Be my guest...
00043 
00044   Asynchronous I/O for the DIO subdevices *is* implemented, however!  They are
00045   basically edge-triggered interrupts for any configuration of the first
00046   24 DIO-lines.
00047 
00048   Also note that this interrupt support is untested.
00049 
00050   A few words about edge-detection IRQ support (commands on DIO):
00051 
00052   * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
00053     of the board to the comedi_config command.  The board IRQ is not jumpered
00054     but rather configured through software, so any IRQ from 1-15 is OK.
00055 
00056   * Due to the genericity of the comedi API, you need to create a special
00057     comedi_command in order to use edge-triggered interrupts for DIO.
00058 
00059   * Use comedi_commands with TRIG_NOW.  Your callback will be called each
00060     time an edge is detected on the specified DIO line(s), and the data
00061     values will be two sample_t's, which should be concatenated to form
00062     one 32-bit unsigned int.  This value is the mask of channels that had
00063     edges detected from your channel list.  Note that the bits positions
00064     in the mask correspond to positions in your chanlist when you
00065     specified the command and *not* channel id's!
00066 
00067  *  To set the polarity of the edge-detection interrupts pass a nonzero value
00068     for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
00069     value for both CR_RANGE and CR_AREF if you want edge-down polarity.
00070 
00071 Configuration Options:
00072   [0] - I/O port base address
00073   [1] - IRQ (optional -- for edge-detect interrupt support only, leave out if you don't need this feature)
00074 */
00075 
00076 #include <linux/comedidev.h>
00077 #include <linux/pci.h>          /* for PCI devices */
00078 
00079 #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
00080 
00081 /* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
00082 #define CHANS_PER_PORT   8
00083 #define PORTS_PER_ASIC   6
00084 #define INTR_PORTS_PER_ASIC   3
00085 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
00086 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
00087 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
00088 #define INTR_CHANS_PER_ASIC 24
00089 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
00090 #define MAX_DIO_CHANS   (PORTS_PER_ASIC*1*CHANS_PER_PORT)
00091 #define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
00092 #define SDEV_NO ((int)(s - dev->subdevices))
00093 #define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
00094 /* IO Memory sizes */
00095 #define ASIC_IOSIZE (0x0B)
00096 #define PCMMIO48_IOSIZE ASIC_IOSIZE
00097 
00098 /* Some offsets - these are all in the 16byte IO memory offset from
00099    the base address.  Note that there is a paging scheme to swap out
00100    offsets 0x8-0xA using the PAGELOCK register.  See the table below.
00101 
00102   Register(s)       Pages        R/W?        Description
00103   --------------------------------------------------------------
00104   REG_PORTx         All          R/W         Read/Write/Configure IO
00105   REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
00106   REG_PAGELOCK      All          WriteOnly   Select a page
00107   REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
00108   REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
00109   REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
00110  */
00111 #define REG_PORT0 0x0
00112 #define REG_PORT1 0x1
00113 #define REG_PORT2 0x2
00114 #define REG_PORT3 0x3
00115 #define REG_PORT4 0x4
00116 #define REG_PORT5 0x5
00117 #define REG_INT_PENDING 0x6
00118 #define REG_PAGELOCK 0x7        /* page selector register, upper 2 bits select a page
00119                                    and bits 0-5 are used to 'lock down' a particular
00120                                    port above to make it readonly.  */
00121 #define REG_POL0 0x8
00122 #define REG_POL1 0x9
00123 #define REG_POL2 0xA
00124 #define REG_ENAB0 0x8
00125 #define REG_ENAB1 0x9
00126 #define REG_ENAB2 0xA
00127 #define REG_INT_ID0 0x8
00128 #define REG_INT_ID1 0x9
00129 #define REG_INT_ID2 0xA
00130 
00131 #define NUM_PAGED_REGS 3
00132 #define NUM_PAGES 4
00133 #define FIRST_PAGED_REG 0x8
00134 #define REG_PAGE_BITOFFSET 6
00135 #define REG_LOCK_BITOFFSET 0
00136 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
00137 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
00138 #define PAGE_POL 1
00139 #define PAGE_ENAB 2
00140 #define PAGE_INT_ID 3
00141 
00142 typedef int (*comedi_insn_fn_t) (comedi_device *, comedi_subdevice *,
00143         comedi_insn *, lsampl_t *);
00144 
00145 static int ai_rinsn(comedi_device *, comedi_subdevice *, comedi_insn *,
00146         lsampl_t *);
00147 static int ao_rinsn(comedi_device *, comedi_subdevice *, comedi_insn *,
00148         lsampl_t *);
00149 static int ao_winsn(comedi_device *, comedi_subdevice *, comedi_insn *,
00150         lsampl_t *);
00151 
00152 /*
00153  * Board descriptions for two imaginary boards.  Describing the
00154  * boards in this way is optional, and completely driver-dependent.
00155  * Some drivers use arrays such as this, other do not.
00156  */
00157 typedef struct pcmmio_board_struct {
00158         const char *name;
00159         const int dio_num_asics;
00160         const int dio_num_ports;
00161         const int total_iosize;
00162         const int ai_bits;
00163         const int ao_bits;
00164         const int n_ai_chans;
00165         const int n_ao_chans;
00166         const comedi_lrange *ai_range_table, *ao_range_table;
00167         comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
00168 } pcmmio_board;
00169 
00170 static const comedi_lrange ranges_ai =
00171         { 4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0.,
00172                 10.)}
00173 };
00174 
00175 static const comedi_lrange ranges_ao =
00176         { 6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
00177         RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
00178 };
00179 
00180 static const pcmmio_board pcmmio_boards[] = {
00181         {
00182               name:     "pcmmio",
00183               dio_num_asics:1,
00184               dio_num_ports:6,
00185               total_iosize:32,
00186               ai_bits:  16,
00187               ao_bits:  16,
00188               n_ai_chans:16,
00189               n_ao_chans:8,
00190               ai_range_table:&ranges_ai,
00191               ao_range_table:&ranges_ao,
00192               ai_rinsn:ai_rinsn,
00193               ao_rinsn:ao_rinsn,
00194       ao_winsn:ao_winsn},
00195 };
00196 
00197 /*
00198  * Useful for shorthand access to the particular board structure
00199  */
00200 #define thisboard ((const pcmmio_board *)dev->board_ptr)
00201 
00202 /* this structure is for data unique to this subdevice.  */
00203 typedef struct {
00204 
00205         union {
00206                 /* for DIO: mapping of halfwords (bytes) in port/chanarray to iobase */
00207                 unsigned long iobases[PORTS_PER_SUBDEV];
00208 
00209                 /* for AI/AO */
00210                 unsigned long iobase;
00211         };
00212         union {
00213                 struct {
00214 
00215                         /* The below is only used for intr subdevices */
00216                         struct {
00217                                 int asic;       /* if non-negative, this subdev has an interrupt asic */
00218                                 int first_chan; /* if nonnegative, the first channel id for
00219                                                    interrupts. */
00220                                 int num_asic_chans;     /* the number of asic channels in this subdev
00221                                                            that have interrutps */
00222                                 int asic_chan;  /* if nonnegative, the first channel id with
00223                                                    respect to the asic that has interrupts */
00224                                 int enabled_mask;       /* subdev-relative channel mask for channels
00225                                                            we are interested in */
00226                                 int active;
00227                                 int stop_count;
00228                                 int continuous;
00229                                 spinlock_t spinlock;
00230                         } intr;
00231                 } dio;
00232                 struct {
00233                         lsampl_t shadow_samples[8];     /* the last lsampl_t data written */
00234                 } ao;
00235         };
00236 } pcmmio_subdev_private;
00237 
00238 /* this structure is for data unique to this hardware driver.  If
00239    several hardware drivers keep similar information in this structure,
00240    feel free to suggest moving the variable to the comedi_device struct.  */
00241 typedef struct {
00242         /* stuff for DIO */
00243         struct {
00244                 unsigned char pagelock; /* current page and lock */
00245                 unsigned char pol[NUM_PAGED_REGS];      /* shadow of POLx registers */
00246                 unsigned char enab[NUM_PAGED_REGS];     /* shadow of ENABx registers */
00247                 int num;
00248                 unsigned long iobase;
00249                 unsigned int irq;
00250                 spinlock_t spinlock;
00251         } asics[MAX_ASICS];
00252         pcmmio_subdev_private *sprivs;
00253 } pcmmio_private;
00254 
00255 /*
00256  * most drivers define the following macro to make it easy to
00257  * access the private structure.
00258  */
00259 #define devpriv ((pcmmio_private *)dev->private)
00260 #define subpriv ((pcmmio_subdev_private *)s->private)
00261 /*
00262  * The comedi_driver structure tells the Comedi core module
00263  * which functions to call to configure/deconfigure (attach/detach)
00264  * the board, and also about the kernel module that contains
00265  * the device code.
00266  */
00267 static int pcmmio_attach(comedi_device * dev, comedi_devconfig * it);
00268 static int pcmmio_detach(comedi_device * dev);
00269 
00270 static comedi_driver driver = {
00271       driver_name:"pcmmio",
00272       module:THIS_MODULE,
00273       attach:pcmmio_attach,
00274       detach:pcmmio_detach,
00275 /* It is not necessary to implement the following members if you are
00276  * writing a driver for a ISA PnP or PCI card */
00277         /* Most drivers will support multiple types of boards by
00278          * having an array of board structures.  These were defined
00279          * in pcmmio_boards[] above.  Note that the element 'name'
00280          * was first in the structure -- Comedi uses this fact to
00281          * extract the name of the board without knowing any details
00282          * about the structure except for its length.
00283          * When a device is attached (by comedi_config), the name
00284          * of the device is given to Comedi, and Comedi tries to
00285          * match it by going through the list of board names.  If
00286          * there is a match, the address of the pointer is put
00287          * into dev->board_ptr and driver->attach() is called.
00288          *
00289          * Note that these are not necessary if you can determine
00290          * the type of board in software.  ISA PnP, PCI, and PCMCIA
00291          * devices are such boards.
00292          */
00293       board_name:&pcmmio_boards[0].name,
00294       offset:sizeof(pcmmio_board),
00295       num_names:sizeof(pcmmio_boards) / sizeof(pcmmio_board),
00296 };
00297 
00298 static int pcmmio_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
00299         comedi_insn * insn, lsampl_t * data);
00300 static int pcmmio_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
00301         comedi_insn * insn, lsampl_t * data);
00302 
00303 static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG);
00304 static void pcmmio_stop_intr(comedi_device *, comedi_subdevice *);
00305 static int pcmmio_cancel(comedi_device * dev, comedi_subdevice * s);
00306 static int pcmmio_cmd(comedi_device * dev, comedi_subdevice * s);
00307 static int pcmmio_cmdtest(comedi_device * dev, comedi_subdevice * s,
00308         comedi_cmd * cmd);
00309 
00310 /* some helper functions to deal with specifics of this device's registers */
00311 static void init_asics(comedi_device * dev);    /* sets up/clears ASIC chips to defaults */
00312 static void switch_page(comedi_device * dev, int asic, int page);
00313 #ifdef notused
00314 static void lock_port(comedi_device * dev, int asic, int port);
00315 static void unlock_port(comedi_device * dev, int asic, int port);
00316 #endif
00317 
00318 /*
00319  * Attach is called by the Comedi core to configure the driver
00320  * for a particular board.  If you specified a board_name array
00321  * in the driver structure, dev->board_ptr contains that
00322  * address.
00323  */
00324 static int pcmmio_attach(comedi_device * dev, comedi_devconfig * it)
00325 {
00326         comedi_subdevice *s;
00327         int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
00328                 thisasic_chanct = 0;
00329         unsigned long iobase;
00330         unsigned int irq[MAX_ASICS];
00331 
00332         iobase = it->options[0];
00333         irq[0] = it->options[1];
00334 
00335         printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
00336                 iobase);
00337 
00338         dev->iobase = iobase;
00339 
00340         if (!iobase || !request_region(iobase,
00341                         thisboard->total_iosize, driver.driver_name)) {
00342                 printk("I/O port conflict\n");
00343                 return -EIO;
00344         }
00345 
00346 /*
00347  * Initialize dev->board_name.  Note that we can use the "thisboard"
00348  * macro now, since we just initialized it in the last line.
00349  */
00350         dev->board_name = thisboard->name;
00351 
00352 /*
00353  * Allocate the private structure area.  alloc_private() is a
00354  * convenient macro defined in comedidev.h.
00355  */
00356         if (alloc_private(dev, sizeof(pcmmio_private)) < 0) {
00357                 printk("cannot allocate private data structure\n");
00358                 return -ENOMEM;
00359         }
00360 
00361         for (asic = 0; asic < MAX_ASICS; ++asic) {
00362                 devpriv->asics[asic].num = asic;
00363                 devpriv->asics[asic].iobase =
00364                         dev->iobase + 16 + asic * ASIC_IOSIZE;
00365                 devpriv->asics[asic].irq = 0;   /* this gets actually set at the end of
00366                                                    this function when we
00367                                                    comedi_request_irqs */
00368                 spin_lock_init(&devpriv->asics[asic].spinlock);
00369         }
00370 
00371         chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
00372         n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
00373         n_subdevs = n_dio_subdevs + 2;
00374         devpriv->sprivs =
00375                 kcalloc(n_subdevs, sizeof(pcmmio_subdev_private), GFP_KERNEL);
00376         if (!devpriv->sprivs) {
00377                 printk("cannot allocate subdevice private data structures\n");
00378                 return -ENOMEM;
00379         }
00380         /*
00381          * Allocate the subdevice structures.  alloc_subdevice() is a
00382          * convenient macro defined in comedidev.h.
00383          *
00384          * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
00385          */
00386         if (alloc_subdevices(dev, n_subdevs) < 0) {
00387                 printk("cannot allocate subdevice data structures\n");
00388                 return -ENOMEM;
00389         }
00390 
00391         /* First, AI */
00392         sdev_no = 0;
00393         s = dev->subdevices + sdev_no;
00394         s->private = devpriv->sprivs + sdev_no;
00395         s->maxdata = (1 << thisboard->ai_bits) - 1;
00396         s->range_table = thisboard->ai_range_table;
00397         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
00398         s->type = COMEDI_SUBD_AI;
00399         s->n_chan = thisboard->n_ai_chans;
00400         s->len_chanlist = s->n_chan;
00401         s->insn_read = thisboard->ai_rinsn;
00402         subpriv->iobase = dev->iobase + 0;
00403         /* initialize the resource enable register by clearing it */
00404         outb(0, subpriv->iobase + 3);
00405         outb(0, subpriv->iobase + 4 + 3);
00406 
00407         /* Next, AO */
00408         ++sdev_no;
00409         s = dev->subdevices + sdev_no;
00410         s->private = devpriv->sprivs + sdev_no;
00411         s->maxdata = (1 << thisboard->ao_bits) - 1;
00412         s->range_table = thisboard->ao_range_table;
00413         s->subdev_flags = SDF_READABLE;
00414         s->type = COMEDI_SUBD_AO;
00415         s->n_chan = thisboard->n_ao_chans;
00416         s->len_chanlist = s->n_chan;
00417         s->insn_read = thisboard->ao_rinsn;
00418         s->insn_write = thisboard->ao_winsn;
00419         subpriv->iobase = dev->iobase + 8;
00420         /* initialize the resource enable register by clearing it */
00421         outb(0, subpriv->iobase + 3);
00422         outb(0, subpriv->iobase + 4 + 3);
00423 
00424         ++sdev_no;
00425         port = 0;
00426         asic = 0;
00427         for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
00428                 int byte_no;
00429 
00430                 s = dev->subdevices + sdev_no;
00431                 s->private = devpriv->sprivs + sdev_no;
00432                 s->maxdata = 1;
00433                 s->range_table = &range_digital;
00434                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
00435                 s->type = COMEDI_SUBD_DIO;
00436                 s->insn_bits = pcmmio_dio_insn_bits;
00437                 s->insn_config = pcmmio_dio_insn_config;
00438                 s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
00439                 subpriv->dio.intr.asic = -1;
00440                 subpriv->dio.intr.first_chan = -1;
00441                 subpriv->dio.intr.asic_chan = -1;
00442                 subpriv->dio.intr.num_asic_chans = -1;
00443                 subpriv->dio.intr.active = 0;
00444                 s->len_chanlist = 1;
00445 
00446                 /* save the ioport address for each 'port' of 8 channels in the
00447                    subdevice */
00448                 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
00449                         if (port >= PORTS_PER_ASIC) {
00450                                 port = 0;
00451                                 ++asic;
00452                                 thisasic_chanct = 0;
00453                         }
00454                         subpriv->iobases[byte_no] =
00455                                 devpriv->asics[asic].iobase + port;
00456 
00457                         if (thisasic_chanct <
00458                                 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
00459                                 && subpriv->dio.intr.asic < 0) {
00460                                 /* this is an interrupt subdevice, so setup the struct */
00461                                 subpriv->dio.intr.asic = asic;
00462                                 subpriv->dio.intr.active = 0;
00463                                 subpriv->dio.intr.stop_count = 0;
00464                                 subpriv->dio.intr.first_chan = byte_no * 8;
00465                                 subpriv->dio.intr.asic_chan = thisasic_chanct;
00466                                 subpriv->dio.intr.num_asic_chans =
00467                                         s->n_chan -
00468                                         subpriv->dio.intr.first_chan;
00469                                 s->cancel = pcmmio_cancel;
00470                                 s->do_cmd = pcmmio_cmd;
00471                                 s->do_cmdtest = pcmmio_cmdtest;
00472                                 s->len_chanlist =
00473                                         subpriv->dio.intr.num_asic_chans;
00474                         }
00475                         thisasic_chanct += CHANS_PER_PORT;
00476                 }
00477                 spin_lock_init(&subpriv->dio.intr.spinlock);
00478 
00479                 chans_left -= s->n_chan;
00480 
00481                 if (!chans_left) {
00482                         asic = 0;       /* reset the asic to our first asic, to do intr subdevs */
00483                         port = 0;
00484                 }
00485 
00486         }
00487 
00488         init_asics(dev);        /* clear out all the registers, basically */
00489 
00490         for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
00491                 if (irq[asic]
00492                         && comedi_request_irq(irq[asic], interrupt_pcmmio,
00493                                 IRQF_SHARED, thisboard->name, dev)) {
00494                         int i;
00495                         /* unroll the allocated irqs.. */
00496                         for (i = asic - 1; i >= 0; --i) {
00497                                 comedi_free_irq(irq[i], dev);
00498                                 devpriv->asics[i].irq = irq[i] = 0;
00499                         }
00500                         irq[asic] = 0;
00501                 }
00502                 devpriv->asics[asic].irq = irq[asic];
00503         }
00504 
00505         dev->irq = irq[0];      /* grr.. wish comedi dev struct supported multiple
00506                                    irqs.. */
00507 
00508         if (irq[0]) {
00509                 printk("irq: %u ", irq[0]);
00510                 if (irq[1] && thisboard->dio_num_asics == 2)
00511                         printk("second ASIC irq: %u ", irq[1]);
00512         } else {
00513                 printk("(IRQ mode disabled) ");
00514         }
00515 
00516         printk("attached\n");
00517 
00518         return 1;
00519 }
00520 
00521 /*
00522  * _detach is called to deconfigure a device.  It should deallocate
00523  * resources.
00524  * This function is also called when _attach() fails, so it should be
00525  * careful not to release resources that were not necessarily
00526  * allocated by _attach().  dev->private and dev->subdevices are
00527  * deallocated automatically by the core.
00528  */
00529 static int pcmmio_detach(comedi_device * dev)
00530 {
00531         int i;
00532 
00533         printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
00534         if (dev->iobase)
00535                 release_region(dev->iobase, thisboard->total_iosize);
00536 
00537         for (i = 0; i < MAX_ASICS; ++i) {
00538                 if (devpriv && devpriv->asics[i].irq)
00539                         comedi_free_irq(devpriv->asics[i].irq, dev);
00540         }
00541 
00542         if (devpriv && devpriv->sprivs)
00543                 kfree(devpriv->sprivs);
00544 
00545         return 0;
00546 }
00547 
00548 /* DIO devices are slightly special.  Although it is possible to
00549  * implement the insn_read/insn_write interface, it is much more
00550  * useful to applications if you implement the insn_bits interface.
00551  * This allows packed reading/writing of the DIO channels.  The
00552  * comedi core can convert between insn_bits and insn_read/write */
00553 static int pcmmio_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
00554         comedi_insn * insn, lsampl_t * data)
00555 {
00556         int byte_no;
00557         if (insn->n != 2)
00558                 return -EINVAL;
00559 
00560         /* NOTE:
00561            reading a 0 means this channel was high
00562            writine a 0 sets the channel high
00563            reading a 1 means this channel was low
00564            writing a 1 means set this channel low
00565 
00566            Therefore everything is always inverted. */
00567 
00568         /* The insn data is a mask in data[0] and the new data
00569          * in data[1], each channel cooresponding to a bit. */
00570 
00571 #ifdef DAMMIT_ITS_BROKEN
00572         /* DEBUG */
00573         printk("write mask: %08x  data: %08x\n", data[0], data[1]);
00574 #endif
00575 
00576         s->state = 0;
00577 
00578         for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
00579                 /* address of 8-bit port */
00580                 unsigned long ioaddr = subpriv->iobases[byte_no],
00581                         /* bit offset of port in 32-bit doubleword */
00582                         offset = byte_no * 8;
00583                 /* this 8-bit port's data */
00584                 unsigned char byte = 0,
00585                         /* The write mask for this port (if any) */
00586                         write_mask_byte = (data[0] >> offset) & 0xff,
00587                         /* The data byte for this port */
00588                         data_byte = (data[1] >> offset) & 0xff;
00589 
00590                 byte = inb(ioaddr);     /* read all 8-bits for this port */
00591 
00592 #ifdef DAMMIT_ITS_BROKEN
00593                 /* DEBUG */
00594                 printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
00595 #endif
00596 
00597                 if (write_mask_byte) {
00598                         /* this byte has some write_bits -- so set the output lines */
00599                         byte &= ~write_mask_byte;       /* clear bits for write mask */
00600                         byte |= ~data_byte & write_mask_byte;   /* set to inverted data_byte */
00601                         /* Write out the new digital output state */
00602                         outb(byte, ioaddr);
00603                 }
00604 #ifdef DAMMIT_ITS_BROKEN
00605                 /* DEBUG */
00606                 printk("data_out_byte %02x\n", (unsigned)byte);
00607 #endif
00608                 /* save the digital input lines for this byte.. */
00609                 s->state |= ((unsigned int)byte) << offset;
00610         }
00611 
00612         /* now return the DIO lines to data[1] - note they came inverted! */
00613         data[1] = ~s->state;
00614 
00615 #ifdef DAMMIT_ITS_BROKEN
00616         /* DEBUG */
00617         printk("s->state %08x data_out %08x\n", s->state, data[1]);
00618 #endif
00619 
00620         return 2;
00621 }
00622 
00623 /* The input or output configuration of each digital line is
00624  * configured by a special insn_config instruction.  chanspec
00625  * contains the channel to be changed, and data[0] contains the
00626  * value COMEDI_INPUT or COMEDI_OUTPUT. */
00627 static int pcmmio_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
00628         comedi_insn * insn, lsampl_t * data)
00629 {
00630         int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
00631                 chan % 8;
00632         unsigned long ioaddr;
00633         unsigned char byte;
00634 
00635         /* Compute ioaddr for this channel */
00636         ioaddr = subpriv->iobases[byte_no];
00637 
00638         /* NOTE:
00639            writing a 0 an IO channel's bit sets the channel to INPUT
00640            and pulls the line high as well
00641 
00642            writing a 1 to an IO channel's  bit pulls the line low
00643 
00644            All channels are implicitly always in OUTPUT mode -- but when
00645            they are high they can be considered to be in INPUT mode..
00646 
00647            Thus, we only force channels low if the config request was INPUT,
00648            otherwise we do nothing to the hardware.    */
00649 
00650         switch (data[0]) {
00651         case INSN_CONFIG_DIO_OUTPUT:
00652                 /* save to io_bits -- don't actually do anything since
00653                    all input channels are also output channels... */
00654                 s->io_bits |= 1 << chan;
00655                 break;
00656         case INSN_CONFIG_DIO_INPUT:
00657                 /* write a 0 to the actual register representing the channel
00658                    to set it to 'input'.  0 means "float high". */
00659                 byte = inb(ioaddr);
00660                 byte &= ~(1 << bit_no);
00663                 /* write out byte -- this is the only time we actually affect the
00664                    hardware as all channels are implicitly output -- but input
00665                    channels are set to float-high */
00666                 outb(byte, ioaddr);
00667 
00668                 /* save to io_bits */
00669                 s->io_bits &= ~(1 << chan);
00670                 break;
00671 
00672         case INSN_CONFIG_DIO_QUERY:
00673                 /* retreive from shadow register */
00674                 data[1] =
00675                         (s->
00676                         io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
00677                 return insn->n;
00678                 break;
00679 
00680         default:
00681                 return -EINVAL;
00682                 break;
00683         }
00684 
00685         return insn->n;
00686 }
00687 
00688 static void init_asics(comedi_device * dev)
00689 {                               /* sets up an
00690                                    ASIC chip to defaults */
00691         int asic;
00692 
00693         for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
00694                 int port, page;
00695                 unsigned long baseaddr = devpriv->asics[asic].iobase;
00696 
00697                 switch_page(dev, asic, 0);      /* switch back to page 0 */
00698 
00699                 /* first, clear all the DIO port bits */
00700                 for (port = 0; port < PORTS_PER_ASIC; ++port)
00701                         outb(0, baseaddr + REG_PORT0 + port);
00702 
00703                 /* Next, clear all the paged registers for each page */
00704                 for (page = 1; page < NUM_PAGES; ++page) {
00705                         int reg;
00706                         /* now clear all the paged registers */
00707                         switch_page(dev, asic, page);
00708                         for (reg = FIRST_PAGED_REG;
00709                                 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
00710                                 outb(0, baseaddr + reg);
00711                 }
00712 
00713                 /* DEBUG  set rising edge interrupts on port0 of both asics */
00714                 /*switch_page(dev, asic, PAGE_POL);
00715                    outb(0xff, baseaddr + REG_POL0);
00716                    switch_page(dev, asic, PAGE_ENAB);
00717                    outb(0xff, baseaddr + REG_ENAB0); */
00718                 /* END DEBUG */
00719 
00720                 switch_page(dev, asic, 0);      /* switch back to default page 0 */
00721 
00722         }
00723 }
00724 
00725 static void switch_page(comedi_device * dev, int asic, int page)
00726 {
00727         if (asic < 0 || asic >= thisboard->dio_num_asics)
00728                 return;         /* paranoia */
00729         if (page < 0 || page >= NUM_PAGES)
00730                 return;         /* more paranoia */
00731 
00732         devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
00733         devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
00734 
00735         /* now write out the shadow register */
00736         outb(devpriv->asics[asic].pagelock,
00737                 devpriv->asics[asic].iobase + REG_PAGELOCK);
00738 }
00739 
00740 #ifdef notused
00741 static void lock_port(comedi_device * dev, int asic, int port)
00742 {
00743         if (asic < 0 || asic >= thisboard->dio_num_asics)
00744                 return;         /* paranoia */
00745         if (port < 0 || port >= PORTS_PER_ASIC)
00746                 return;         /* more paranoia */
00747 
00748         devpriv->asics[asic].pagelock |= 0x1 << port;
00749         /* now write out the shadow register */
00750         outb(devpriv->asics[asic].pagelock,
00751                 devpriv->asics[asic].iobase + REG_PAGELOCK);
00752         return;
00753 }
00754 
00755 static void unlock_port(comedi_device * dev, int asic, int port)
00756 {
00757         if (asic < 0 || asic >= thisboard->dio_num_asics)
00758                 return;         /* paranoia */
00759         if (port < 0 || port >= PORTS_PER_ASIC)
00760                 return;         /* more paranoia */
00761         devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
00762         /* now write out the shadow register */
00763         outb(devpriv->asics[asic].pagelock,
00764                 devpriv->asics[asic].iobase + REG_PAGELOCK);
00765 }
00766 #endif /* notused */
00767 
00768 static irqreturn_t interrupt_pcmmio(int irq, void *d PT_REGS_ARG)
00769 {
00770         int asic, got1 = 0;
00771         comedi_device *dev = (comedi_device *) d;
00772 
00773         for (asic = 0; asic < MAX_ASICS; ++asic) {
00774                 if (irq == devpriv->asics[asic].irq) {
00775                         unsigned long flags;
00776                         unsigned triggered = 0;
00777                         unsigned long iobase = devpriv->asics[asic].iobase;
00778                         /* it is an interrupt for ASIC #asic */
00779                         unsigned char int_pend;
00780 
00781                         comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
00782                                 flags);
00783 
00784                         int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
00785 
00786                         if (int_pend) {
00787                                 int port;
00788                                 for (port = 0; port < INTR_PORTS_PER_ASIC;
00789                                         ++port) {
00790                                         if (int_pend & (0x1 << port)) {
00791                                                 unsigned char
00792                                                         io_lines_with_edges = 0;
00793                                                 switch_page(dev, asic,
00794                                                         PAGE_INT_ID);
00795                                                 io_lines_with_edges =
00796                                                         inb(iobase +
00797                                                         REG_INT_ID0 + port);
00798 
00799                                                 if (io_lines_with_edges)
00800                                                         /* clear pending interrupt */
00801                                                         outb(0, iobase +
00802                                                                 REG_INT_ID0 +
00803                                                                 port);
00804 
00805                                                 triggered |=
00806                                                         io_lines_with_edges <<
00807                                                         port * 8;
00808                                         }
00809                                 }
00810 
00811                                 ++got1;
00812                         }
00813 
00814                         comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
00815                                 spinlock, flags);
00816 
00817                         if (triggered) {
00818                                 comedi_subdevice *s;
00819                                 /* TODO here: dispatch io lines to subdevs with commands.. */
00820                                 printk("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
00821                                 for (s = dev->subdevices + 2;
00822                                         s < dev->subdevices + dev->n_subdevices;
00823                                         ++s) {
00824                                         if (subpriv->dio.intr.asic == asic) {   /* this is an interrupt subdev, and it matches this asic! */
00825                                                 unsigned long flags;
00826                                                 unsigned oldevents;
00827 
00828                                                 comedi_spin_lock_irqsave
00829                                                         (&subpriv->dio.intr.
00830                                                         spinlock, flags);
00831 
00832                                                 oldevents = s->async->events;
00833 
00834                                                 if (subpriv->dio.intr.active) {
00835                                                         unsigned mytrig =
00836                                                                 ((triggered >>
00837                                                                         subpriv->
00838                                                                         dio.
00839                                                                         intr.
00840                                                                         asic_chan)
00841                                                                 & ((0x1 << subpriv->dio.intr.num_asic_chans) - 1)) << subpriv->dio.intr.first_chan;
00842                                                         if (mytrig & subpriv->
00843                                                                 dio.intr.
00844                                                                 enabled_mask) {
00845                                                                 lsampl_t val =
00846                                                                         0;
00847                                                                 unsigned int n,
00848                                                                         ch, len;
00849 
00850                                                                 len = s->async->
00851                                                                         cmd.
00852                                                                         chanlist_len;
00853                                                                 for (n = 0;
00854                                                                         n < len;
00855                                                                         n++) {
00856                                                                         ch = CR_CHAN(s->async->cmd.chanlist[n]);
00857                                                                         if (mytrig & (1U << ch)) {
00858                                                                                 val |= (1U << n);
00859                                                                         }
00860                                                                 }
00861                                                                 /* Write the scan to the buffer. */
00862                                                                 if (comedi_buf_put(s->async, ((sampl_t *) & val)[0])
00863                                                                         &&
00864                                                                         comedi_buf_put
00865                                                                         (s->async, ((sampl_t *) & val)[1])) {
00866                                                                         s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
00867                                                                 } else {
00868                                                                         /* Overflow! Stop acquisition!! */
00869                                                                         /* TODO: STOP_ACQUISITION_CALL_HERE!! */
00870                                                                         pcmmio_stop_intr
00871                                                                                 (dev,
00872                                                                                 s);
00873                                                                 }
00874 
00875                                                                 /* Check for end of acquisition. */
00876                                                                 if (!subpriv->
00877                                                                         dio.
00878                                                                         intr.
00879                                                                         continuous)
00880                                                                 {
00881                                                                         /* stop_src == TRIG_COUNT */
00882                                                                         if (subpriv->dio.intr.stop_count > 0) {
00883                                                                                 subpriv->
00884                                                                                         dio.
00885                                                                                         intr.
00886                                                                                         stop_count--;
00887                                                                                 if (subpriv->dio.intr.stop_count == 0) {
00888                                                                                         s->async->events |= COMEDI_CB_EOA;
00889                                                                                         /* TODO: STOP_ACQUISITION_CALL_HERE!! */
00890                                                                                         pcmmio_stop_intr
00891                                                                                                 (dev,
00892                                                                                                 s);
00893                                                                                 }
00894                                                                         }
00895                                                                 }
00896                                                         }
00897                                                 }
00898 
00899                                                 comedi_spin_unlock_irqrestore
00900                                                         (&subpriv->dio.intr.
00901                                                         spinlock, flags);
00902 
00903                                                 if (oldevents !=
00904                                                         s->async->events) {
00905                                                         comedi_event(dev, s);
00906                                                 }
00907 
00908                                         }
00909 
00910                                 }
00911                         }
00912 
00913                 }
00914         }
00915         if (!got1)
00916                 return IRQ_NONE;        /* interrupt from other source */
00917         return IRQ_HANDLED;
00918 }
00919 
00920 static void pcmmio_stop_intr(comedi_device * dev, comedi_subdevice * s)
00921 {
00922         int nports, firstport, asic, port;
00923 
00924         if ((asic = subpriv->dio.intr.asic) < 0)
00925                 return;         /* not an interrupt subdev */
00926 
00927         subpriv->dio.intr.enabled_mask = 0;
00928         subpriv->dio.intr.active = 0;
00929         s->async->inttrig = 0;
00930         nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
00931         firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
00932         switch_page(dev, asic, PAGE_ENAB);
00933         for (port = firstport; port < firstport + nports; ++port) {
00934                 /* disable all intrs for this subdev.. */
00935                 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
00936         }
00937 }
00938 
00939 static int pcmmio_start_intr(comedi_device * dev, comedi_subdevice * s)
00940 {
00941         if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
00942                 /* An empty acquisition! */
00943                 s->async->events |= COMEDI_CB_EOA;
00944                 subpriv->dio.intr.active = 0;
00945                 return 1;
00946         } else {
00947                 unsigned bits = 0, pol_bits = 0, n;
00948                 int nports, firstport, asic, port;
00949                 comedi_cmd *cmd = &s->async->cmd;
00950 
00951                 if ((asic = subpriv->dio.intr.asic) < 0)
00952                         return 1;       /* not an interrupt
00953                                            subdev */
00954                 subpriv->dio.intr.enabled_mask = 0;
00955                 subpriv->dio.intr.active = 1;
00956                 nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
00957                 firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
00958                 if (cmd->chanlist) {
00959                         for (n = 0; n < cmd->chanlist_len; n++) {
00960                                 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
00961                                 pol_bits |= (CR_AREF(cmd->chanlist[n])
00962                                         || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
00963                                         << CR_CHAN(cmd->chanlist[n]);
00964                         }
00965                 }
00966                 bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
00967                         1) << subpriv->dio.intr.first_chan;
00968                 subpriv->dio.intr.enabled_mask = bits;
00969 
00970                 {               /* the below code configures the board to use a specific IRQ from 0-15. */
00971                         unsigned char b;
00972                         /* set resource enable register to enable IRQ operation */
00973                         outb(1 << 4, dev->iobase + 3);
00974                         /* set bits 0-3 of b to the irq number from 0-15 */
00975                         b = dev->irq & ((1 << 4) - 1);
00976                         outb(b, dev->iobase + 2);
00977                         /* done, we told the board what irq to use */
00978                 }
00979 
00980                 switch_page(dev, asic, PAGE_ENAB);
00981                 for (port = firstport; port < firstport + nports; ++port) {
00982                         unsigned enab =
00983                                 bits >> (subpriv->dio.intr.first_chan + (port -
00984                                         firstport) * 8) & 0xff, pol =
00985                                 pol_bits >> (subpriv->dio.intr.first_chan +
00986                                 (port - firstport) * 8) & 0xff;
00987                         /* set enab intrs for this subdev.. */
00988                         outb(enab,
00989                                 devpriv->asics[asic].iobase + REG_ENAB0 + port);
00990                         switch_page(dev, asic, PAGE_POL);
00991                         outb(pol,
00992                                 devpriv->asics[asic].iobase + REG_ENAB0 + port);
00993                 }
00994         }
00995         return 0;
00996 }
00997 
00998 static int pcmmio_cancel(comedi_device * dev, comedi_subdevice * s)
00999 {
01000         unsigned long flags;
01001 
01002         comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
01003         if (subpriv->dio.intr.active)
01004                 pcmmio_stop_intr(dev, s);
01005         comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
01006 
01007         return 0;
01008 }
01009 
01010 /*
01011  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
01012  */
01013 static int
01014 pcmmio_inttrig_start_intr(comedi_device * dev, comedi_subdevice * s,
01015         unsigned int trignum)
01016 {
01017         unsigned long flags;
01018         int event = 0;
01019 
01020         if (trignum != 0)
01021                 return -EINVAL;
01022 
01023         comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
01024         s->async->inttrig = 0;
01025         if (subpriv->dio.intr.active) {
01026                 event = pcmmio_start_intr(dev, s);
01027         }
01028         comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
01029 
01030         if (event) {
01031                 comedi_event(dev, s);
01032         }
01033 
01034         return 1;
01035 }
01036 
01037 /*
01038  * 'do_cmd' function for an 'INTERRUPT' subdevice.
01039  */
01040 static int pcmmio_cmd(comedi_device * dev, comedi_subdevice * s)
01041 {
01042         comedi_cmd *cmd = &s->async->cmd;
01043         unsigned long flags;
01044         int event = 0;
01045 
01046         comedi_spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
01047         subpriv->dio.intr.active = 1;
01048 
01049         /* Set up end of acquisition. */
01050         switch (cmd->stop_src) {
01051         case TRIG_COUNT:
01052                 subpriv->dio.intr.continuous = 0;
01053                 subpriv->dio.intr.stop_count = cmd->stop_arg;
01054                 break;
01055         default:
01056                 /* TRIG_NONE */
01057                 subpriv->dio.intr.continuous = 1;
01058                 subpriv->dio.intr.stop_count = 0;
01059                 break;
01060         }
01061 
01062         /* Set up start of acquisition. */
01063         switch (cmd->start_src) {
01064         case TRIG_INT:
01065                 s->async->inttrig = pcmmio_inttrig_start_intr;
01066                 break;
01067         default:
01068                 /* TRIG_NOW */
01069                 event = pcmmio_start_intr(dev, s);
01070                 break;
01071         }
01072         comedi_spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
01073 
01074         if (event) {
01075                 comedi_event(dev, s);
01076         }
01077 
01078         return 0;
01079 }
01080 
01081 /*
01082  * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
01083  */
01084 static int
01085 pcmmio_cmdtest(comedi_device * dev, comedi_subdevice * s, comedi_cmd * cmd)
01086 {
01087         int err = 0;
01088         unsigned int tmp;
01089 
01090         /* step 1: make sure trigger sources are trivially valid */
01091 
01092         tmp = cmd->start_src;
01093         cmd->start_src &= (TRIG_NOW | TRIG_INT);
01094         if (!cmd->start_src || tmp != cmd->start_src)
01095                 err++;
01096 
01097         tmp = cmd->scan_begin_src;
01098         cmd->scan_begin_src &= TRIG_EXT;
01099         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
01100                 err++;
01101 
01102         tmp = cmd->convert_src;
01103         cmd->convert_src &= TRIG_NOW;
01104         if (!cmd->convert_src || tmp != cmd->convert_src)
01105                 err++;
01106 
01107         tmp = cmd->scan_end_src;
01108         cmd->scan_end_src &= TRIG_COUNT;
01109         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
01110                 err++;
01111 
01112         tmp = cmd->stop_src;
01113         cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
01114         if (!cmd->stop_src || tmp != cmd->stop_src)
01115                 err++;
01116 
01117         if (err)
01118                 return 1;
01119 
01120         /* step 2: make sure trigger sources are unique and mutually compatible */
01121 
01122         /* these tests are true if more than one _src bit is set */
01123         if ((cmd->start_src & (cmd->start_src - 1)) != 0)
01124                 err++;
01125         if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
01126                 err++;
01127         if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
01128                 err++;
01129         if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
01130                 err++;
01131         if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
01132                 err++;
01133 
01134         if (err)
01135                 return 2;
01136 
01137         /* step 3: make sure arguments are trivially compatible */
01138 
01139         /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
01140         if (cmd->start_arg != 0) {
01141                 cmd->start_arg = 0;
01142                 err++;
01143         }
01144 
01145         /* cmd->scan_begin_src == TRIG_EXT */
01146         if (cmd->scan_begin_arg != 0) {
01147                 cmd->scan_begin_arg = 0;
01148                 err++;
01149         }
01150 
01151         /* cmd->convert_src == TRIG_NOW */
01152         if (cmd->convert_arg != 0) {
01153                 cmd->convert_arg = 0;
01154                 err++;
01155         }
01156 
01157         /* cmd->scan_end_src == TRIG_COUNT */
01158         if (cmd->scan_end_arg != cmd->chanlist_len) {
01159                 cmd->scan_end_arg = cmd->chanlist_len;
01160                 err++;
01161         }
01162 
01163         switch (cmd->stop_src) {
01164         case TRIG_COUNT:
01165                 /* any count allowed */
01166                 break;
01167         case TRIG_NONE:
01168                 if (cmd->stop_arg != 0) {
01169                         cmd->stop_arg = 0;
01170                         err++;
01171                 }
01172                 break;
01173         default:
01174                 break;
01175         }
01176 
01177         if (err)
01178                 return 3;
01179 
01180         /* step 4: fix up any arguments */
01181 
01182         /* if (err) return 4; */
01183 
01184         return 0;
01185 }
01186 
01187 static int adc_wait_ready(unsigned long iobase)
01188 {
01189         unsigned long retry = 100000;
01190         while (retry--)
01191                 if (inb(iobase + 3) & 0x80)
01192                         return 0;
01193         return 1;
01194 }
01195 
01196 /* All this is for AI and AO */
01197 static int ai_rinsn(comedi_device * dev, comedi_subdevice * s,
01198         comedi_insn * insn, lsampl_t * data)
01199 {
01200         int n;
01201         unsigned long iobase = subpriv->iobase;
01202 
01203         /*
01204            1. write the CMD byte (to BASE+2)
01205            2. read junk lo byte (BASE+0)
01206            3. read junk hi byte (BASE+1)
01207            4. (mux settled so) write CMD byte again (BASE+2)
01208            5. read valid lo byte(BASE+0)
01209            6. read valid hi byte(BASE+1)
01210 
01211            Additionally note that the BASE += 4 if the channel >= 8
01212          */
01213 
01214         /* convert n samples */
01215         for (n = 0; n < insn->n; n++) {
01216                 unsigned chan = CR_CHAN(insn->chanspec), range =
01217                         CR_RANGE(insn->chanspec), aref =
01218                         CR_AREF(insn->chanspec);
01219                 unsigned char command_byte = 0;
01220                 unsigned iooffset = 0;
01221                 sampl_t sample, adc_adjust = 0;
01222 
01223                 if (chan > 7)
01224                         chan -= 8, iooffset = 4;        /* use the second dword for channels > 7 */
01225 
01226                 if (aref != AREF_DIFF) {
01227                         aref = AREF_GROUND;
01228                         command_byte |= 1 << 7; /* set bit 7 to indicate single-ended */
01229                 }
01230                 if (range < 2)
01231                         adc_adjust = 0x8000;    /* bipolar ranges (-5,5 .. -10,10 need to be adjusted -- that is.. they need to wrap around by adding 0x8000 */
01232 
01233                 if (chan % 2) {
01234                         command_byte |= 1 << 6; /* odd-numbered channels have bit 6 set */
01235                 }
01236 
01237                 /* select the channel, bits 4-5 == chan/2 */
01238                 command_byte |= ((chan / 2) & 0x3) << 4;
01239 
01240                 /* set the range, bits 2-3 */
01241                 command_byte |= (range & 0x3) << 2;
01242 
01243                 /* need to do this twice to make sure mux settled */
01244                 outb(command_byte, iobase + iooffset + 2);      /* chan/range/aref select */
01245 
01246                 adc_wait_ready(iobase + iooffset);      /* wait for the adc to say it finised the conversion */
01247 
01248                 outb(command_byte, iobase + iooffset + 2);      /* select the chan/range/aref AGAIN */
01249 
01250                 adc_wait_ready(iobase + iooffset);
01251 
01252                 sample = inb(iobase + iooffset + 0);    /* read data lo byte */
01253                 sample |= inb(iobase + iooffset + 1) << 8;      /* read data hi byte */
01254                 sample += adc_adjust;   /* adjustment .. munge data */
01255                 data[n] = sample;
01256         }
01257         /* return the number of samples read/written */
01258         return n;
01259 }
01260 
01261 static int ao_rinsn(comedi_device * dev, comedi_subdevice * s,
01262         comedi_insn * insn, lsampl_t * data)
01263 {
01264         int n;
01265         for (n = 0; n < insn->n; n++) {
01266                 unsigned chan = CR_CHAN(insn->chanspec);
01267                 if (chan < s->n_chan)
01268                         data[n] = subpriv->ao.shadow_samples[chan];
01269         }
01270         return n;
01271 }
01272 
01273 static int wait_dac_ready(unsigned long iobase)
01274 {
01275         unsigned long retry = 100000L;
01276 
01277         /* This may seem like an absurd way to handle waiting and violates the
01278            "no busy waiting" policy. The fact is that the hardware is
01279            normally so fast that we usually only need one time through the loop
01280            anyway. The longer timeout is for rare occasions and for detecting
01281            non-existant hardware.  */
01282 
01283         while (retry--) {
01284                 if (inb(iobase + 3) & 0x80)
01285                         return 0;
01286 
01287         }
01288         return 1;
01289 }
01290 
01291 static int ao_winsn(comedi_device * dev, comedi_subdevice * s,
01292         comedi_insn * insn, lsampl_t * data)
01293 {
01294         int n;
01295         unsigned iobase = subpriv->iobase, iooffset = 0;
01296 
01297         for (n = 0; n < insn->n; n++) {
01298                 unsigned chan = CR_CHAN(insn->chanspec), range =
01299                         CR_RANGE(insn->chanspec);
01300                 if (chan < s->n_chan) {
01301                         unsigned char command_byte = 0, range_byte =
01302                                 range & ((1 << 4) - 1);
01303                         if (chan >= 4)
01304                                 chan -= 4, iooffset += 4;
01305                         /* set the range.. */
01306                         outb(range_byte, iobase + iooffset + 0);
01307                         outb(0, iobase + iooffset + 1);
01308 
01309                         /* tell it to begin */
01310                         command_byte = (chan << 1) | 0x60;
01311                         outb(command_byte, iobase + iooffset + 2);
01312 
01313                         wait_dac_ready(iobase + iooffset);
01314 
01315                         outb(data[n] & 0xff, iobase + iooffset + 0);    /* low order byte */
01316                         outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);     /* high order byte */
01317                         command_byte = 0x70 | (chan << 1);      /* set bit 4 of command byte to indicate data is loaded and trigger conversion */
01318                         /* trigger converion */
01319                         outb(command_byte, iobase + iooffset + 2);
01320 
01321                         wait_dac_ready(iobase + iooffset);
01322 
01323                         subpriv->ao.shadow_samples[chan] = data[n];     /* save to shadow register for ao_rinsn */
01324                 }
01325         }
01326         return n;
01327 }
01328 
01329 /*
01330  * A convenient macro that defines init_module() and cleanup_module(),
01331  * as necessary.
01332  */
01333 COMEDI_INITCLEANUP(driver);
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines