Commit 124b13b2 authored by Frank Mori Hess's avatar Frank Mori Hess Committed by Greg Kroah-Hartman

Staging: comedi: add ni_labpc drivers

This supports National Instruments Lab-PC and compatibles

From: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: David Schleef <ds@schleef.org>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 59c7dd3d
/*
comedi/drivers/ni_labpc.c
Driver for National Instruments Lab-PC series boards and compatibles
Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
************************************************************************
*/
/*
Driver: ni_labpc
Description: National Instruments Lab-PC (& compatibles)
Author: Frank Mori Hess <fmhess@users.sourceforge.net>
Devices: [National Instruments] Lab-PC-1200 (labpc-1200),
Lab-PC-1200AI (labpc-1200ai), Lab-PC+ (lab-pc+), PCI-1200 (ni_labpc)
Status: works
Tested with lab-pc-1200. For the older Lab-PC+, not all input ranges
and analog references will work, the available ranges/arefs will
depend on how you have configured the jumpers on your board
(see your owner's manual).
Kernel-level ISA plug-and-play support for the lab-pc-1200
boards has not
yet been added to the driver, mainly due to the fact that
I don't know the device id numbers. If you have one
of these boards,
please file a bug report at https://bugs.comedi.org/
so I can get the necessary information from you.
The 1200 series boards have onboard calibration dacs for correcting
analog input/output offsets and gains. The proper settings for these
caldacs are stored on the board's eeprom. To read the caldac values
from the eeprom and store them into a file that can be then be used by
comedilib, use the comedi_calibrate program.
Configuration options - ISA boards:
[0] - I/O port base address
[1] - IRQ (optional, required for timed or externally triggered conversions)
[2] - DMA channel (optional)
Configuration options - PCI boards:
[0] - bus (optional)
[1] - slot (optional)
The Lab-pc+ has quirky chanlist requirements
when scanning multiple channels. Multiple channel scan
sequence must start at highest channel, then decrement down to
channel 0. The rest of the cards can scan down like lab-pc+ or scan
up from channel zero. Chanlists consisting of all one channel
are also legal, and allow you to pace conversions in bursts.
*/
/*
NI manuals:
341309a (labpc-1200 register manual)
340914a (pci-1200)
320502b (lab-pc+)
*/
#undef LABPC_DEBUG
//#define LABPC_DEBUG // enable debugging messages
#include "../comedidev.h"
#include <linux/delay.h>
#include <asm/dma.h>
#include "8253.h"
#include "8255.h"
#include "mite.h"
#include "comedi_fc.h"
#include "ni_labpc.h"
#define DRV_NAME "ni_labpc"
#define LABPC_SIZE 32 // size of io region used by board
#define LABPC_TIMER_BASE 500 // 2 MHz master clock
/* Registers for the lab-pc+ */
//write-only registers
#define COMMAND1_REG 0x0
#define ADC_GAIN_MASK (0x7 << 4)
#define ADC_CHAN_BITS(x) ((x) & 0x7)
#define ADC_SCAN_EN_BIT 0x80 // enables multi channel scans
#define COMMAND2_REG 0x1
#define PRETRIG_BIT 0x1 // enable pretriggering (used in conjunction with SWTRIG)
#define HWTRIG_BIT 0x2 // enable paced conversions on external trigger
#define SWTRIG_BIT 0x4 // enable paced conversions
#define CASCADE_BIT 0x8 // use two cascaded counters for pacing
#define DAC_PACED_BIT(channel) (0x40 << ((channel) & 0x1))
#define COMMAND3_REG 0x2
#define DMA_EN_BIT 0x1 // enable dma transfers
#define DIO_INTR_EN_BIT 0x2 // enable interrupts for 8255
#define DMATC_INTR_EN_BIT 0x4 // enable dma terminal count interrupt
#define TIMER_INTR_EN_BIT 0x8 // enable timer interrupt
#define ERR_INTR_EN_BIT 0x10 // enable error interrupt
#define ADC_FNE_INTR_EN_BIT 0x20 // enable fifo not empty interrupt
#define ADC_CONVERT_REG 0x3
#define DAC_LSB_REG(channel) (0x4 + 2 * ((channel) & 0x1))
#define DAC_MSB_REG(channel) (0x5 + 2 * ((channel) & 0x1))
#define ADC_CLEAR_REG 0x8
#define DMATC_CLEAR_REG 0xa
#define TIMER_CLEAR_REG 0xc
#define COMMAND6_REG 0xe // 1200 boards only
#define ADC_COMMON_BIT 0x1 // select ground or common-mode reference
#define ADC_UNIP_BIT 0x2 // adc unipolar
#define DAC_UNIP_BIT(channel) (0x4 << ((channel) & 0x1)) // dac unipolar
#define ADC_FHF_INTR_EN_BIT 0x20 // enable fifo half full interrupt
#define A1_INTR_EN_BIT 0x40 // enable interrupt on end of hardware count
#define ADC_SCAN_UP_BIT 0x80 // scan up from channel zero instead of down to zero
#define COMMAND4_REG 0xf
#define INTERVAL_SCAN_EN_BIT 0x1 // enables 'interval' scanning
#define EXT_SCAN_EN_BIT 0x2 // enables external signal on counter b1 output to trigger scan
#define EXT_CONVERT_OUT_BIT 0x4 // chooses direction (output or input) for EXTCONV* line
#define ADC_DIFF_BIT 0x8 // chooses differential inputs for adc (in conjunction with board jumper)
#define EXT_CONVERT_DISABLE_BIT 0x10
#define COMMAND5_REG 0x1c // 1200 boards only, calibration stuff
#define EEPROM_WRITE_UNPROTECT_BIT 0x4 // enable eeprom for write
#define DITHER_EN_BIT 0x8 // enable dithering
#define CALDAC_LOAD_BIT 0x10 // load calibration dac
#define SCLOCK_BIT 0x20 // serial clock - rising edge writes, falling edge reads
#define SDATA_BIT 0x40 // serial data bit for writing to eeprom or calibration dacs
#define EEPROM_EN_BIT 0x80 // enable eeprom for read/write
#define INTERVAL_COUNT_REG 0x1e
#define INTERVAL_LOAD_REG 0x1f
#define INTERVAL_LOAD_BITS 0x1
// read-only registers
#define STATUS1_REG 0x0
#define DATA_AVAIL_BIT 0x1 // data is available in fifo
#define OVERRUN_BIT 0x2 // overrun has occurred
#define OVERFLOW_BIT 0x4 // fifo overflow
#define TIMER_BIT 0x8 // timer interrupt has occured
#define DMATC_BIT 0x10 // dma terminal count has occured
#define EXT_TRIG_BIT 0x40 // external trigger has occured
#define STATUS2_REG 0x1d // 1200 boards only
#define EEPROM_OUT_BIT 0x1 // programmable eeprom serial output
#define A1_TC_BIT 0x2 // counter A1 terminal count
#define FNHF_BIT 0x4 // fifo not half full
#define ADC_FIFO_REG 0xa
#define DIO_BASE_REG 0x10
#define COUNTER_A_BASE_REG 0x14
#define COUNTER_A_CONTROL_REG (COUNTER_A_BASE_REG + 0x3)
#define INIT_A0_BITS 0x14 // check modes put conversion pacer output in harmless state (a0 mode 2)
#define INIT_A1_BITS 0x70 // put hardware conversion counter output in harmless state (a1 mode 0)
#define COUNTER_B_BASE_REG 0x18
static int labpc_attach(comedi_device * dev, comedi_devconfig * it);
static int labpc_cancel(comedi_device * dev, comedi_subdevice * s);
static irqreturn_t labpc_interrupt(int irq, void *d PT_REGS_ARG);
static int labpc_drain_fifo(comedi_device * dev);
static void labpc_drain_dma(comedi_device * dev);
static void handle_isa_dma(comedi_device * dev);
static void labpc_drain_dregs(comedi_device * dev);
static int labpc_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
comedi_cmd * cmd);
static int labpc_ai_cmd(comedi_device * dev, comedi_subdevice * s);
static int labpc_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_ao_winsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_calib_read_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_calib_write_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_eeprom_read_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static int labpc_eeprom_write_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data);
static unsigned int labpc_suggest_transfer_size(comedi_cmd cmd);
static void labpc_adc_timing(comedi_device * dev, comedi_cmd * cmd);
#ifdef CONFIG_COMEDI_PCI
static int labpc_find_device(comedi_device *dev, int bus, int slot);
#endif
static int labpc_dio_mem_callback(int dir, int port, int data,
unsigned long arg);
static void labpc_serial_out(comedi_device * dev, unsigned int value,
unsigned int num_bits);
static unsigned int labpc_serial_in(comedi_device * dev);
static unsigned int labpc_eeprom_read(comedi_device * dev,
unsigned int address);
static unsigned int labpc_eeprom_read_status(comedi_device * dev);
static unsigned int labpc_eeprom_write(comedi_device * dev,
unsigned int address, unsigned int value);
static void write_caldac(comedi_device * dev, unsigned int channel,
unsigned int value);
enum scan_mode {
MODE_SINGLE_CHAN,
MODE_SINGLE_CHAN_INTERVAL,
MODE_MULT_CHAN_UP,
MODE_MULT_CHAN_DOWN,
};
//analog input ranges
#define NUM_LABPC_PLUS_AI_RANGES 16
// indicates unipolar ranges
static const int labpc_plus_is_unipolar[NUM_LABPC_PLUS_AI_RANGES] = {
0,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
1,
};
// map range index to gain bits
static const int labpc_plus_ai_gain_bits[NUM_LABPC_PLUS_AI_RANGES] = {
0x00,
0x10,
0x20,
0x30,
0x40,
0x50,
0x60,
0x70,
0x00,
0x10,
0x20,
0x30,
0x40,
0x50,
0x60,
0x70,
};
static const comedi_lrange range_labpc_plus_ai = {
NUM_LABPC_PLUS_AI_RANGES,
{
BIP_RANGE(5),
BIP_RANGE(4),
BIP_RANGE(2.5),
BIP_RANGE(1),
BIP_RANGE(0.5),
BIP_RANGE(0.25),
BIP_RANGE(0.1),
BIP_RANGE(0.05),
UNI_RANGE(10),
UNI_RANGE(8),
UNI_RANGE(5),
UNI_RANGE(2),
UNI_RANGE(1),
UNI_RANGE(0.5),
UNI_RANGE(0.2),
UNI_RANGE(0.1),
}
};
#define NUM_LABPC_1200_AI_RANGES 14
// indicates unipolar ranges
const int labpc_1200_is_unipolar[NUM_LABPC_1200_AI_RANGES] = {
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
1,
1,
1,
};
// map range index to gain bits
const int labpc_1200_ai_gain_bits[NUM_LABPC_1200_AI_RANGES] = {
0x00,
0x20,
0x30,
0x40,
0x50,
0x60,
0x70,
0x00,
0x20,
0x30,
0x40,
0x50,
0x60,
0x70,
};
const comedi_lrange range_labpc_1200_ai = {
NUM_LABPC_1200_AI_RANGES,
{
BIP_RANGE(5),
BIP_RANGE(2.5),
BIP_RANGE(1),
BIP_RANGE(0.5),
BIP_RANGE(0.25),
BIP_RANGE(0.1),
BIP_RANGE(0.05),
UNI_RANGE(10),
UNI_RANGE(5),
UNI_RANGE(2),
UNI_RANGE(1),
UNI_RANGE(0.5),
UNI_RANGE(0.2),
UNI_RANGE(0.1),
}
};
//analog output ranges
#define AO_RANGE_IS_UNIPOLAR 0x1
static const comedi_lrange range_labpc_ao = {
2,
{
BIP_RANGE(5),
UNI_RANGE(10),
}
};
/* functions that do inb/outb and readb/writeb so we can use
* function pointers to decide which to use */
static inline unsigned int labpc_inb(unsigned long address)
{
return inb(address);
}
static inline void labpc_outb(unsigned int byte, unsigned long address)
{
outb(byte, address);
}
static inline unsigned int labpc_readb(unsigned long address)
{
return readb((void *)address);
}
static inline void labpc_writeb(unsigned int byte, unsigned long address)
{
writeb(byte, (void *)address);
}
static const labpc_board labpc_boards[] = {
{
name: "lab-pc-1200",
ai_speed:10000,
bustype: isa_bustype,
register_layout:labpc_1200_layout,
has_ao: 1,
ai_range_table:&range_labpc_1200_ai,
ai_range_code:labpc_1200_ai_gain_bits,
ai_range_is_unipolar:labpc_1200_is_unipolar,
ai_scan_up:1,
memory_mapped_io:0,
},
{
name: "lab-pc-1200ai",
ai_speed:10000,
bustype: isa_bustype,
register_layout:labpc_1200_layout,
has_ao: 0,
ai_range_table:&range_labpc_1200_ai,
ai_range_code:labpc_1200_ai_gain_bits,
ai_range_is_unipolar:labpc_1200_is_unipolar,
ai_scan_up:1,
memory_mapped_io:0,
},
{
name: "lab-pc+",
ai_speed:12000,
bustype: isa_bustype,
register_layout:labpc_plus_layout,
has_ao: 1,
ai_range_table:&range_labpc_plus_ai,
ai_range_code:labpc_plus_ai_gain_bits,
ai_range_is_unipolar:labpc_plus_is_unipolar,
ai_scan_up:0,
memory_mapped_io:0,
},
#ifdef CONFIG_COMEDI_PCI
{
name: "pci-1200",
device_id:0x161,
ai_speed:10000,
bustype: pci_bustype,
register_layout:labpc_1200_layout,
has_ao: 1,
ai_range_table:&range_labpc_1200_ai,
ai_range_code:labpc_1200_ai_gain_bits,
ai_range_is_unipolar:labpc_1200_is_unipolar,
ai_scan_up:1,
memory_mapped_io:1,
},
// dummy entry so pci board works when comedi_config is passed driver name
{
.name = DRV_NAME,
.bustype = pci_bustype,
},
#endif
};
/*
* Useful for shorthand access to the particular board structure
*/
#define thisboard ((labpc_board *)dev->board_ptr)
static const int dma_buffer_size = 0xff00; // size in bytes of dma buffer
static const int sample_size = 2; // 2 bytes per sample
#define devpriv ((labpc_private *)dev->private)
static comedi_driver driver_labpc = {
.driver_name = DRV_NAME,
.module = THIS_MODULE,
.attach = labpc_attach,
.detach = labpc_common_detach,
.num_names = sizeof(labpc_boards) / sizeof(labpc_board),
.board_name = &labpc_boards[0].name,
.offset = sizeof(labpc_board),
};
#ifdef CONFIG_COMEDI_PCI
static DEFINE_PCI_DEVICE_TABLE(labpc_pci_table) = {
{PCI_VENDOR_ID_NATINST, 0x161, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0}
};
MODULE_DEVICE_TABLE(pci, labpc_pci_table);
#endif /* CONFIG_COMEDI_PCI */
static inline int labpc_counter_load(comedi_device * dev,
unsigned long base_address, unsigned int counter_number,
unsigned int count, unsigned int mode)
{
if (thisboard->memory_mapped_io)
return i8254_mm_load((void *)base_address, 0, counter_number,
count, mode);
else
return i8254_load(base_address, 0, counter_number, count, mode);
}
int labpc_common_attach(comedi_device * dev, unsigned long iobase,
unsigned int irq, unsigned int dma_chan)
{
comedi_subdevice *s;
int i;
unsigned long dma_flags, isr_flags;
short lsb, msb;
printk("comedi%d: ni_labpc: %s, io 0x%lx", dev->minor, thisboard->name,
iobase);
if (irq) {
printk(", irq %u", irq);
}
if (dma_chan) {
printk(", dma %u", dma_chan);
}
printk("\n");
if (iobase == 0) {
printk("io base address is zero!\n");
return -EINVAL;
}
// request io regions for isa boards
if (thisboard->bustype == isa_bustype) {
/* check if io addresses are available */
if (!request_region(iobase, LABPC_SIZE,
driver_labpc.driver_name)) {
printk("I/O port conflict\n");
return -EIO;
}
}
dev->iobase = iobase;
if (thisboard->memory_mapped_io) {
devpriv->read_byte = labpc_readb;
devpriv->write_byte = labpc_writeb;
} else {
devpriv->read_byte = labpc_inb;
devpriv->write_byte = labpc_outb;
}
// initialize board's command registers
devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
if (thisboard->register_layout == labpc_1200_layout) {
devpriv->write_byte(devpriv->command5_bits,
dev->iobase + COMMAND5_REG);
devpriv->write_byte(devpriv->command6_bits,
dev->iobase + COMMAND6_REG);
}
/* grab our IRQ */
if (irq) {
isr_flags = 0;
if (thisboard->bustype == pci_bustype)
isr_flags |= IRQF_SHARED;
if (comedi_request_irq(irq, labpc_interrupt, isr_flags,
driver_labpc.driver_name, dev)) {
printk("unable to allocate irq %u\n", irq);
return -EINVAL;
}
}
dev->irq = irq;
// grab dma channel
if (dma_chan > 3) {
printk(" invalid dma channel %u\n", dma_chan);
return -EINVAL;
} else if (dma_chan) {
// allocate dma buffer
devpriv->dma_buffer =
kmalloc(dma_buffer_size, GFP_KERNEL | GFP_DMA);
if (devpriv->dma_buffer == NULL) {
printk(" failed to allocate dma buffer\n");
return -ENOMEM;
}
if (request_dma(dma_chan, driver_labpc.driver_name)) {
printk(" failed to allocate dma channel %u\n",
dma_chan);
return -EINVAL;
}
devpriv->dma_chan = dma_chan;
dma_flags = claim_dma_lock();
disable_dma(devpriv->dma_chan);
set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
release_dma_lock(dma_flags);
}
dev->board_name = thisboard->name;
if (alloc_subdevices(dev, 5) < 0)
return -ENOMEM;
/* analog input subdevice */
s = dev->subdevices + 0;
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags =
SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF |
SDF_CMD_READ;
s->n_chan = 8;
s->len_chanlist = 8;
s->maxdata = (1 << 12) - 1; // 12 bit resolution
s->range_table = thisboard->ai_range_table;
s->do_cmd = labpc_ai_cmd;
s->do_cmdtest = labpc_ai_cmdtest;
s->insn_read = labpc_ai_rinsn;
s->cancel = labpc_cancel;
/* analog output */
s = dev->subdevices + 1;
if (thisboard->has_ao) {
/* Could provide command support, except it only has a one sample
* hardware buffer for analog output and no underrun flag. */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
s->n_chan = NUM_AO_CHAN;
s->maxdata = (1 << 12) - 1; // 12 bit resolution
s->range_table = &range_labpc_ao;
s->insn_read = labpc_ao_rinsn;
s->insn_write = labpc_ao_winsn;
/* initialize analog outputs to a known value */
for (i = 0; i < s->n_chan; i++) {
devpriv->ao_value[i] = s->maxdata / 2;
lsb = devpriv->ao_value[i] & 0xff;
msb = (devpriv->ao_value[i] >> 8) & 0xff;
devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(i));
devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(i));
}
} else {
s->type = COMEDI_SUBD_UNUSED;
}
/* 8255 dio */
s = dev->subdevices + 2;
// if board uses io memory we have to give a custom callback function to the 8255 driver
if (thisboard->memory_mapped_io)
subdev_8255_init(dev, s, labpc_dio_mem_callback,
(unsigned long)(dev->iobase + DIO_BASE_REG));
else
subdev_8255_init(dev, s, NULL, dev->iobase + DIO_BASE_REG);
// calibration subdevices for boards that have one
s = dev->subdevices + 3;
if (thisboard->register_layout == labpc_1200_layout) {
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = 16;
s->maxdata = 0xff;
s->insn_read = labpc_calib_read_insn;
s->insn_write = labpc_calib_write_insn;
for (i = 0; i < s->n_chan; i++)
write_caldac(dev, i, s->maxdata / 2);
} else
s->type = COMEDI_SUBD_UNUSED;
/* EEPROM */
s = dev->subdevices + 4;
if (thisboard->register_layout == labpc_1200_layout) {
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = EEPROM_SIZE;
s->maxdata = 0xff;
s->insn_read = labpc_eeprom_read_insn;
s->insn_write = labpc_eeprom_write_insn;
for (i = 0; i < EEPROM_SIZE; i++) {
devpriv->eeprom_data[i] = labpc_eeprom_read(dev, i);
}
#ifdef LABPC_DEBUG
printk(" eeprom:");
for (i = 0; i < EEPROM_SIZE; i++) {
printk(" %i:0x%x ", i, devpriv->eeprom_data[i]);
}
printk("\n");
#endif
} else
s->type = COMEDI_SUBD_UNUSED;
return 0;
}
static int labpc_attach(comedi_device * dev, comedi_devconfig * it)
{
unsigned long iobase = 0;
unsigned int irq = 0;
unsigned int dma_chan = 0;
#ifdef CONFIG_COMEDI_PCI
int retval;
#endif
/* allocate and initialize dev->private */
if (alloc_private(dev, sizeof(labpc_private)) < 0)
return -ENOMEM;
// get base address, irq etc. based on bustype
switch (thisboard->bustype) {
case isa_bustype:
iobase = it->options[0];
irq = it->options[1];
dma_chan = it->options[2];
break;
case pci_bustype:
#ifdef CONFIG_COMEDI_PCI
retval = labpc_find_device(dev, it->options[0], it->options[1]);
if (retval < 0) {
return retval;
}
retval = mite_setup(devpriv->mite);
if (retval < 0)
return retval;
iobase = (unsigned long)devpriv->mite->daq_io_addr;
irq = mite_irq(devpriv->mite);
#else
printk(" this driver has not been built with PCI support.\n");
return -EINVAL;
#endif
break;
case pcmcia_bustype:
printk(" this driver does not support pcmcia cards, use ni_labpc_cs.o\n");
return -EINVAL;
break;
default:
printk("bug! couldn't determine board type\n");
return -EINVAL;
break;
}
return labpc_common_attach(dev, iobase, irq, dma_chan);
}
// adapted from ni_pcimio for finding mite based boards (pc-1200)
#ifdef CONFIG_COMEDI_PCI
static int labpc_find_device(comedi_device *dev, int bus, int slot)
{
struct mite_struct *mite;
int i;
for (mite = mite_devices; mite; mite = mite->next) {
if (mite->used)
continue;
// if bus/slot are specified then make sure we have the right bus/slot
if (bus || slot) {
if (bus != mite->pcidev->bus->number
|| slot != PCI_SLOT(mite->pcidev->devfn))
continue;
}
for (i = 0; i < driver_labpc.num_names; i++) {
if (labpc_boards[i].bustype != pci_bustype)
continue;
if (mite_device_id(mite) == labpc_boards[i].device_id) {
devpriv->mite = mite;
// fixup board pointer, in case we were using the dummy "ni_labpc" entry
dev->board_ptr = &labpc_boards[i];
return 0;
}
}
}
printk("no device found\n");
mite_list_devices();
return -EIO;
}
#endif
int labpc_common_detach(comedi_device * dev)
{
printk("comedi%d: ni_labpc: detach\n", dev->minor);
if (dev->subdevices)
subdev_8255_cleanup(dev, dev->subdevices + 2);
/* only free stuff if it has been allocated by _attach */
if (devpriv->dma_buffer)
kfree(devpriv->dma_buffer);
if (devpriv->dma_chan)
free_dma(devpriv->dma_chan);
if (dev->irq)
comedi_free_irq(dev->irq, dev);
if (thisboard->bustype == isa_bustype && dev->iobase)
release_region(dev->iobase, LABPC_SIZE);
#ifdef CONFIG_COMEDI_PCI
if (devpriv->mite)
mite_unsetup(devpriv->mite);
#endif
return 0;
};
static void labpc_clear_adc_fifo(const comedi_device * dev)
{
devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
}
static int labpc_cancel(comedi_device * dev, comedi_subdevice * s)
{
unsigned long flags;
comedi_spin_lock_irqsave(&dev->spinlock, flags);
devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
devpriv->command3_bits = 0;
devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
return 0;
}
static enum scan_mode labpc_ai_scan_mode(const comedi_cmd * cmd)
{
if (cmd->chanlist_len == 1)
return MODE_SINGLE_CHAN;
/* chanlist may be NULL during cmdtest. */
if (cmd->chanlist == NULL)
return MODE_MULT_CHAN_UP;
if (CR_CHAN(cmd->chanlist[0]) == CR_CHAN(cmd->chanlist[1]))
return MODE_SINGLE_CHAN_INTERVAL;
if (CR_CHAN(cmd->chanlist[0]) < CR_CHAN(cmd->chanlist[1]))
return MODE_MULT_CHAN_UP;
if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
return MODE_MULT_CHAN_DOWN;
rt_printk("ni_labpc: bug! this should never happen\n");
return 0;
}
static int labpc_ai_chanlist_invalid(const comedi_device * dev,
const comedi_cmd * cmd)
{
int mode, channel, range, aref, i;
if (cmd->chanlist == NULL)
return 0;
mode = labpc_ai_scan_mode(cmd);
if (mode == MODE_SINGLE_CHAN)
return 0;
if (mode == MODE_SINGLE_CHAN_INTERVAL) {
if (cmd->chanlist_len > 0xff) {
comedi_error(dev,
"ni_labpc: chanlist too long for single channel interval mode\n");
return 1;
}
}
channel = CR_CHAN(cmd->chanlist[0]);
range = CR_RANGE(cmd->chanlist[0]);
aref = CR_AREF(cmd->chanlist[0]);
for (i = 0; i < cmd->chanlist_len; i++) {
switch (mode) {
case MODE_SINGLE_CHAN_INTERVAL:
if (CR_CHAN(cmd->chanlist[i]) != channel) {
comedi_error(dev,
"channel scanning order specified in chanlist is not supported by hardware.\n");
return 1;
}
break;
case MODE_MULT_CHAN_UP:
if (CR_CHAN(cmd->chanlist[i]) != i) {
comedi_error(dev,
"channel scanning order specified in chanlist is not supported by hardware.\n");
return 1;
}
break;
case MODE_MULT_CHAN_DOWN:
if (CR_CHAN(cmd->chanlist[i]) !=
cmd->chanlist_len - i - 1) {
comedi_error(dev,
"channel scanning order specified in chanlist is not supported by hardware.\n");
return 1;
}
break;
default:
rt_printk("ni_labpc: bug! in chanlist check\n");
return 1;
break;
}
if (CR_RANGE(cmd->chanlist[i]) != range) {
comedi_error(dev,
"entries in chanlist must all have the same range\n");
return 1;
}
if (CR_AREF(cmd->chanlist[i]) != aref) {
comedi_error(dev,
"entries in chanlist must all have the same reference\n");
return 1;
}
}
return 0;
}
static int labpc_use_continuous_mode(const comedi_cmd * cmd)
{
if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN)
return 1;
if (cmd->scan_begin_src == TRIG_FOLLOW)
return 1;
return 0;
}
static unsigned int labpc_ai_convert_period(const comedi_cmd * cmd)
{
if (cmd->convert_src != TRIG_TIMER)
return 0;
if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
cmd->scan_begin_src == TRIG_TIMER)
return cmd->scan_begin_arg;
return cmd->convert_arg;
}
static void labpc_set_ai_convert_period(comedi_cmd * cmd, unsigned int ns)
{
if (cmd->convert_src != TRIG_TIMER)
return;
if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
cmd->scan_begin_src == TRIG_TIMER) {
cmd->scan_begin_arg = ns;
if (cmd->convert_arg > cmd->scan_begin_arg)
cmd->convert_arg = cmd->scan_begin_arg;
} else
cmd->convert_arg = ns;
}
static unsigned int labpc_ai_scan_period(const comedi_cmd * cmd)
{
if (cmd->scan_begin_src != TRIG_TIMER)
return 0;
if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
cmd->convert_src == TRIG_TIMER)
return 0;
return cmd->scan_begin_arg;
}
static void labpc_set_ai_scan_period(comedi_cmd * cmd, unsigned int ns)
{
if (cmd->scan_begin_src != TRIG_TIMER)
return;
if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
cmd->convert_src == TRIG_TIMER)
return;
cmd->scan_begin_arg = ns;
}
static int labpc_ai_cmdtest(comedi_device * dev, comedi_subdevice * s,
comedi_cmd * cmd)
{
int err = 0;
int tmp, tmp2;
int stop_mask;
/* step 1: make sure trigger sources are trivially valid */
tmp = cmd->start_src;
cmd->start_src &= TRIG_NOW | TRIG_EXT;
if (!cmd->start_src || tmp != cmd->start_src)
err++;
tmp = cmd->scan_begin_src;
cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
err++;
tmp = cmd->convert_src;
cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
if (!cmd->convert_src || tmp != cmd->convert_src)
err++;
tmp = cmd->scan_end_src;
cmd->scan_end_src &= TRIG_COUNT;
if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
err++;
tmp = cmd->stop_src;
stop_mask = TRIG_COUNT | TRIG_NONE;
if (thisboard->register_layout == labpc_1200_layout)
stop_mask |= TRIG_EXT;
cmd->stop_src &= stop_mask;
if (!cmd->stop_src || tmp != cmd->stop_src)
err++;
if (err)
return 1;
/* step 2: make sure trigger sources are unique and mutually compatible */
if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
err++;
if (cmd->scan_begin_src != TRIG_TIMER &&
cmd->scan_begin_src != TRIG_FOLLOW &&
cmd->scan_begin_src != TRIG_EXT)
err++;
if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
err++;
if (cmd->stop_src != TRIG_COUNT &&
cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
err++;
// can't have external stop and start triggers at once
if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
err++;
if (err)
return 2;
/* step 3: make sure arguments are trivially compatible */
if (cmd->start_arg == TRIG_NOW && cmd->start_arg != 0) {
cmd->start_arg = 0;
err++;
}
if (!cmd->chanlist_len) {
err++;
}
if (cmd->scan_end_arg != cmd->chanlist_len) {
cmd->scan_end_arg = cmd->chanlist_len;
err++;
}
if (cmd->convert_src == TRIG_TIMER) {
if (cmd->convert_arg < thisboard->ai_speed) {
cmd->convert_arg = thisboard->ai_speed;
err++;
}
}
// make sure scan timing is not too fast
if (cmd->scan_begin_src == TRIG_TIMER) {
if (cmd->convert_src == TRIG_TIMER &&
cmd->scan_begin_arg <
cmd->convert_arg * cmd->chanlist_len) {
cmd->scan_begin_arg =
cmd->convert_arg * cmd->chanlist_len;
err++;
}
if (cmd->scan_begin_arg <
thisboard->ai_speed * cmd->chanlist_len) {
cmd->scan_begin_arg =
thisboard->ai_speed * cmd->chanlist_len;
err++;
}
}
// stop source
switch (cmd->stop_src) {
case TRIG_COUNT:
if (!cmd->stop_arg) {
cmd->stop_arg = 1;
err++;
}
break;
case TRIG_NONE:
if (cmd->stop_arg != 0) {
cmd->stop_arg = 0;
err++;
}
break;
// TRIG_EXT doesn't care since it doesn't trigger off a numbered channel
default:
break;
}
if (err)
return 3;
/* step 4: fix up any arguments */
tmp = cmd->convert_arg;
tmp2 = cmd->scan_begin_arg;
labpc_adc_timing(dev, cmd);
if (tmp != cmd->convert_arg || tmp2 != cmd->scan_begin_arg)
err++;
if (err)
return 4;
if (labpc_ai_chanlist_invalid(dev, cmd))
return 5;
return 0;
}
static int labpc_ai_cmd(comedi_device * dev, comedi_subdevice * s)
{
int channel, range, aref;
unsigned long irq_flags;
int ret;
comedi_async *async = s->async;
comedi_cmd *cmd = &async->cmd;
enum transfer_type xfer;
unsigned long flags;
if (!dev->irq) {
comedi_error(dev, "no irq assigned, cannot perform command");
return -1;
}
range = CR_RANGE(cmd->chanlist[0]);
aref = CR_AREF(cmd->chanlist[0]);
// make sure board is disabled before setting up aquisition
comedi_spin_lock_irqsave(&dev->spinlock, flags);
devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
devpriv->command3_bits = 0;
devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
// initialize software conversion count
if (cmd->stop_src == TRIG_COUNT) {
devpriv->count = cmd->stop_arg * cmd->chanlist_len;
}
// setup hardware conversion counter
if (cmd->stop_src == TRIG_EXT) {
// load counter a1 with count of 3 (pc+ manual says this is minimum allowed) using mode 0
ret = labpc_counter_load(dev, dev->iobase + COUNTER_A_BASE_REG,
1, 3, 0);
if (ret < 0) {
comedi_error(dev, "error loading counter a1");
return -1;
}
} else // otherwise, just put a1 in mode 0 with no count to set its output low
devpriv->write_byte(INIT_A1_BITS,
dev->iobase + COUNTER_A_CONTROL_REG);
// figure out what method we will use to transfer data
if (devpriv->dma_chan && // need a dma channel allocated
// dma unsafe at RT priority, and too much setup time for TRIG_WAKE_EOS for
(cmd->flags & (TRIG_WAKE_EOS | TRIG_RT)) == 0 &&
// only available on the isa boards
thisboard->bustype == isa_bustype) {
xfer = isa_dma_transfer;
} else if (thisboard->register_layout == labpc_1200_layout && // pc-plus has no fifo-half full interrupt
// wake-end-of-scan should interrupt on fifo not empty
(cmd->flags & TRIG_WAKE_EOS) == 0 &&
// make sure we are taking more than just a few points
(cmd->stop_src != TRIG_COUNT || devpriv->count > 256)) {
xfer = fifo_half_full_transfer;
} else
xfer = fifo_not_empty_transfer;
devpriv->current_transfer = xfer;
// setup command6 register for 1200 boards
if (thisboard->register_layout == labpc_1200_layout) {
// reference inputs to ground or common?
if (aref != AREF_GROUND)
devpriv->command6_bits |= ADC_COMMON_BIT;
else
devpriv->command6_bits &= ~ADC_COMMON_BIT;
// bipolar or unipolar range?
if (thisboard->ai_range_is_unipolar[range])
devpriv->command6_bits |= ADC_UNIP_BIT;
else
devpriv->command6_bits &= ~ADC_UNIP_BIT;
// interrupt on fifo half full?
if (xfer == fifo_half_full_transfer)
devpriv->command6_bits |= ADC_FHF_INTR_EN_BIT;
else
devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
// enable interrupt on counter a1 terminal count?
if (cmd->stop_src == TRIG_EXT)
devpriv->command6_bits |= A1_INTR_EN_BIT;
else
devpriv->command6_bits &= ~A1_INTR_EN_BIT;
// are we scanning up or down through channels?
if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
devpriv->command6_bits |= ADC_SCAN_UP_BIT;
else
devpriv->command6_bits &= ~ADC_SCAN_UP_BIT;
// write to register
devpriv->write_byte(devpriv->command6_bits,
dev->iobase + COMMAND6_REG);
}
/* setup channel list, etc (command1 register) */
devpriv->command1_bits = 0;
if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
else
channel = CR_CHAN(cmd->chanlist[0]);
// munge channel bits for differential / scan disabled mode
if (labpc_ai_scan_mode(cmd) != MODE_SINGLE_CHAN && aref == AREF_DIFF)
channel *= 2;
devpriv->command1_bits |= ADC_CHAN_BITS(channel);
devpriv->command1_bits |= thisboard->ai_range_code[range];
devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
// manual says to set scan enable bit on second pass
if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP ||
labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_DOWN) {
devpriv->command1_bits |= ADC_SCAN_EN_BIT;
/* need a brief delay before enabling scan, or scan list will get screwed when you switch
* between scan up to scan down mode - dunno why */
comedi_udelay(1);
devpriv->write_byte(devpriv->command1_bits,
dev->iobase + COMMAND1_REG);
}
// setup any external triggering/pacing (command4 register)
devpriv->command4_bits = 0;
if (cmd->convert_src != TRIG_EXT)
devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
/* XXX should discard first scan when using interval scanning
* since manual says it is not synced with scan clock */
if (labpc_use_continuous_mode(cmd) == 0) {
devpriv->command4_bits |= INTERVAL_SCAN_EN_BIT;
if (cmd->scan_begin_src == TRIG_EXT)
devpriv->command4_bits |= EXT_SCAN_EN_BIT;
}
// single-ended/differential
if (aref == AREF_DIFF)
devpriv->command4_bits |= ADC_DIFF_BIT;
devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
devpriv->write_byte(cmd->chanlist_len,
dev->iobase + INTERVAL_COUNT_REG);
// load count
devpriv->write_byte(INTERVAL_LOAD_BITS,
dev->iobase + INTERVAL_LOAD_REG);
if (cmd->convert_src == TRIG_TIMER || cmd->scan_begin_src == TRIG_TIMER) {
// set up pacing
labpc_adc_timing(dev, cmd);
// load counter b0 in mode 3
ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
0, devpriv->divisor_b0, 3);
if (ret < 0) {
comedi_error(dev, "error loading counter b0");
return -1;
}
}
// set up conversion pacing
if (labpc_ai_convert_period(cmd)) {
// load counter a0 in mode 2
ret = labpc_counter_load(dev, dev->iobase + COUNTER_A_BASE_REG,
0, devpriv->divisor_a0, 2);
if (ret < 0) {
comedi_error(dev, "error loading counter a0");
return -1;
}
} else
devpriv->write_byte(INIT_A0_BITS,
dev->iobase + COUNTER_A_CONTROL_REG);
// set up scan pacing
if (labpc_ai_scan_period(cmd)) {
// load counter b1 in mode 2
ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
1, devpriv->divisor_b1, 2);
if (ret < 0) {
comedi_error(dev, "error loading counter b1");
return -1;
}
}
labpc_clear_adc_fifo(dev);
// set up dma transfer
if (xfer == isa_dma_transfer) {
irq_flags = claim_dma_lock();
disable_dma(devpriv->dma_chan);
/* clear flip-flop to make sure 2-byte registers for
* count and address get set correctly */
clear_dma_ff(devpriv->dma_chan);
set_dma_addr(devpriv->dma_chan,
virt_to_bus(devpriv->dma_buffer));
// set appropriate size of transfer
devpriv->dma_transfer_size = labpc_suggest_transfer_size(*cmd);
if (cmd->stop_src == TRIG_COUNT &&
devpriv->count * sample_size <
devpriv->dma_transfer_size) {
devpriv->dma_transfer_size =
devpriv->count * sample_size;
}
set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size);
enable_dma(devpriv->dma_chan);
release_dma_lock(irq_flags);
// enable board's dma
devpriv->command3_bits |= DMA_EN_BIT | DMATC_INTR_EN_BIT;
} else
devpriv->command3_bits &= ~DMA_EN_BIT & ~DMATC_INTR_EN_BIT;
// enable error interrupts
devpriv->command3_bits |= ERR_INTR_EN_BIT;
// enable fifo not empty interrupt?
if (xfer == fifo_not_empty_transfer)
devpriv->command3_bits |= ADC_FNE_INTR_EN_BIT;
else
devpriv->command3_bits &= ~ADC_FNE_INTR_EN_BIT;
devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
// startup aquisition
// command2 reg
// use 2 cascaded counters for pacing
comedi_spin_lock_irqsave(&dev->spinlock, flags);
devpriv->command2_bits |= CASCADE_BIT;
switch (cmd->start_src) {
case TRIG_EXT:
devpriv->command2_bits |= HWTRIG_BIT;
devpriv->command2_bits &= ~PRETRIG_BIT & ~SWTRIG_BIT;
break;
case TRIG_NOW:
devpriv->command2_bits |= SWTRIG_BIT;
devpriv->command2_bits &= ~PRETRIG_BIT & ~HWTRIG_BIT;
break;
default:
comedi_error(dev, "bug with start_src");
return -1;
break;
}
switch (cmd->stop_src) {
case TRIG_EXT:
devpriv->command2_bits |= HWTRIG_BIT | PRETRIG_BIT;
break;
case TRIG_COUNT:
case TRIG_NONE:
break;
default:
comedi_error(dev, "bug with stop_src");
return -1;
}
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
return 0;
}
/* interrupt service routine */
static irqreturn_t labpc_interrupt(int irq, void *d PT_REGS_ARG)
{
comedi_device *dev = d;
comedi_subdevice *s = dev->read_subdev;
comedi_async *async;
comedi_cmd *cmd;
if (dev->attached == 0) {
comedi_error(dev, "premature interrupt");
return IRQ_HANDLED;
}
async = s->async;
cmd = &async->cmd;
async->events = 0;
// read board status
devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
if (thisboard->register_layout == labpc_1200_layout)
devpriv->status2_bits =
devpriv->read_byte(dev->iobase + STATUS2_REG);
if ((devpriv->status1_bits & (DMATC_BIT | TIMER_BIT | OVERFLOW_BIT |
OVERRUN_BIT | DATA_AVAIL_BIT)) == 0
&& (devpriv->status2_bits & A1_TC_BIT) == 0
&& (devpriv->status2_bits & FNHF_BIT)) {
return IRQ_NONE;
}
if (devpriv->status1_bits & OVERRUN_BIT) {
// clear error interrupt
devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
comedi_event(dev, s);
comedi_error(dev, "overrun");
return IRQ_HANDLED;
}
if (devpriv->current_transfer == isa_dma_transfer) {
// if a dma terminal count of external stop trigger has occurred
if (devpriv->status1_bits & DMATC_BIT ||
(thisboard->register_layout == labpc_1200_layout
&& devpriv->status2_bits & A1_TC_BIT)) {
handle_isa_dma(dev);
}
} else
labpc_drain_fifo(dev);
if (devpriv->status1_bits & TIMER_BIT) {
comedi_error(dev, "handled timer interrupt?");
// clear it
devpriv->write_byte(0x1, dev->iobase + TIMER_CLEAR_REG);
}
if (devpriv->status1_bits & OVERFLOW_BIT) {
// clear error interrupt
devpriv->write_byte(0x1, dev->iobase + ADC_CLEAR_REG);
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
comedi_event(dev, s);
comedi_error(dev, "overflow");
return IRQ_HANDLED;
}
// handle external stop trigger
if (cmd->stop_src == TRIG_EXT) {
if (devpriv->status2_bits & A1_TC_BIT) {
labpc_drain_dregs(dev);
labpc_cancel(dev, s);
async->events |= COMEDI_CB_EOA;
}
}
/* TRIG_COUNT end of acquisition */
if (cmd->stop_src == TRIG_COUNT) {
if (devpriv->count == 0) {
labpc_cancel(dev, s);
async->events |= COMEDI_CB_EOA;
}
}
comedi_event(dev, s);
return IRQ_HANDLED;
}
// read all available samples from ai fifo
static int labpc_drain_fifo(comedi_device * dev)
{
unsigned int lsb, msb;
sampl_t data;
comedi_async *async = dev->read_subdev->async;
const int timeout = 10000;
unsigned int i;
devpriv->status1_bits = devpriv->read_byte(dev->iobase + STATUS1_REG);
for (i = 0; (devpriv->status1_bits & DATA_AVAIL_BIT) && i < timeout;
i++) {
// quit if we have all the data we want
if (async->cmd.stop_src == TRIG_COUNT) {
if (devpriv->count == 0)
break;
devpriv->count--;
}
lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
data = (msb << 8) | lsb;
cfc_write_to_buffer(dev->read_subdev, data);
devpriv->status1_bits =
devpriv->read_byte(dev->iobase + STATUS1_REG);
}
if (i == timeout) {
comedi_error(dev, "ai timeout, fifo never empties");
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
return -1;
}
return 0;
}
static void labpc_drain_dma(comedi_device * dev)
{
comedi_subdevice *s = dev->read_subdev;
comedi_async *async = s->async;
int status;
unsigned long flags;
unsigned int max_points, num_points, residue, leftover;
int i;
status = devpriv->status1_bits;
flags = claim_dma_lock();
disable_dma(devpriv->dma_chan);
/* clear flip-flop to make sure 2-byte registers for
* count and address get set correctly */
clear_dma_ff(devpriv->dma_chan);
// figure out how many points to read
max_points = devpriv->dma_transfer_size / sample_size;
/* residue is the number of points left to be done on the dma
* transfer. It should always be zero at this point unless
* the stop_src is set to external triggering.
*/
residue = get_dma_residue(devpriv->dma_chan) / sample_size;
num_points = max_points - residue;
if (devpriv->count < num_points && async->cmd.stop_src == TRIG_COUNT)
num_points = devpriv->count;
// figure out how many points will be stored next time
leftover = 0;
if (async->cmd.stop_src != TRIG_COUNT) {
leftover = devpriv->dma_transfer_size / sample_size;
} else if (devpriv->count > num_points) {
leftover = devpriv->count - num_points;
if (leftover > max_points)
leftover = max_points;
}
/* write data to comedi buffer */
for (i = 0; i < num_points; i++) {
cfc_write_to_buffer(s, devpriv->dma_buffer[i]);
}
if (async->cmd.stop_src == TRIG_COUNT)
devpriv->count -= num_points;
// set address and count for next transfer
set_dma_addr(devpriv->dma_chan, virt_to_bus(devpriv->dma_buffer));
set_dma_count(devpriv->dma_chan, leftover * sample_size);
release_dma_lock(flags);
async->events |= COMEDI_CB_BLOCK;
}
static void handle_isa_dma(comedi_device * dev)
{
labpc_drain_dma(dev);
enable_dma(devpriv->dma_chan);
// clear dma tc interrupt
devpriv->write_byte(0x1, dev->iobase + DMATC_CLEAR_REG);
}
/* makes sure all data aquired by board is transfered to comedi (used
* when aquisition is terminated by stop_src == TRIG_EXT). */
static void labpc_drain_dregs(comedi_device * dev)
{
if (devpriv->current_transfer == isa_dma_transfer)
labpc_drain_dma(dev);
labpc_drain_fifo(dev);
}
static int labpc_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int i, n;
int chan, range;
int lsb, msb;
int timeout = 1000;
unsigned long flags;
// disable timed conversions
comedi_spin_lock_irqsave(&dev->spinlock, flags);
devpriv->command2_bits &= ~SWTRIG_BIT & ~HWTRIG_BIT & ~PRETRIG_BIT;
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
// disable interrupt generation and dma
devpriv->command3_bits = 0;
devpriv->write_byte(devpriv->command3_bits, dev->iobase + COMMAND3_REG);
/* set gain and channel */
devpriv->command1_bits = 0;
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
devpriv->command1_bits |= thisboard->ai_range_code[range];
// munge channel bits for differential/scan disabled mode
if (CR_AREF(insn->chanspec) == AREF_DIFF)
chan *= 2;
devpriv->command1_bits |= ADC_CHAN_BITS(chan);
devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
// setup command6 register for 1200 boards
if (thisboard->register_layout == labpc_1200_layout) {
// reference inputs to ground or common?
if (CR_AREF(insn->chanspec) != AREF_GROUND)
devpriv->command6_bits |= ADC_COMMON_BIT;
else
devpriv->command6_bits &= ~ADC_COMMON_BIT;
// bipolar or unipolar range?
if (thisboard->ai_range_is_unipolar[range])
devpriv->command6_bits |= ADC_UNIP_BIT;
else
devpriv->command6_bits &= ~ADC_UNIP_BIT;
// don't interrupt on fifo half full
devpriv->command6_bits &= ~ADC_FHF_INTR_EN_BIT;
// don't enable interrupt on counter a1 terminal count?
devpriv->command6_bits &= ~A1_INTR_EN_BIT;
// write to register
devpriv->write_byte(devpriv->command6_bits,
dev->iobase + COMMAND6_REG);
}
// setup command4 register
devpriv->command4_bits = 0;
devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
// single-ended/differential
if (CR_AREF(insn->chanspec) == AREF_DIFF)
devpriv->command4_bits |= ADC_DIFF_BIT;
devpriv->write_byte(devpriv->command4_bits, dev->iobase + COMMAND4_REG);
// initialize pacer counter output to make sure it doesn't cause any problems
devpriv->write_byte(INIT_A0_BITS, dev->iobase + COUNTER_A_CONTROL_REG);
labpc_clear_adc_fifo(dev);
for (n = 0; n < insn->n; n++) {
/* trigger conversion */
devpriv->write_byte(0x1, dev->iobase + ADC_CONVERT_REG);
for (i = 0; i < timeout; i++) {
if (devpriv->read_byte(dev->iobase +
STATUS1_REG) & DATA_AVAIL_BIT)
break;
comedi_udelay(1);
}
if (i == timeout) {
comedi_error(dev, "timeout");
return -ETIME;
}
lsb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
msb = devpriv->read_byte(dev->iobase + ADC_FIFO_REG);
data[n] = (msb << 8) | lsb;
}
return n;
}
// analog output insn
static int labpc_ao_winsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int channel, range;
unsigned long flags;
int lsb, msb;
channel = CR_CHAN(insn->chanspec);
// turn off pacing of analog output channel
/* note: hardware bug in daqcard-1200 means pacing cannot
* be independently enabled/disabled for its the two channels */
comedi_spin_lock_irqsave(&dev->spinlock, flags);
devpriv->command2_bits &= ~DAC_PACED_BIT(channel);
devpriv->write_byte(devpriv->command2_bits, dev->iobase + COMMAND2_REG);
comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
// set range
if (thisboard->register_layout == labpc_1200_layout) {
range = CR_RANGE(insn->chanspec);
if (range & AO_RANGE_IS_UNIPOLAR)
devpriv->command6_bits |= DAC_UNIP_BIT(channel);
else
devpriv->command6_bits &= ~DAC_UNIP_BIT(channel);
// write to register
devpriv->write_byte(devpriv->command6_bits,
dev->iobase + COMMAND6_REG);
}
// send data
lsb = data[0] & 0xff;
msb = (data[0] >> 8) & 0xff;
devpriv->write_byte(lsb, dev->iobase + DAC_LSB_REG(channel));
devpriv->write_byte(msb, dev->iobase + DAC_MSB_REG(channel));
// remember value for readback
devpriv->ao_value[channel] = data[0];
return 1;
}
// analog output readback insn
static int labpc_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
return 1;
}
static int labpc_calib_read_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
data[0] = devpriv->caldac[CR_CHAN(insn->chanspec)];
return 1;
}
static int labpc_calib_write_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int channel = CR_CHAN(insn->chanspec);
write_caldac(dev, channel, data[0]);
return 1;
}
static int labpc_eeprom_read_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
data[0] = devpriv->eeprom_data[CR_CHAN(insn->chanspec)];
return 1;
}
static int labpc_eeprom_write_insn(comedi_device * dev, comedi_subdevice * s,
comedi_insn * insn, lsampl_t * data)
{
int channel = CR_CHAN(insn->chanspec);
int ret;
// only allow writes to user area of eeprom
if (channel < 16 || channel > 127) {
printk("eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)");
return -EINVAL;
}
ret = labpc_eeprom_write(dev, channel, data[0]);
if (ret < 0)
return ret;
return 1;
}
// utility function that suggests a dma transfer size in bytes
static unsigned int labpc_suggest_transfer_size(comedi_cmd cmd)
{
unsigned int size;
unsigned int freq;
if (cmd.convert_src == TRIG_TIMER)
freq = 1000000000 / cmd.convert_arg;
// return some default value
else
freq = 0xffffffff;
// make buffer fill in no more than 1/3 second
size = (freq / 3) * sample_size;
// set a minimum and maximum size allowed
if (size > dma_buffer_size)
size = dma_buffer_size - dma_buffer_size % sample_size;
else if (size < sample_size)
size = sample_size;
return size;
}
// figures out what counter values to use based on command
static void labpc_adc_timing(comedi_device * dev, comedi_cmd * cmd)
{
const int max_counter_value = 0x10000; // max value for 16 bit counter in mode 2
const int min_counter_value = 2; // min value for 16 bit counter in mode 2
unsigned int base_period;
// if both convert and scan triggers are TRIG_TIMER, then they both rely on counter b0
if (labpc_ai_convert_period(cmd) && labpc_ai_scan_period(cmd)) {
// pick the lowest b0 divisor value we can (for maximum input clock speed on convert and scan counters)
devpriv->divisor_b0 = (labpc_ai_scan_period(cmd) - 1) /
(LABPC_TIMER_BASE * max_counter_value) + 1;
if (devpriv->divisor_b0 < min_counter_value)
devpriv->divisor_b0 = min_counter_value;
if (devpriv->divisor_b0 > max_counter_value)
devpriv->divisor_b0 = max_counter_value;
base_period = LABPC_TIMER_BASE * devpriv->divisor_b0;
// set a0 for conversion frequency and b1 for scan frequency
switch (cmd->flags & TRIG_ROUND_MASK) {
default:
case TRIG_ROUND_NEAREST:
devpriv->divisor_a0 =
(labpc_ai_convert_period(cmd) +
(base_period / 2)) / base_period;
devpriv->divisor_b1 =
(labpc_ai_scan_period(cmd) +
(base_period / 2)) / base_period;
break;
case TRIG_ROUND_UP:
devpriv->divisor_a0 =
(labpc_ai_convert_period(cmd) + (base_period -
1)) / base_period;
devpriv->divisor_b1 =
(labpc_ai_scan_period(cmd) + (base_period -
1)) / base_period;
break;
case TRIG_ROUND_DOWN:
devpriv->divisor_a0 =
labpc_ai_convert_period(cmd) / base_period;
devpriv->divisor_b1 =
labpc_ai_scan_period(cmd) / base_period;
break;
}
// make sure a0 and b1 values are acceptable
if (devpriv->divisor_a0 < min_counter_value)
devpriv->divisor_a0 = min_counter_value;
if (devpriv->divisor_a0 > max_counter_value)
devpriv->divisor_a0 = max_counter_value;
if (devpriv->divisor_b1 < min_counter_value)
devpriv->divisor_b1 = min_counter_value;
if (devpriv->divisor_b1 > max_counter_value)
devpriv->divisor_b1 = max_counter_value;
// write corrected timings to command
labpc_set_ai_convert_period(cmd,
base_period * devpriv->divisor_a0);
labpc_set_ai_scan_period(cmd,
base_period * devpriv->divisor_b1);
// if only one TRIG_TIMER is used, we can employ the generic cascaded timing functions
} else if (labpc_ai_scan_period(cmd)) {
unsigned int scan_period;
scan_period = labpc_ai_scan_period(cmd);
/* calculate cascaded counter values that give desired scan timing */
i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
&(devpriv->divisor_b1), &(devpriv->divisor_b0),
&scan_period, cmd->flags & TRIG_ROUND_MASK);
labpc_set_ai_scan_period(cmd, scan_period);
} else if (labpc_ai_convert_period(cmd)) {
unsigned int convert_period;
convert_period = labpc_ai_convert_period(cmd);
/* calculate cascaded counter values that give desired conversion timing */
i8253_cascade_ns_to_timer_2div(LABPC_TIMER_BASE,
&(devpriv->divisor_a0), &(devpriv->divisor_b0),
&convert_period, cmd->flags & TRIG_ROUND_MASK);
labpc_set_ai_convert_period(cmd, convert_period);
}
}
static int labpc_dio_mem_callback(int dir, int port, int data,
unsigned long iobase)
{
if (dir) {
writeb(data, (void *)(iobase + port));
return 0;
} else {
return readb((void *)(iobase + port));
}
}
// lowlevel write to eeprom/dac
static void labpc_serial_out(comedi_device * dev, unsigned int value,
unsigned int value_width)
{
int i;
for (i = 1; i <= value_width; i++) {
// clear serial clock
devpriv->command5_bits &= ~SCLOCK_BIT;
// send bits most significant bit first
if (value & (1 << (value_width - i)))
devpriv->command5_bits |= SDATA_BIT;
else
devpriv->command5_bits &= ~SDATA_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits,
dev->iobase + COMMAND5_REG);
// set clock to load bit
devpriv->command5_bits |= SCLOCK_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits,
dev->iobase + COMMAND5_REG);
}
}
// lowlevel read from eeprom
static unsigned int labpc_serial_in(comedi_device * dev)
{
unsigned int value = 0;
int i;
const int value_width = 8; // number of bits wide values are
for (i = 1; i <= value_width; i++) {
// set serial clock
devpriv->command5_bits |= SCLOCK_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits,
dev->iobase + COMMAND5_REG);
// clear clock bit
devpriv->command5_bits &= ~SCLOCK_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits,
dev->iobase + COMMAND5_REG);
// read bits most significant bit first
comedi_udelay(1);
devpriv->status2_bits =
devpriv->read_byte(dev->iobase + STATUS2_REG);
if (devpriv->status2_bits & EEPROM_OUT_BIT) {
value |= 1 << (value_width - i);
}
}
return value;
}
static unsigned int labpc_eeprom_read(comedi_device * dev, unsigned int address)
{
unsigned int value;
const int read_instruction = 0x3; // bits to tell eeprom to expect a read
const int write_length = 8; // 8 bit write lengths to eeprom
// enable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// send read instruction
labpc_serial_out(dev, read_instruction, write_length);
// send 8 bit address to read from
labpc_serial_out(dev, address, write_length);
// read result
value = labpc_serial_in(dev);
// disable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
return value;
}
static unsigned int labpc_eeprom_write(comedi_device * dev,
unsigned int address, unsigned int value)
{
const int write_enable_instruction = 0x6;
const int write_instruction = 0x2;
const int write_length = 8; // 8 bit write lengths to eeprom
const int write_in_progress_bit = 0x1;
const int timeout = 10000;
int i;
// make sure there isn't already a write in progress
for (i = 0; i < timeout; i++) {
if ((labpc_eeprom_read_status(dev) & write_in_progress_bit) ==
0)
break;
}
if (i == timeout) {
comedi_error(dev, "eeprom write timed out");
return -ETIME;
}
// update software copy of eeprom
devpriv->eeprom_data[address] = value;
// enable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// send write_enable instruction
labpc_serial_out(dev, write_enable_instruction, write_length);
devpriv->command5_bits &= ~EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// send write instruction
devpriv->command5_bits |= EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
labpc_serial_out(dev, write_instruction, write_length);
// send 8 bit address to write to
labpc_serial_out(dev, address, write_length);
// write value
labpc_serial_out(dev, value, write_length);
devpriv->command5_bits &= ~EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// disable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
return 0;
}
static unsigned int labpc_eeprom_read_status(comedi_device * dev)
{
unsigned int value;
const int read_status_instruction = 0x5;
const int write_length = 8; // 8 bit write lengths to eeprom
// enable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
devpriv->command5_bits |= EEPROM_EN_BIT | EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// send read status instruction
labpc_serial_out(dev, read_status_instruction, write_length);
// read result
value = labpc_serial_in(dev);
// disable read/write to eeprom
devpriv->command5_bits &= ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
return value;
}
// writes to 8 bit calibration dacs
static void write_caldac(comedi_device * dev, unsigned int channel,
unsigned int value)
{
if (value == devpriv->caldac[channel])
return;
devpriv->caldac[channel] = value;
// clear caldac load bit and make sure we don't write to eeprom
devpriv->command5_bits &=
~CALDAC_LOAD_BIT & ~EEPROM_EN_BIT & ~EEPROM_WRITE_UNPROTECT_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
// write 4 bit channel
labpc_serial_out(dev, channel, 4);
// write 8 bit caldac value
labpc_serial_out(dev, value, 8);
// set and clear caldac bit to load caldac value
devpriv->command5_bits |= CALDAC_LOAD_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
devpriv->command5_bits &= ~CALDAC_LOAD_BIT;
comedi_udelay(1);
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
}
#ifdef CONFIG_COMEDI_PCI
COMEDI_PCI_INITCLEANUP(driver_labpc, labpc_pci_table);
#else
COMEDI_INITCLEANUP(driver_labpc);
#endif
EXPORT_SYMBOL_GPL(labpc_common_attach);
EXPORT_SYMBOL_GPL(labpc_common_detach);
EXPORT_SYMBOL_GPL(range_labpc_1200_ai);
EXPORT_SYMBOL_GPL(labpc_1200_ai_gain_bits);
EXPORT_SYMBOL_GPL(labpc_1200_is_unipolar);
/*
ni_labpc.h
Header for ni_labpc.c and ni_labpc_cs.c
Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _NI_LABPC_H
#define _NI_LABPC_H
#define EEPROM_SIZE 256 // 256 byte eeprom
#define NUM_AO_CHAN 2 // boards have two analog output channels
enum labpc_bustype { isa_bustype, pci_bustype, pcmcia_bustype };
enum labpc_register_layout { labpc_plus_layout, labpc_1200_layout };
enum transfer_type { fifo_not_empty_transfer, fifo_half_full_transfer,
isa_dma_transfer };
typedef struct labpc_board_struct {
const char *name;
int device_id; // device id for pci and pcmcia boards
int ai_speed; // maximum input speed in nanoseconds
enum labpc_bustype bustype; // ISA/PCI/etc.
enum labpc_register_layout register_layout; // 1200 has extra registers compared to pc+
int has_ao; // has analog output true/false
const comedi_lrange *ai_range_table;
const int *ai_range_code;
const int *ai_range_is_unipolar;
unsigned ai_scan_up:1; // board can auto scan up in ai channels, not just down
unsigned memory_mapped_io:1; /* uses memory mapped io instead of ioports */
} labpc_board;
typedef struct {
struct mite_struct *mite; // for mite chip on pci-1200
volatile unsigned long long count; /* number of data points left to be taken */
unsigned int ao_value[NUM_AO_CHAN]; // software copy of analog output values
// software copys of bits written to command registers
volatile unsigned int command1_bits;
volatile unsigned int command2_bits;
volatile unsigned int command3_bits;
volatile unsigned int command4_bits;
volatile unsigned int command5_bits;
volatile unsigned int command6_bits;
// store last read of board status registers
volatile unsigned int status1_bits;
volatile unsigned int status2_bits;
unsigned int divisor_a0; /* value to load into board's counter a0 (conversion pacing) for timed conversions */
unsigned int divisor_b0; /* value to load into board's counter b0 (master) for timed conversions */
unsigned int divisor_b1; /* value to load into board's counter b1 (scan pacing) for timed conversions */
unsigned int dma_chan; // dma channel to use
u16 *dma_buffer; // buffer ai will dma into
unsigned int dma_transfer_size; // transfer size in bytes for current transfer
enum transfer_type current_transfer; // we are using dma/fifo-half-full/etc.
unsigned int eeprom_data[EEPROM_SIZE]; // stores contents of board's eeprom
unsigned int caldac[16]; // stores settings of calibration dacs
// function pointers so we can use inb/outb or readb/writeb as appropriate
unsigned int (*read_byte) (unsigned long address);
void (*write_byte) (unsigned int byte, unsigned long address);
} labpc_private;
int labpc_common_attach(comedi_device * dev, unsigned long iobase,
unsigned int irq, unsigned int dma);
int labpc_common_detach(comedi_device * dev);
extern const int labpc_1200_is_unipolar[];
extern const int labpc_1200_ai_gain_bits[];
extern const comedi_lrange range_labpc_1200_ai;
#endif /* _NI_LABPC_H */
/*
comedi/drivers/ni_labpc_cs.c
Driver for National Instruments daqcard-1200 boards
Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
PCMCIA crap is adapted from dummy_cs.c 1.31 2001/08/24 12:13:13
from the pcmcia package.
The initial developer of the pcmcia dummy_cs.c code is David A. Hinds
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
************************************************************************
*/
/*
Driver: ni_labpc_cs
Description: National Instruments Lab-PC (& compatibles)
Author: Frank Mori Hess <fmhess@users.sourceforge.net>
Devices: [National Instruments] DAQCard-1200 (daqcard-1200)
Status: works
Thanks go to Fredrik Lingvall for much testing and perseverance in
helping to debug daqcard-1200 support.
The 1200 series boards have onboard calibration dacs for correcting
analog input/output offsets and gains. The proper settings for these
caldacs are stored on the board's eeprom. To read the caldac values
from the eeprom and store them into a file that can be then be used by
comedilib, use the comedi_calibrate program.
Configuration options:
none
The daqcard-1200 has quirky chanlist requirements
when scanning multiple channels. Multiple channel scan
sequence must start at highest channel, then decrement down to
channel 0. Chanlists consisting of all one channel
are also legal, and allow you to pace conversions in bursts.
*/
/*
NI manuals:
340988a (daqcard-1200)
*/
#undef LABPC_DEBUG
//#define LABPC_DEBUG // enable debugging messages
#include "../comedidev.h"
#include <linux/delay.h>
#include <linux/version.h>
#include "8253.h"
#include "8255.h"
#include "comedi_fc.h"
#include "ni_labpc.h"
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
static struct pcmcia_device *pcmcia_cur_dev = NULL;
static int labpc_attach(comedi_device * dev, comedi_devconfig * it);
static const labpc_board labpc_cs_boards[] = {
{
name: "daqcard-1200",
device_id:0x103, // 0x10b is manufacturer id, 0x103 is device id
ai_speed:10000,
bustype: pcmcia_bustype,
register_layout:labpc_1200_layout,
has_ao: 1,
ai_range_table:&range_labpc_1200_ai,
ai_range_code:labpc_1200_ai_gain_bits,
ai_range_is_unipolar:labpc_1200_is_unipolar,
ai_scan_up:0,
memory_mapped_io:0,
},
/* duplicate entry, to support using alternate name */
{
name: "ni_labpc_cs",
device_id:0x103,
ai_speed:10000,
bustype: pcmcia_bustype,
register_layout:labpc_1200_layout,
has_ao: 1,
ai_range_table:&range_labpc_1200_ai,
ai_range_code:labpc_1200_ai_gain_bits,
ai_range_is_unipolar:labpc_1200_is_unipolar,
ai_scan_up:0,
memory_mapped_io:0,
},
};
/*
* Useful for shorthand access to the particular board structure
*/
#define thisboard ((const labpc_board *)dev->board_ptr)
static comedi_driver driver_labpc_cs = {
.driver_name = "ni_labpc_cs",
.module = THIS_MODULE,
.attach = &labpc_attach,
.detach = &labpc_common_detach,
.num_names = sizeof(labpc_cs_boards) / sizeof(labpc_board),
.board_name = &labpc_cs_boards[0].name,
.offset = sizeof(labpc_board),
};
static int labpc_attach(comedi_device * dev, comedi_devconfig * it)
{
unsigned long iobase = 0;
unsigned int irq = 0;
struct pcmcia_device *link;
/* allocate and initialize dev->private */
if (alloc_private(dev, sizeof(labpc_private)) < 0)
return -ENOMEM;
// get base address, irq etc. based on bustype
switch (thisboard->bustype) {
case pcmcia_bustype:
link = pcmcia_cur_dev; /* XXX hack */
if (!link)
return -EIO;
iobase = link->io.BasePort1;
irq = link->irq.AssignedIRQ;
break;
default:
printk("bug! couldn't determine board type\n");
return -EINVAL;
break;
}
return labpc_common_attach(dev, iobase, irq, 0);
}
/*
All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
you do not define PCMCIA_DEBUG at all, all the debug code will be
left out. If you compile with PCMCIA_DEBUG=0, the debug code will
be present but disabled -- but it can then be enabled for specific
modules at load time with a 'pc_debug=#' option to insmod.
*/
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
module_param(pc_debug, int, 0644);
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static const char *version =
"ni_labpc.c, based on dummy_cs.c 1.31 2001/08/24 12:13:13";
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
/*
The event() function is this driver's Card Services event handler.
It will be called by Card Services when an appropriate card status
event is received. The config() and release() entry points are
used to configure or release a socket, in response to card
insertion and ejection events. They are invoked from the dummy
event handler.
Kernel version 2.6.16 upwards uses suspend() and resume() functions
instead of an event() function.
*/
static void labpc_config(struct pcmcia_device *link);
static void labpc_release(struct pcmcia_device *link);
static int labpc_cs_suspend(struct pcmcia_device *p_dev);
static int labpc_cs_resume(struct pcmcia_device *p_dev);
/*
The attach() and detach() entry points are used to create and destroy
"instances" of the driver, where each instance represents everything
needed to manage one actual PCMCIA card.
*/
static int labpc_cs_attach(struct pcmcia_device *);
static void labpc_cs_detach(struct pcmcia_device *);
/*
You'll also need to prototype all the functions that will actually
be used to talk to your device. See 'memory_cs' for a good example
of a fully self-sufficient driver; the other drivers rely more or
less on other parts of the kernel.
*/
/*
The dev_info variable is the "key" that is used to match up this
device driver with appropriate cards, through the card configuration
database.
*/
static const dev_info_t dev_info = "daqcard-1200";
typedef struct local_info_t {
struct pcmcia_device *link;
dev_node_t node;
int stop;
struct bus_operations *bus;
} local_info_t;
/*======================================================================
labpc_cs_attach() creates an "instance" of the driver, allocating
local data structures for one device. The device is registered
with Card Services.
The dev_link structure is initialized, but we don't actually
configure the card at this point -- we wait until we receive a
card insertion event.
======================================================================*/
static int labpc_cs_attach(struct pcmcia_device *link)
{
local_info_t *local;
DEBUG(0, "labpc_cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
if (!local)
return -ENOMEM;
local->link = link;
link->priv = local;
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_FORCED_PULSE;
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_PULSE_ID;
link->irq.Handler = NULL;
/*
General socket configuration defaults can go here. In this
client, we assume very little, and rely on the CIS for almost
everything. In most clients, many details (i.e., number, sizes,
and attributes of IO windows) are fixed by the nature of the
device, and can be hard-wired here.
*/
link->conf.Attributes = 0;
link->conf.IntType = INT_MEMORY_AND_IO;
pcmcia_cur_dev = link;
labpc_config(link);
return 0;
} /* labpc_cs_attach */
/*======================================================================
This deletes a driver "instance". The device is de-registered
with Card Services. If it has been released, all local data
structures are freed. Otherwise, the structures will be freed
when the device is released.
======================================================================*/
static void labpc_cs_detach(struct pcmcia_device *link)
{
DEBUG(0, "labpc_cs_detach(0x%p)\n", link);
/*
If the device is currently configured and active, we won't
actually delete it yet. Instead, it is marked so that when
the release() function is called, that will trigger a proper
detach().
*/
if (link->dev_node) {
((local_info_t *) link->priv)->stop = 1;
labpc_release(link);
}
/* This points to the parent local_info_t struct */
if (link->priv)
kfree(link->priv);
} /* labpc_cs_detach */
/*======================================================================
labpc_config() is scheduled to run after a CARD_INSERTION event
is received, to configure the PCMCIA socket, and to make the
device available to the system.
======================================================================*/
static void labpc_config(struct pcmcia_device *link)
{
local_info_t *dev = link->priv;
tuple_t tuple;
cisparse_t parse;
int last_ret;
u_char buf[64];
win_req_t req;
memreq_t map;
cistpl_cftable_entry_t dflt = { 0 };
DEBUG(0, "labpc_config(0x%p)\n", link);
/*
This reads the card's CONFIG tuple to find its configuration
registers.
*/
tuple.DesiredTuple = CISTPL_CONFIG;
tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
cs_error(link, GetFirstTuple, last_ret);
goto cs_failed;
}
if ((last_ret = pcmcia_get_tuple_data(link, &tuple))) {
cs_error(link, GetTupleData, last_ret);
goto cs_failed;
}
if ((last_ret = pcmcia_parse_tuple(&tuple, &parse))) {
cs_error(link, ParseTuple, last_ret);
goto cs_failed;
}
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/*
In this loop, we scan the CIS for configuration table entries,
each of which describes a valid card configuration, including
voltage, IO window, memory window, and interrupt settings.
We make no assumptions about the card to be configured: we use
just the information available in the CIS. In an ideal world,
this would work for any PCMCIA card, but it requires a complete
and accurate CIS. In practice, a driver usually "knows" most of
these things without consulting the CIS, and most client drivers
will only use the CIS to fill in implementation-defined details.
*/
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if ((last_ret = pcmcia_get_first_tuple(link, &tuple))) {
cs_error(link, GetFirstTuple, last_ret);
goto cs_failed;
}
while (1) {
cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
if (pcmcia_get_tuple_data(link, &tuple))
goto next_entry;
if (pcmcia_parse_tuple(&tuple, &parse))
goto next_entry;
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
dflt = *cfg;
if (cfg->index == 0)
goto next_entry;
link->conf.ConfigIndex = cfg->index;
/* Does this card need audio output? */
if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
}
/* Do we need to allocate an interrupt? */
if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
link->conf.Attributes |= CONF_ENABLE_IRQ;
/* IO window settings */
link->io.NumPorts1 = link->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
link->io.BasePort1 = io->win[0].base;
link->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
link->io.Attributes2 = link->io.Attributes1;
link->io.BasePort2 = io->win[1].base;
link->io.NumPorts2 = io->win[1].len;
}
/* This reserves IO space but doesn't actually enable it */
if (pcmcia_request_io(link, &link->io))
goto next_entry;
}
if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
cistpl_mem_t *mem =
(cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
req.Attributes |= WIN_ENABLE;
req.Base = mem->win[0].host_addr;
req.Size = mem->win[0].len;
if (req.Size < 0x1000)
req.Size = 0x1000;
req.AccessSpeed = 0;
link->win = (window_handle_t) link;
if (pcmcia_request_window(&link, &req, &link->win))
goto next_entry;
map.Page = 0;
map.CardOffset = mem->win[0].card_addr;
if (pcmcia_map_mem_page(link->win, &map))
goto next_entry;
}
/* If we got this far, we're cool! */
break;
next_entry:
if ((last_ret = pcmcia_get_next_tuple(link, &tuple))) {
cs_error(link, GetNextTuple, last_ret);
goto cs_failed;
}
}
/*
Allocate an interrupt line. Note that this does not assign a
handler to the interrupt, unless the 'Handler' member of the
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ)
if ((last_ret = pcmcia_request_irq(link, &link->irq))) {
cs_error(link, RequestIRQ, last_ret);
goto cs_failed;
}
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
if ((last_ret = pcmcia_request_configuration(link, &link->conf))) {
cs_error(link, RequestConfiguration, last_ret);
goto cs_failed;
}
/*
At this point, the dev_node_t structure(s) need to be
initialized and arranged in a linked list at link->dev.
*/
sprintf(dev->node.dev_name, "daqcard-1200");
dev->node.major = dev->node.minor = 0;
link->dev_node = &dev->node;
/* Finally, report what we've done */
printk(KERN_INFO "%s: index 0x%02x",
dev->node.dev_name, link->conf.ConfigIndex);
if (link->conf.Attributes & CONF_ENABLE_IRQ)
printk(", irq %d", link->irq.AssignedIRQ);
if (link->io.NumPorts1)
printk(", io 0x%04x-0x%04x", link->io.BasePort1,
link->io.BasePort1 + link->io.NumPorts1 - 1);
if (link->io.NumPorts2)
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
link->io.BasePort2 + link->io.NumPorts2 - 1);
if (link->win)
printk(", mem 0x%06lx-0x%06lx", req.Base,
req.Base + req.Size - 1);
printk("\n");
return;
cs_failed:
labpc_release(link);
} /* labpc_config */
static void labpc_release(struct pcmcia_device *link)
{
DEBUG(0, "labpc_release(0x%p)\n", link);
pcmcia_disable_device(link);
} /* labpc_release */
/*======================================================================
The card status event handler. Mostly, this schedules other
stuff to run after an event is received.
When a CARD_REMOVAL event is received, we immediately set a
private flag to block future accesses to this device. All the
functions that actually access the device should check this flag
to make sure the card is still present.
======================================================================*/
static int labpc_cs_suspend(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
/* Mark the device as stopped, to block IO until later */
local->stop = 1;
return 0;
} /* labpc_cs_suspend */
static int labpc_cs_resume(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
local->stop = 0;
return 0;
} /* labpc_cs_resume */
/*====================================================================*/
static struct pcmcia_device_id labpc_cs_ids[] = {
/* N.B. These IDs should match those in labpc_cs_boards (ni_labpc.c) */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0103), /* daqcard-1200 */
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, labpc_cs_ids);
struct pcmcia_driver labpc_cs_driver = {
.probe = labpc_cs_attach,
.remove = labpc_cs_detach,
.suspend = labpc_cs_suspend,
.resume = labpc_cs_resume,
.id_table = labpc_cs_ids,
.owner = THIS_MODULE,
.drv = {
.name = dev_info,
},
};
static int __init init_labpc_cs(void)
{
DEBUG(0, "%s\n", version);
pcmcia_register_driver(&labpc_cs_driver);
return 0;
}
static void __exit exit_labpc_cs(void)
{
DEBUG(0, "ni_labpc: unloading\n");
pcmcia_unregister_driver(&labpc_cs_driver);
}
int __init labpc_init_module(void)
{
int ret;
ret = init_labpc_cs();
if (ret < 0)
return ret;
return comedi_driver_register(&driver_labpc_cs);
}
void __exit labpc_exit_module(void)
{
exit_labpc_cs();
comedi_driver_unregister(&driver_labpc_cs);
}
MODULE_LICENSE("GPL");
module_init(labpc_init_module);
module_exit(labpc_exit_module);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment