Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd2aef64 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman
Browse files

staging: comedi: ni_labpc: remove forward declarations 1



Move the comedi_driver (*attach), (*auto_attach), and (*detach)
functions as well as a couple of the attach helper functions to
remove the need for a number of the forward declarations.

Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6e8bddf2
Loading
Loading
Loading
Loading
+361 −392
Original line number Diff line number Diff line
@@ -215,48 +215,17 @@ enum scan_mode {
	MODE_MULT_CHAN_DOWN,
};

static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
static irqreturn_t labpc_interrupt(int irq, void *d);
static int labpc_drain_fifo(struct comedi_device *dev);
#ifdef CONFIG_ISA_DMA_API
static void labpc_drain_dma(struct comedi_device *dev);
static void handle_isa_dma(struct comedi_device *dev);
#endif
static void labpc_drain_dregs(struct comedi_device *dev);
static int labpc_ai_cmdtest(struct comedi_device *dev,
			    struct comedi_subdevice *s, struct comedi_cmd *cmd);
static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
static int labpc_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
			  struct comedi_insn *insn, unsigned int *data);
static int labpc_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
			  struct comedi_insn *insn, unsigned int *data);
static int labpc_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
			  struct comedi_insn *insn, unsigned int *data);
static int labpc_calib_read_insn(struct comedi_device *dev,
				 struct comedi_subdevice *s,
				 struct comedi_insn *insn, unsigned int *data);
static int labpc_calib_write_insn(struct comedi_device *dev,
				  struct comedi_subdevice *s,
				  struct comedi_insn *insn, unsigned int *data);
static int labpc_eeprom_read_insn(struct comedi_device *dev,
				  struct comedi_subdevice *s,
				  struct comedi_insn *insn, unsigned int *data);
static int labpc_eeprom_write_insn(struct comedi_device *dev,
				   struct comedi_subdevice *s,
				   struct comedi_insn *insn,
				   unsigned int *data);
static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
			     enum scan_mode scan_mode);
#ifdef CONFIG_ISA_DMA_API
static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd);
#endif
static int labpc_dio_mem_callback(int dir, int port, int data,
				  unsigned long arg);
static void labpc_serial_out(struct comedi_device *dev, unsigned int value,
			     unsigned int num_bits);
static unsigned int labpc_serial_in(struct comedi_device *dev);
static unsigned int labpc_eeprom_read(struct comedi_device *dev,
				      unsigned int address);
static unsigned int labpc_eeprom_read_status(struct comedi_device *dev);
static int labpc_eeprom_write(struct comedi_device *dev,
				       unsigned int address,
@@ -498,390 +467,78 @@ static inline int labpc_counter_load(struct comedi_device *dev,
		return i8254_load(base_address, 0, counter_number, count, mode);
}

int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
			unsigned int irq, unsigned int dma_chan)
static void labpc_clear_adc_fifo(const struct comedi_device *dev)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv = dev->private;
	struct comedi_subdevice *s;
	int i;
	unsigned long isr_flags;
#ifdef CONFIG_ISA_DMA_API
	unsigned long dma_flags;
#endif
	short lsb, msb;
	int ret;

	dev_info(dev->class_dev, "ni_labpc: %s\n", thisboard->name);
	if (iobase == 0) {
		dev_err(dev->class_dev, "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, DRV_NAME)) {
			dev_err(dev->class_dev, "I/O port conflict\n");
			return -EIO;
		}
	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);
}
	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);
static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
{
	struct labpc_private *devpriv = dev->private;
	unsigned long flags;

	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);
	spin_unlock_irqrestore(&dev->spinlock, flags);

	devpriv->command3_bits = 0;
	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
		    || thisboard->bustype == pcmcia_bustype)
			isr_flags |= IRQF_SHARED;
		if (request_irq(irq, labpc_interrupt, isr_flags,
				DRV_NAME, dev)) {
			dev_err(dev->class_dev, "unable to allocate irq %u\n",
				irq);
			return -EINVAL;
		}
	return 0;
}
	dev->irq = irq;

