* Author: Ian Abbott <abbotti@mev.co.uk>
* Devices: [Amplicon] PCI224 (amplc_pci224 or pci224),
* PCI234 (amplc_pci224 or pci234)
- * Updated: Wed, 22 Oct 2008 12:25:08 +0100
+ * Updated: Wed, 30 Jul 2014 18:08:43 +0000
* Status: works, but see caveats
*
* Supports:
* There is only one external trigger source so only one of start_src,
* scan_begin_src or stop_src may use TRIG_EXT.
*
- * Configuration options - PCI224:
+ * Configuration options:
* [0] - PCI bus of device (optional).
* [1] - PCI slot of device (optional).
* If bus/slot is not specified, the first available PCI device
* will be used.
- * [2] - Select available ranges according to jumper LK1. All channels
- * are set to the same range:
- * 0=Jumper position 1-2 (factory default), 4 software-selectable
- * internal voltage references, giving 4 bipolar and 4 unipolar
- * ranges:
- * [-10V,+10V], [-5V,+5V], [-2.5V,+2.5V], [-1.25V,+1.25V],
- * [0,+10V], [0,+5V], [0,+2.5V], [0,1.25V].
- * 1=Jumper position 2-3, 1 external voltage reference, giving
- * 1 bipolar and 1 unipolar range:
- * [-Vext,+Vext], [0,+Vext].
- *
- * Configuration options - PCI234:
- * [0] - PCI bus of device (optional).
- * [1] - PCI slot of device (optional).
- * If bus/slot is not specified, the first available PCI device
- * will be used.
- * [2] - Select internal or external voltage reference according to
- * jumper LK1. This affects all channels:
- * 0=Jumper position 1-2 (factory default), Vref=5V internal.
- * 1=Jumper position 2-3, Vref=Vext external.
- * [3] - Select channel 0 range according to jumper LK2:
- * 0=Jumper position 2-3 (factory default), range [-2*Vref,+2*Vref]
- * (10V bipolar when options[2]=0).
- * 1=Jumper position 1-2, range [-Vref,+Vref]
- * (5V bipolar when options[2]=0).
- * [4] - Select channel 1 range according to jumper LK3: cf. options[3].
- * [5] - Select channel 2 range according to jumper LK4: cf. options[3].
- * [6] - Select channel 3 range according to jumper LK5: cf. options[3].
*
* Passing a zero for an option is the same as leaving it unspecified.
*
+ * Output range selection - PCI224:
+ *
+ * Output ranges on PCI224 are partly software-selectable and partly
+ * hardware-selectable according to jumper LK1. All channels are set
+ * to the same range:
+ *
+ * - LK1 position 1-2 (factory default) corresponds to the following
+ * comedi ranges:
+ *
+ * 0: [-10V,+10V]; 1: [-5V,+5V]; 2: [-2.5V,+2.5V], 3: [-1.25V,+1.25V],
+ * 4: [0,+10V], 5: [0,+5V], 6: [0,+2.5V], 7: [0,+1.25V]
+ *
+ * - LK1 position 2-3 corresponds to the following Comedi ranges, using
+ * an external voltage reference:
+ *
+ * 0: [-Vext,+Vext],
+ * 1: [0,+Vext]
+ *
+ * Output range selection - PCI234:
+ *
+ * Output ranges on PCI234 are hardware-selectable according to jumper
+ * LK1 which affects all channels, and jumpers LK2, LK3, LK4 and LK5
+ * which affect channels 0, 1, 2 and 3 individually. LK1 chooses between
+ * an internal 5V reference and an external voltage reference (Vext).
+ * LK2/3/4/5 choose (per channel) to double the reference or not according
+ * to the following table:
+ *
+ * LK1 position LK2/3/4/5 pos Comedi range
+ * ------------- ------------- --------------
+ * 2-3 (factory) 1-2 (factory) 0: [-10V,+10V]
+ * 2-3 (factory) 2-3 1: [-5V,+5V]
+ * 1-2 1-2 (factory) 2: [-2*Vext,+2*Vext]
+ * 1-2 2-3 3: [-Vext,+Vext]
+ *
* Caveats:
*
* 1) All channels on the PCI224 share the same range. Any change to the
* Range tables.
*/
-/* The software selectable internal ranges for PCI224 (option[2] == 0). */
-static const struct comedi_lrange range_pci224_internal = {
- 8, {
+/*
+ * The ranges for PCI224.
+ *
+ * These are partly hardware-selectable by jumper LK1 and partly
+ * software-selectable.
+ *
+ * All channels share the same hardware range.
+ */
+static const struct comedi_lrange range_pci224 = {
+ 10, {
+ /* jumper LK1 in position 1-2 (factory default) */
BIP_RANGE(10),
BIP_RANGE(5),
BIP_RANGE(2.5),
UNI_RANGE(10),
UNI_RANGE(5),
UNI_RANGE(2.5),
- UNI_RANGE(1.25)
+ UNI_RANGE(1.25),
+ /* jumper LK1 in position 2-3 */
+ RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */
+ RANGE_ext(0, 1), /* unipolar [0,+Vext] */
}
};
-static const unsigned short hwrange_pci224_internal[8] = {
+static const unsigned short hwrange_pci224[10] = {
+ /* jumper LK1 in position 1-2 (factory default) */
PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_10,
PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_5,
PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_2_5,
PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_5,
PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_2_5,
PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_1_25,
-};
-
-/* The software selectable external ranges for PCI224 (option[2] == 1). */
-static const struct comedi_lrange range_pci224_external = {
- 2, {
- RANGE_ext(-1, 1), /* bipolar [-Vref,+Vref] */
- RANGE_ext(0, 1) /* unipolar [0,+Vref] */
- }
-};
-
-static const unsigned short hwrange_pci224_external[2] = {
+ /* jumper LK1 in position 2-3 */
PCI224_DACCON_POLAR_BI,
PCI224_DACCON_POLAR_UNI,
};
-/*
- * The hardware selectable Vref*2 external range for PCI234
- * (option[2] == 1, option[3+n] == 0).
- */
-static const struct comedi_lrange range_pci234_ext2 = {
- 1, {
- RANGE_ext(-2, 2)
- }
+/* Used to check all channels set to the same range on PCI224. */
+static const unsigned char range_check_pci224[10] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
};
/*
- * The hardware selectable Vref external range for PCI234
- * (option[2] == 1, option[3+n] == 1).
+ * The ranges for PCI234.
+ *
+ * These are all hardware-selectable by jumper LK1 affecting all channels,
+ * and jumpers LK2, LK3, LK4 and LK5 affecting channels 0, 1, 2 and 3
+ * individually.
*/
-static const struct comedi_lrange range_pci234_ext = {
- 1, {
- RANGE_ext(-1, 1)
+static const struct comedi_lrange range_pci234 = {
+ 4, {
+ /* LK1: 1-2 (fact def), LK2/3/4/5: 2-3 (fac def) */
+ BIP_RANGE(10),
+ /* LK1: 1-2 (fact def), LK2/3/4/5: 1-2 */
+ BIP_RANGE(5),
+ /* LK1: 2-3, LK2/3/4/5: 2-3 (fac def) */
+ RANGE_ext(-2, 2), /* bipolar [-2*Vext,+2*Vext] */
+ /* LK1: 2-3, LK2/3/4/5: 1-2 */
+ RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */
}
};
-/* This serves for all the PCI234 ranges. */
-static const unsigned short hwrange_pci234[1] = {
- PCI224_DACCON_POLAR_BI, /* bipolar - hardware ignores it! */
+/* N.B. PCI234 ignores the polarity bit, but software uses it. */
+static const unsigned short hwrange_pci234[4] = {
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+};
+
+/* Used to check all channels use same LK1 setting on PCI234. */
+static const unsigned char range_check_pci234[4] = {
+ 0, 0, 1, 1,
};
/*
enum pci224_model model;
unsigned int ao_chans;
unsigned int ao_bits;
+ const struct comedi_lrange *ao_range;
+ const unsigned short *ao_hwrange;
+ const unsigned char *ao_range_check;
};
static const struct pci224_board pci224_boards[] = {
.model = pci224_model,
.ao_chans = 16,
.ao_bits = 12,
+ .ao_range = &range_pci224,
+ .ao_hwrange = &hwrange_pci224[0],
+ .ao_range_check = &range_check_pci224[0],
},
{
.name = "pci234",
.model = pci234_model,
.ao_chans = 4,
.ao_bits = 16,
+ .ao_range = &range_pci234,
+ .ao_hwrange = &hwrange_pci234[0],
+ .ao_range_check = &range_check_pci234[0],
},
{
.name = "amplc_pci224",
};
struct pci224_private {
- const unsigned short *hwrange;
unsigned long iobase1;
unsigned long state;
spinlock_t ao_spinlock; /* spinlock for AO command handling */
/* Enable the channel. */
outw(1 << chan, dev->iobase + PCI224_DACCEN);
/* Set range and reset FIFO. */
- devpriv->daccon = COMBINE(devpriv->daccon, devpriv->hwrange[range],
+ devpriv->daccon = COMBINE(devpriv->daccon, thisboard->ao_hwrange[range],
PCI224_DACCON_POLAR_MASK |
PCI224_DACCON_VREF_MASK);
outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
- unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ const struct pci224_board *thisboard = comedi_board(dev);
+ unsigned int range_check_0;
unsigned int chan_mask = 0;
int i;
+ range_check_0 = thisboard->ao_range_check[CR_RANGE(cmd->chanlist[0])];
for (i = 0; i < cmd->chanlist_len; i++) {
unsigned int chan = CR_CHAN(cmd->chanlist[i]);
- unsigned int range = CR_RANGE(cmd->chanlist[i]);
if (chan_mask & (1 << chan)) {
dev_dbg(dev->class_dev,
}
chan_mask |= 1 << chan;
- if (range != range0) {
+ if (thisboard->ao_range_check[CR_RANGE(cmd->chanlist[i])] !=
+ range_check_0) {
dev_dbg(dev->class_dev,
- "%s: entries in chanlist must all have the same range index\n",
+ "%s: entries in chanlist have incompatible ranges\n",
__func__);
return -EINVAL;
}
static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
+ const struct pci224_board *thisboard = comedi_board(dev);
struct pci224_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
int range;
*/
devpriv->daccon =
COMBINE(devpriv->daccon,
- devpriv->hwrange[range] | PCI224_DACCON_TRIG_NONE |
+ thisboard->ao_hwrange[range] | PCI224_DACCON_TRIG_NONE |
PCI224_DACCON_FIFOINTR_NHALF,
PCI224_DACCON_POLAR_MASK | PCI224_DACCON_VREF_MASK |
PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK);
void *data, unsigned int num_bytes, unsigned int chan_index)
{
const struct pci224_board *thisboard = comedi_board(dev);
- struct pci224_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
unsigned short *array = data;
unsigned int length = num_bytes / sizeof(*array);
/* The hardware expects 16-bit numbers. */
shift = 16 - thisboard->ao_bits;
/* Channels will be all bipolar or all unipolar. */
- if ((devpriv->hwrange[CR_RANGE(cmd->chanlist[0])] &
+ if ((thisboard->ao_hwrange[CR_RANGE(cmd->chanlist[0])] &
PCI224_DACCON_POLAR_MASK) == PCI224_DACCON_POLAR_UNI) {
/* Unipolar */
offset = 0;
* Common part of attach and auto_attach.
*/
static int pci224_attach_common(struct comedi_device *dev,
- struct pci_dev *pci_dev, int *options)
+ struct pci_dev *pci_dev)
{
const struct pci224_board *thisboard = comedi_board(dev);
struct pci224_private *devpriv = dev->private;
struct comedi_subdevice *s;
unsigned int irq;
- unsigned n;
int ret;
comedi_set_hw_dev(dev, &pci_dev->dev);
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
s->n_chan = thisboard->ao_chans;
s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ao_range;
s->insn_write = pci224_ao_insn_write;
s->insn_read = pci224_ao_insn_read;
s->len_chanlist = s->n_chan;
s->cancel = pci224_ao_cancel;
s->munge = pci224_ao_munge;
- /* Sort out channel range options. */
- if (thisboard->model == pci234_model) {
- /* PCI234 range options. */
- const struct comedi_lrange **range_table_list;
-
- range_table_list =
- kmalloc(sizeof(struct comedi_lrange *) * s->n_chan,
- GFP_KERNEL);
- if (!range_table_list)
- return -ENOMEM;
- s->range_table_list = range_table_list;
-
- if (options) {
- for (n = 2; n < 3 + s->n_chan; n++) {
- if (options[n] < 0 || options[n] > 1) {
- dev_warn(dev->class_dev,
- "warning! bad options[%u]=%d\n",
- n, options[n]);
- }
- }
- }
- for (n = 0; n < s->n_chan; n++) {
- if (n < COMEDI_NDEVCONFOPTS - 3 && options &&
- options[3 + n] == 1) {
- if (options[2] == 1)
- range_table_list[n] = &range_pci234_ext;
- else
- range_table_list[n] = &range_bipolar5;
-
- } else {
- if (options && options[2] == 1) {
- range_table_list[n] =
- &range_pci234_ext2;
- } else {
- range_table_list[n] = &range_bipolar10;
- }
- }
- }
- devpriv->hwrange = hwrange_pci234;
- } else {
- /* PCI224 range options. */
- if (options && options[2] == 1) {
- s->range_table = &range_pci224_external;
- devpriv->hwrange = hwrange_pci224_external;
- } else {
- if (options && options[2] != 0) {
- dev_warn(dev->class_dev,
- "warning! bad options[2]=%d\n",
- options[2]);
- }
- s->range_table = &range_pci224_internal;
- devpriv->hwrange = hwrange_pci224_internal;
- }
- }
-
dev->board_name = thisboard->name;
if (irq) {
if (!pci_dev)
return -EIO;
- return pci224_attach_common(dev, pci_dev, it->options);
+ return pci224_attach_common(dev, pci_dev);
}
static int
* has been removed.
*/
pci_dev_get(pci_dev);
- return pci224_attach_common(dev, pci_dev, NULL);
+ return pci224_attach_common(dev, pci_dev);
}
static void pci224_detach(struct comedi_device *dev)
if (dev->irq)
free_irq(dev->irq, dev);
- if (dev->subdevices) {
- struct comedi_subdevice *s;
-
- s = &dev->subdevices[0];
- /* AO subdevice */
- kfree(s->range_table_list);
- }
if (devpriv) {
kfree(devpriv->ao_readback);
kfree(devpriv->ao_scan_vals);