static int me4000_auto_attach()

in drivers/me4000.c [1094:1222]


static int me4000_auto_attach(struct comedi_device *dev,
			      unsigned long context)
{
	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
	const struct me4000_board *board = NULL;
	struct me4000_private *devpriv;
	struct comedi_subdevice *s;
	int result;

	if (context < ARRAY_SIZE(me4000_boards))
		board = &me4000_boards[context];
	if (!board)
		return -ENODEV;
	dev->board_ptr = board;
	dev->board_name = board->name;

	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
	if (!devpriv)
		return -ENOMEM;

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

	devpriv->plx_regbase = pci_resource_start(pcidev, 1);
	dev->iobase = pci_resource_start(pcidev, 2);
	if (!devpriv->plx_regbase || !dev->iobase)
		return -ENODEV;

	result = comedi_load_firmware(dev, &pcidev->dev, ME4000_FIRMWARE,
				      me4000_xilinx_download, 0);
	if (result < 0)
		return result;

	me4000_reset(dev);

	if (pcidev->irq > 0) {
		result = request_irq(pcidev->irq, me4000_ai_isr, IRQF_SHARED,
				     dev->board_name, dev);
		if (result == 0) {
			dev->irq = pcidev->irq;

			/* Enable interrupts on the PLX */
			outl(PLX9052_INTCSR_LI1ENAB | PLX9052_INTCSR_LI1POL |
			     PLX9052_INTCSR_PCIENAB,
			     devpriv->plx_regbase + PLX9052_INTCSR);
		}
	}

	result = comedi_alloc_subdevices(dev, 4);
	if (result)
		return result;

	/* Analog Input subdevice */
	s = &dev->subdevices[0];
	s->type		= COMEDI_SUBD_AI;
	s->subdev_flags	= SDF_READABLE | SDF_COMMON | SDF_GROUND;
	if (board->can_do_diff_ai)
		s->subdev_flags	|= SDF_DIFF;
	s->n_chan	= board->ai_nchan;
	s->maxdata	= 0xffff;
	s->len_chanlist	= ME4000_AI_CHANNEL_LIST_COUNT;
	s->range_table	= &me4000_ai_range;
	s->insn_read	= me4000_ai_insn_read;

	if (dev->irq) {
		dev->read_subdev = s;
		s->subdev_flags	|= SDF_CMD_READ;
		s->cancel	= me4000_ai_cancel;
		s->do_cmdtest	= me4000_ai_do_cmd_test;
		s->do_cmd	= me4000_ai_do_cmd;
	}

	/* Analog Output subdevice */
	s = &dev->subdevices[1];
	if (board->has_ao) {
		s->type		= COMEDI_SUBD_AO;
		s->subdev_flags	= SDF_WRITABLE | SDF_COMMON | SDF_GROUND;
		s->n_chan	= 4;
		s->maxdata	= 0xffff;
		s->range_table	= &range_bipolar10;
		s->insn_write	= me4000_ao_insn_write;

		result = comedi_alloc_subdev_readback(s);
		if (result)
			return result;
	} else {
		s->type		= COMEDI_SUBD_UNUSED;
	}

	/* Digital I/O subdevice */
	s = &dev->subdevices[2];
	s->type		= COMEDI_SUBD_DIO;
	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
	s->n_chan	= 32;
	s->maxdata	= 1;
	s->range_table	= &range_digital;
	s->insn_bits	= me4000_dio_insn_bits;
	s->insn_config	= me4000_dio_insn_config;

	/*
	 * Check for optoisolated ME-4000 version. If one the first
	 * port is a fixed output port and the second is a fixed input port.
	 */
	if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) {
		s->io_bits |= 0xFF;
		outl(ME4000_DIO_CTRL_MODE_0,
		     dev->iobase + ME4000_DIO_DIR_REG);
	}

	/* Counter subdevice (8254) */
	s = &dev->subdevices[3];
	if (board->has_counter) {
		unsigned long timer_base = pci_resource_start(pcidev, 3);

		if (!timer_base)
			return -ENODEV;

		dev->pacer = comedi_8254_init(timer_base, 0, I8254_IO8, 0);
		if (!dev->pacer)
			return -ENOMEM;

		comedi_8254_subdevice_init(s, dev->pacer);
	} else {
		s->type = COMEDI_SUBD_UNUSED;
	}

	return 0;
}