#ifdef CONFIG_ISA_DMA_API
	/* grab dma channel */
	if (dma_chan > 3) {
		dev_err(dev->class_dev, "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)
			return -ENOMEM;
static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
{
	if (cmd->chanlist_len == 1)
		return MODE_SINGLE_CHAN;

		if (request_dma(dma_chan, DRV_NAME)) {
			dev_err(dev->class_dev,
				"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);
	}
#endif
	/* chanlist may be NULL during cmdtest. */
	if (cmd->chanlist == NULL)
		return MODE_MULT_CHAN_UP;

	dev->board_name = thisboard->name;
	if (CR_CHAN(cmd->chanlist[0]) == CR_CHAN(cmd->chanlist[1]))
		return MODE_SINGLE_CHAN_INTERVAL;

	ret = comedi_alloc_subdevices(dev, 5);
	if (ret)
		return ret;
	if (CR_CHAN(cmd->chanlist[0]) < CR_CHAN(cmd->chanlist[1]))
		return MODE_MULT_CHAN_UP;

	/* 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;
	if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
		return MODE_MULT_CHAN_DOWN;

	/* 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;
	pr_err("ni_labpc: bug! cannot determine AI scan mode\n");
	return 0;
}

	/* 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);
	} else
		s->type = COMEDI_SUBD_UNUSED;
static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
				     const struct comedi_cmd *cmd,
				     enum scan_mode mode)
{
	int channel, range, aref, i;

	if (cmd->chanlist == NULL)
		return 0;
}
EXPORT_SYMBOL_GPL(labpc_common_attach);

static const struct labpc_boardinfo *
labpc_pci_find_boardinfo(struct pci_dev *pcidev)
{
	unsigned int device_id = pcidev->device;
	unsigned int n;
	if (mode == MODE_SINGLE_CHAN)
		return 0;

	for (n = 0; n < ARRAY_SIZE(labpc_boards); n++) {
		const struct labpc_boardinfo *board = &labpc_boards[n];
		if (board->bustype == pci_bustype &&
		    board->device_id == device_id)
			return board;
	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;
		}
	return NULL;
	}

static int labpc_auto_attach(struct comedi_device *dev,
				       unsigned long context_unused)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	struct labpc_private *devpriv;
	unsigned long iobase;
	unsigned int irq;
	int ret;

	if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
		return -ENODEV;

	ret = comedi_pci_enable(dev);
	if (ret)
		return ret;
	channel = CR_CHAN(cmd->chanlist[0]);
	range = CR_RANGE(cmd->chanlist[0]);
	aref = CR_AREF(cmd->chanlist[0]);

	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
	if (!devpriv)
		return -ENOMEM;
	dev->private = devpriv;

	dev->board_ptr = labpc_pci_find_boardinfo(pcidev);
	if (!dev->board_ptr)
		return -ENODEV;
	devpriv->mite = mite_alloc(pcidev);
	if (!devpriv->mite)
		return -ENOMEM;
	ret = mite_setup(devpriv->mite);
	if (ret < 0)
		return ret;
	iobase = (unsigned long)devpriv->mite->daq_io_addr;
	irq = mite_irq(devpriv->mite);
	return labpc_common_attach(dev, iobase, irq, 0);
}

static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv;
	unsigned long iobase = 0;
	unsigned int irq = 0;
	unsigned int dma_chan = 0;

	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
	if (!devpriv)
		return -ENOMEM;
	dev->private = devpriv;

	/* get base address, irq etc. based on bustype */
	switch (thisboard->bustype) {
	case isa_bustype:
#ifdef CONFIG_ISA_DMA_API
		iobase = it->options[0];
		irq = it->options[1];
		dma_chan = it->options[2];
#else
		dev_err(dev->class_dev,
			"ni_labpc driver has not been built with ISA DMA support.\n");
		return -EINVAL;
#endif
		break;
	case pci_bustype:
#ifdef CONFIG_COMEDI_PCI_DRIVERS
		dev_err(dev->class_dev,
			"manual configuration of PCI board '%s' is not supported\n",
			thisboard->name);
		return -EINVAL;
#else
		dev_err(dev->class_dev,
			"ni_labpc driver has not been built with PCI support.\n");
		return -EINVAL;
#endif
		break;
	default:
		dev_err(dev->class_dev,
			"ni_labpc: bug! couldn't determine board type\n");
		return -EINVAL;
		break;
	}

	return labpc_common_attach(dev, iobase, irq, dma_chan);
}

void labpc_common_detach(struct comedi_device *dev)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv = dev->private;
	struct comedi_subdevice *s;

	if (!thisboard)
		return;
	if (dev->subdevices) {
		s = &dev->subdevices[2];
		subdev_8255_cleanup(dev, s);
	}
#ifdef CONFIG_ISA_DMA_API
	/* only free stuff if it has been allocated by _attach */
	kfree(devpriv->dma_buffer);
	if (devpriv->dma_chan)
		free_dma(devpriv->dma_chan);
#endif
	if (dev->irq)
		free_irq(dev->irq, dev);
	if (thisboard->bustype == isa_bustype && dev->iobase)
		release_region(dev->iobase, LABPC_SIZE);
#ifdef CONFIG_COMEDI_PCI_DRIVERS
	if (devpriv->mite) {
		mite_unsetup(devpriv->mite);
		mite_free(devpriv->mite);
	}
	if (thisboard->bustype == pci_bustype)
		comedi_pci_disable(dev);
#endif
};
EXPORT_SYMBOL_GPL(labpc_common_detach);

static void labpc_clear_adc_fifo(const struct comedi_device *dev)
{
	struct labpc_private *devpriv = dev->private;

	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(struct comedi_device *dev, struct comedi_subdevice *s)
{
	struct labpc_private *devpriv = dev->private;
	unsigned long flags;

	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);
	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 struct 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;

	pr_err("ni_labpc: bug! cannot determine AI scan mode\n");
	return 0;
}

static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
				     const struct comedi_cmd *cmd,
				     enum scan_mode mode)
{
	int channel, range, aref, i;

	if (cmd->chanlist == NULL)
		return 0;

	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++) {
	for (i = 0; i < cmd->chanlist_len; i++) {

		switch (mode) {
		case MODE_SINGLE_CHAN_INTERVAL:
@@ -2105,6 +1762,318 @@ static void write_caldac(struct comedi_device *dev, unsigned int channel,
	devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
}

int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
			unsigned int irq, unsigned int dma_chan)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv = dev->private;
	struct comedi_subdevice *s;
	int i;
	unsigned long isr_flags;
#ifdef CONFIG_ISA_DMA_API
	unsigned long dma_flags;
#endif
	short lsb, msb;
	int ret;

	dev_info(dev->class_dev, "ni_labpc: %s\n", thisboard->name);
	if (iobase == 0) {
		dev_err(dev->class_dev, "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, DRV_NAME)) {
			dev_err(dev->class_dev, "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
		    || thisboard->bustype == pcmcia_bustype)
			isr_flags |= IRQF_SHARED;
		if (request_irq(irq, labpc_interrupt, isr_flags,
				DRV_NAME, dev)) {
			dev_err(dev->class_dev, "unable to allocate irq %u\n",
				irq);
			return -EINVAL;
		}
	}
	dev->irq = irq;

#ifdef CONFIG_ISA_DMA_API
	/* grab dma channel */
	if (dma_chan > 3) {
		dev_err(dev->class_dev, "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)
			return -ENOMEM;

		if (request_dma(dma_chan, DRV_NAME)) {
			dev_err(dev->class_dev,
				"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);
	}
#endif

	dev->board_name = thisboard->name;

	ret = comedi_alloc_subdevices(dev, 5);
	if (ret)
		return ret;

	/* 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);
	} else
		s->type = COMEDI_SUBD_UNUSED;

	return 0;
}
EXPORT_SYMBOL_GPL(labpc_common_attach);

static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv;
	unsigned long iobase = 0;
	unsigned int irq = 0;
	unsigned int dma_chan = 0;

	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
	if (!devpriv)
		return -ENOMEM;
	dev->private = devpriv;

	/* get base address, irq etc. based on bustype */
	switch (thisboard->bustype) {
	case isa_bustype:
#ifdef CONFIG_ISA_DMA_API
		iobase = it->options[0];
		irq = it->options[1];
		dma_chan = it->options[2];
#else
		dev_err(dev->class_dev,
			"ni_labpc driver has not been built with ISA DMA support.\n");
		return -EINVAL;
#endif
		break;
	case pci_bustype:
#ifdef CONFIG_COMEDI_PCI_DRIVERS
		dev_err(dev->class_dev,
			"manual configuration of PCI board '%s' is not supported\n",
			thisboard->name);
		return -EINVAL;
#else
		dev_err(dev->class_dev,
			"ni_labpc driver has not been built with PCI support.\n");
		return -EINVAL;
#endif
		break;
	default:
		dev_err(dev->class_dev,
			"ni_labpc: bug! couldn't determine board type\n");
		return -EINVAL;
		break;
	}

	return labpc_common_attach(dev, iobase, irq, dma_chan);
}

static const struct labpc_boardinfo *
labpc_pci_find_boardinfo(struct pci_dev *pcidev)
{
	unsigned int device_id = pcidev->device;
	unsigned int n;

	for (n = 0; n < ARRAY_SIZE(labpc_boards); n++) {
		const struct labpc_boardinfo *board = &labpc_boards[n];
		if (board->bustype == pci_bustype &&
		    board->device_id == device_id)
			return board;
	}
	return NULL;
}

static int labpc_auto_attach(struct comedi_device *dev,
				       unsigned long context_unused)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	struct labpc_private *devpriv;
	unsigned long iobase;
	unsigned int irq;
	int ret;

	if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
		return -ENODEV;

	ret = comedi_pci_enable(dev);
	if (ret)
		return ret;

	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
	if (!devpriv)
		return -ENOMEM;
	dev->private = devpriv;

	dev->board_ptr = labpc_pci_find_boardinfo(pcidev);
	if (!dev->board_ptr)
		return -ENODEV;
	devpriv->mite = mite_alloc(pcidev);
	if (!devpriv->mite)
		return -ENOMEM;
	ret = mite_setup(devpriv->mite);
	if (ret < 0)
		return ret;
	iobase = (unsigned long)devpriv->mite->daq_io_addr;
	irq = mite_irq(devpriv->mite);
	return labpc_common_attach(dev, iobase, irq, 0);
}

void labpc_common_detach(struct comedi_device *dev)
{
	const struct labpc_boardinfo *thisboard = comedi_board(dev);
	struct labpc_private *devpriv = dev->private;
	struct comedi_subdevice *s;

	if (!thisboard)
		return;
	if (dev->subdevices) {
		s = &dev->subdevices[2];
		subdev_8255_cleanup(dev, s);
	}
#ifdef CONFIG_ISA_DMA_API
	/* only free stuff if it has been allocated by _attach */
	kfree(devpriv->dma_buffer);
	if (devpriv->dma_chan)
		free_dma(devpriv->dma_chan);
#endif
	if (dev->irq)
		free_irq(dev->irq, dev);
	if (thisboard->bustype == isa_bustype && dev->iobase)
		release_region(dev->iobase, LABPC_SIZE);
#ifdef CONFIG_COMEDI_PCI_DRIVERS
	if (devpriv->mite) {
		mite_unsetup(devpriv->mite);
		mite_free(devpriv->mite);
	}
	if (thisboard->bustype == pci_bustype)
		comedi_pci_disable(dev);
#endif
}
EXPORT_SYMBOL_GPL(labpc_common_detach);

static struct comedi_driver labpc_driver = {
	.driver_name	= DRV_NAME,
	.module		= THIS_MODULE,