![]() |
RTXI 1.3
|
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);